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

dreammaster paulfgilbert at gmail.com
Sun Dec 9 04:09:03 CET 2018


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

Summary:
c4d4d48d67 GLK: Initial skeleton engine
9c536cb6bf GLK: Add stub classes for sub-engine components
46aafc3558 GLK: Refactor Glk to derice from Interpreter
b2067f3be7 GLK: Added Glk enums and structures
fa253ddba7 GLK: Added stubs for Glk methods
dcefdd6f62 GLK: More outer engine structure, and cleanup
b62e30b84c GLK: Interpreters now derive from GargoyleEngine via Glk
22648145a9 GLK: SCOTT: Initial conversion of ScottFree
9c6aca1d57 GLK: SCOTT: Minor stream and event loop fixes
db35a9cdc8 GLK: SCOTT: Adding game detection code
ef161922d8 ENGINES: Add optional extra configuration entries when creating new targets
5f626f7a6a GLK: Add detection logic needed for engine startup
74080143da GLK: Detect presence of game file in detectGames
77f357e72f GLK: Add window creation
47a7a1d4e2 GLK: Added setup of text grid windows
89102c42ca GLK: Window setup for text buffer windows
b9bafba382 GLK: Remaining window rearrange code
9e804bf484 GLK: Added glk_set_window
3c9987c0a7 GLK: Fixes for window initialization
256f7ff312 GLK: Skeleton implementation of window text stream
65091b25c1 GLK: Starting to flesh out stream classes
cf5259d3bc GLK: Adding stream open/closing
ef15871fec GLK: Setting up of configuration loading
bbd744ad47 GLK: Further config loading
0bc3da9e80 GLK: Remove duplicated Windows fields that are now in Conf
d314cd39ee GLK: SCOTT: Add detection for other games
cc54beadce GLK: Start of window character output
ac9a122e3d GLK: Renaming of window classes fields
855fd39a9e GLK: Added WindowStream put methods
5bfdffc9dc GLK: Added remaining stream write methods
71389c2dd2 GLK: Added window class destructors
0d3ad2dc8a GLK: Added TextGridWindow methods and support
8708ed4f9a GLK: Split the windows.cpp file into separate files for each window class
ede0323c08 GLK: Windows don't have a non-unicode putChar method
cb2b7d439f GLK: Remove more Window methods that aren't actually present
ba907c9aec GLK: Implementing text buffer window methods
4ddda5cebc GLK: Windows focus / iteration
6939fabbfb GLK: Fleshed out graphics window
fdfb1a5502 GLK: Move Draw class into new Screen class
3d8626081c GLK: Added window closing, some structures cleanup
49b8281ce3 GLK: Window rearrangement methods
594f63f475 GLK: Added window getSize methods
72022042ee GLK: Adding glk window methods
4266f3af13 GLK: Adding more window glk methods
e14424c2b9 GLK: Adding more window glk methods
b231dee01a GLK: More window glk methods
d128249854 GLK: Miscellaneous glk methods
485e899312 GLK: Adding glk stream methods
734366ce89 GLK: Added more glk stream commands
4bb5967920 GLK: More glk stream functions
98f1ddc968 GLK: Added unicode case methods
7436604483 GLK: Added time and date glk functions
76bf2726f8 GLK: Added input window focus, and cleanup of event method stubs
7bbedcd099 GLK: Beginnings of event handling
9eb4debd15 GLK: Logic for keypress event handling
efacc17bd1 GLK: Handle more user events
65fa4fa7cb GLK: Beginnings of font handling
e5854a6bd3 GLK: Add loading of TrueType fonts
6807e646b4 GLK: Fix initialization of text buffer window lines
b23291b977 GLK: SCOTT: Fix loading game data
998cf547ed GLK: SCOTT: Exit game when ScummVM is closed
9d72d6007e GLK: Move font method stubs from Screen to Fonts
13e2838715 GLK: Implement font drawing methods
ffe0f5220b GLK: Converted fillRect to use a Rect to specify bounds
a9f5b3351e GLK: Fix fg/bg window color initialization
4fcffce505 GLK: Changes of NULL to nullptr
48a00e7c00 GLK: Set Windows fields from int to bool
1a889689f8 GLK: Added pair window drawing
0a21220707 GLK: Add game frames to actually render the screen
4fc1c30423 GLK: Fix Attributes equality checks
e004784d23 GLK: Fix calculation of text grid height
0d3495bb3b GLK: Add missing configuration defaults
c5668d9300 GLK: Further configuration fixes
7ebbcc87dc GLK: Fix rendering text in text grid windows
7c7178c419 GLK: Text is partially displaying in the buffer window
666edf52a5 GLK: Don't have borders between windows by default
f17998762d GLK: Add drawCaret method
e9c7b30246 GLK: Proper handling of font baseline and leading
012d4bafbb GLK: Fix placement of input cursor
c0fd6ca3e4 GLK: Cleanup of comments in text window classes
d61a7a9ee6 GLK: Change key handle if block to a switch statement
4229312b22 GLK: Cleanup of formatting in text buffer window class
4b5db1da07 GLK: Fix showing input text to the right of input prompt
d954f70d49 GLK: Fix having space between prmpt and input text
2551927bf6 GLK: Fix processing input after Enter is pressed
e36dbfaf2e GLK: Prompt for keypress when game is exited
55c05a0d7a GLK: SCOTT: Camel case method names
aafaace942 GLK: SCOTT: Switch command line options setup to use ConfigMan
99266b8586 GLK: Beginnings of file reference handling
a24b27b14a GLK: Create glk fileref methods
063cfb35ef GLK: Added glk style methods
f91cdb19bf GLK: Add glk style hint methods
60146c383d GLK: Merge Files class into existing Streams class
db112fc9f0 GLK: Adding file stream opening and closing
30cd230d88 GLK: Fixes for file stream reading
8333aed5c2 GLK: Adding extra fields to the savegame format for validation
ce582aab00 GLK: Further work on game detection
8bb3f55dff GLK: SCOTT: Add custom game Ids for each known Scott Adams game
d2554a73fb GLK: Fix refreshing grid windows when text is changed
0ea7f92b67 GLK: SCOTT: Added saveGameState and loadGameState
2e11923677 GLK: SCOTT: Added startup savegame loading
77a4e99c31 GLK: Add savegame listing
9bd7bc87b6 GLK: Added savegame meta info retrieval
db61f4e050 GLK: Add miscellaneous stream methods
75f1ac769b GLK: SCOTT: Fix loading and saving games
6b39263dab GLK: Workaround to show caret for second line entry onwards
135c3adc94 GLK: In progress adding mouse cursors
c5234c9f72 GLK: Fix display of mouse cursor
8e1be3a95c GLK: Text selection highlighting is now working
88315e7349 GLK: Fix selection area getting removed when mouse button was released
596a36aef0 GLK: Allow video mode to be specified in the configuration
65167bd202 GLK: Added a fonts.dat Zip archive containing the fonts
d6538380d7 GLK: Implemented pasting from clipboard
c4bcb0882f GLK: Merge Clipboard and WindowMask into a new Selection class
3c2fd3e74f GLK: Add copying to clipboard
7cba554fc5 GLK: Don't leading & baseline for fonts go below minimum size
e208e14796 GLK: Get proper baseline and leading from loaded fonts
f73d56f6ba GLK: Setup default font colors to match garglk defaults
671321c45f GLK: Fix loading of font colors and styles from configuration
43bee7a727 GLK: Add GUI options to default detection entry
601e1d486b GLK: Add GLK timer intervals
4bd3a4a9c6 GLK: Cleanup of glk_gestalt_ext switch
c524c5859e GLK: Add garglk_unput_string methods
df8dec156e GLK: Add garglk_set_zcolors methods
7cb69167de GLK: Add the garglk_set_reversevideo methods
5ddf55da6c GLK: SCOTT: Convert comments to single line veresions
a8e656a5de GLK: astyle formatting
a698756c24 GLK: SCOTT: Don't show intro text when loading savegame from launcher
fbed3ed7a3 GLK: SCOTT: Handle negative values in readInts
62315b969d GLK: gcc compilation fixes
cde50e00ed GLK: Fix handling of negative file offsets in MemoryStream::setPosition
43cee5fb0f GLK: Fix setTime converting seconds to a date/time structure
c589b807e2 GLK: FROTZ: Skeleton sub-engine for Frotz interpreter
1aaf2fd145 GLK: FROTZ: Fleshing out detection logic and added some entries
8316c1a5b2 GLK: FROTZ: Beginnings of initialization
7d20a3552b GLK: FROTZ: Added error handling
4497a55a86 GLK: FROTZ: Beginnings of Mem class
b8fcb62dd2 GLK: Added Blorb container file handling
fc4c3293d1 GLK: FROTZ: Add parsing if game file is a Blorb container
f19f92e396 GLK: FROTZ: Move Header to be an ancestor class to Mem
cc9b52e5ac GLK: Implement the removeSaveState method
38a458139a GLK: Add Gargoyle engine to credits.pl
a083eb3d5c GLK: FROTZ: Added bulk of game processor
9fadd84b37 GLK: FROTZ: Add GlkInterface initialize method
ad95130e4f GLK: FROTZ: Added user options initialization from configuration
7a3dd94de1 GLK: FROTZ: Add undo data initialization
fbe7fb7e1a GLK: FROTZ: Merge Buffer class into Processor
53612fa6f7 GLK: FROTZ: Added storeb and storew methods
1b4791f923 GLK: FROTZ: Removal of method stubs that are now implemented
1ca59e74c3 GLK: FROTZ: Added storeb & storew into the Mem class
5507242395 GLK: Fix casting of vardiac retrieval
e1de76b491 GLK: FROTZ: Added remaining memory methods
2c37a94913 GLK: SCOTT: Fix detecting .dat gamefiles, added Windows game versions detections
7a52f21c0b GLK: FROTZ: Added script/record/transcript/replay methods
aab1dfeff8 GLK: FROTZ: Added memory redirect methods
7d2406870e GLK: FROTZ: Add screen message methods
ce7113b34a GLK: FROTZ: Added OS read line and character methods
c19d40fa78 GLK: FROTZ: Implemented Quetzal class for savegames
6b23a72e51 GLK: FROTZ: Merge Err class into Processor
7b90a2b60e GLK: FROTZ: Hook up processor execution to main runGame method
7d670ff157 GLK: FROTZ: Fix stack size
1fb931fbd9 GLK: Changing gargoyle folder to glk
a2104deb4c GLK: Changing class and namespaces from Gargoyle to Glk
454f92cc74 GLK: SCOTT: Centralize the set of game names
67275924b5 GLK: Derive detection from MetaEngine rather than AdvancedMetaEngine
eff0ac80ff GLK: Handle starting the engine directly from the command line
7b1b3935b8 GLK: Fix remaining Gargoyle references identified by digitall
a1d355d0be GLK: FROTZ: Move ostream_* flags from GlkInterface to Processor
f29147ba22 GLK: Fix showing unicode input lines
beddf3853e GLK: FROTZ: Fill out HHGTTG version serial numbers
af081ad322 GLK: FROTZ: Improve fallback detection of Z-code games
d9e7fca83c GLK: FROTZ: Fix exiting when application is forcefully closed
04f42acca0 GLK: FROTZ: Allow exiting ScumMVM when waiting for a keypress
625df27cfc GLK: FROTZ: Adding detection entries for Infocom games
dee20ace65 GLK: FROTZ: Added detection entries for more Infocom games
238022890e GLK: FROTZ: Debug code to generate detection entries for unknown games
bd4bc0a43f GLK: FROTZ: Fix crash clicking screen with mouse without dragging
027240678e GLK: SCOTT: Change engine to use ScummVM String and Arrays
83646598c9 GLK: SCOTT: Renaming of all fields to ScummVM convention
fc2b1edd1f GLK: SCOTT: Extra returns/exits after glk_exit is called
bc4df65bd7 GLK: Fix gcc compiler warnings
e344809eea GLK: FROTZ: Fix gcc warnings, adding extra method comments
3d34cd151f GLK: FROTZ: Remove unused local variable
4b011b2f1c GLK: Freeing of data on exit
813918aaba GLK: Memory leak fixes
326f69136e GLK: SCOTT: Fix memory corruption loading game data
b05a16a0ad GLK: Centralizing more of the savegame code in GlkEngine
3b906cb2bb GLK: Fixes for combined loading/saving code
fc86b3705b GLK: Fix initializing fields of created streams
0167d988fa GLK: Free windows on exit
88b0e5d52f GLK: Change text buffer history array to use Array of U32Strings
41a4378169 GLK: SCOTT: Don't show another line prompt when game is exiting
79f3d946ef GLK: FROTZ: Shift Header class into it's own file
ada80dedc0 GLK: FROTZ: Further merging together of configuration into a new config.cpp file
2a3e6c49bf GLK: FROTZ: Set up default Frotz white on blue screen colors
dfe497cb79 GLK: FROTZ: Move setting default colors to GlkInterface
9bc6379476 GLK: Change lots of comments from multiline to single line
0f0b8ae3b7 GLK: FROTZ: Don't have a Y margin above title bar
bd86fd7bbf GLK: Implementing picture loading
5a05140ac3 GLK: Add picture scaling
f7cb417084 GLK: Add picture drawing
525c09ef38 GLK: In progress transforming Blorb code to be a Common::Archive
13e28c0d0d GLK: Finish refactoring Blorb class
720ef67a7d GLK: FROTZ: Fixed detection of Infocom .dat gamefiles
0b1e695f24 GLK: FROTZ: Beginnings of support for Infocom picture files
3e8ed4eafc GRAPHICS: Add convertToInPlace method to ManagedSurface
6f50812493 GLK: Added RawDecoder class for new raw picture data format
a334cd704e GLK: FROTZ: Further loading of Infocom pictures files
f92b82664d GLK: Fix gcc warnings
0aff016ad9 GLK: FROTZ: Fix deletion of undo_mem
5b9e1a53ad GLK: FROTZ: Fix mismatch allocation/free
c7a632ae2e GLK: FROTZ: Fix initialization of undo data
4a3f517b71 GLK: FROTZ: Prompt for exit when game quits
1c190e50b4 GLK: Mark text strings as translateable
3700919881 GLK: Add POTFILES
62af5ea891 GLK: FROTZ: More work on displaying Beyond Zork title screen
77468312de GLK: FROTZ: Beyond Zork title screen now showing
c2625264fb GLK: Change int 0 to false
5cc3d40c28 GLK: FROTZ: Adding bitmap font class for Infocom character graphics
fb7dbffd59 GLK: Merge Fonts class into Screen class
3a543a1e6d GLK: FROTZ: Add derived Screen class to add Infocom character graphics font
9fa7e9be81 GLK: FROTZ: Fix selection of the character graphics font
6080a1d091 GLK: FROTZ: Draw character graphic chars at the same size as the fixed width font
6e6b285e83 GLK: FROTZ: Savegame listing now reads Quetzal savegames
33a23bca1c GLK: FROTZ: Further reading/writing setup for savegames using Quetzal
2331a0e9fb GLK: FROTZ: Quetzal saving and loading now works
bfbc0ba0ed GLK: FROTZ: Allow loading savegames from launcher
855911d0c0 GLK: FROTZ: Fix display of character graphics in text grid windows
0b7a29c797 GLK: TADS: Skeleton engine
09b682cb63 GLK: TADS: Skeletons for TADS 2 & 3 subengines, extra detection logic
75f4d34070 GLK: TADS: Add first detection entry, further fallback detection
d2836360fb GLK: TADS: GLK window initialization
5ab65b0954 GLK: TADS: TADS2: Add ler file
41b62ab00d GLK: TADS: Add a number of GLK interface methods
d3c46f7078 GLK: TADS: Add Regex class
f20d07381c GLK: TADS: Added cmap file
14871c28bc GLK: TADS: Added Data class
c68542dba8 GLK: TADS: Compilation fix
7212924b1e GLK: TADS: Compilation fixes
1933130dfc GLK: FROTZ: Fix setting cwin field when window changes
63079834fa GLK: FROTZ: Improved rendering of Beyond Zork description area
d7c015a17e GLK: FROTZ: Detect Infocom games using .zip extensions
4894ce2134 GLK: FROTZ: Add all the new V6 methods, commented out until I can fix each one
7fda941302 GLK: FROTZ: Implement some of the V6 opcodes
ab8ff73453 GLK: FROTZ: Fix handling of empty rect picture resources
88b47cdd8b GLK: FROTZ: Added winarg methods
2ae0edd58b GLK: FROTZ: Add sound playing code
f6abb3ea33 GLK: FROTZ: Simplify accessing sounds for Lurking Horror & Sherlock
3661fc61ec GLK: Beginnings of Sounds manager
0cbd51641b GLK: FROTZ: Further sound handling
43283ff561 GLK: FROTZ: Playing sounds now works directly from lhsound.zip and shsound.zip
08db684864 GLK: Add support for secondary Blorb files separate from the gamefile
ecb0c10fdb GLK: FROTZ: Properly flag the detections of Infocom games that have sound effects
754819f928 GLK: FROTZ: Reduce window margin to 0 to have status bar fill entire width
290196946d GLK: GLULXE: Skeleton sub-engine
ac9830614d GLK: GLULXE: Validate game file version


Commit: c4d4d48d67953687b1d21eb1f7e01fb4a508958b
    https://github.com/scummvm/scummvm/commit/c4d4d48d67953687b1d21eb1f7e01fb4a508958b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Initial skeleton engine

Changed paths:
  A engines/gargoyle/configure.engine
  A engines/gargoyle/detection.cpp
  A engines/gargoyle/detection_tables.h
  A engines/gargoyle/gargoyle.cpp
  A engines/gargoyle/gargoyle.h
  A engines/gargoyle/module.mk


diff --git a/engines/gargoyle/configure.engine b/engines/gargoyle/configure.engine
new file mode 100644
index 0000000..671f91c
--- /dev/null
+++ b/engines/gargoyle/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine gargoyle "Interactive Fiction games" no
diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
new file mode 100644
index 0000000..94fb90f
--- /dev/null
+++ b/engines/gargoyle/detection.cpp
@@ -0,0 +1,129 @@
+/* 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 "gargoyle/gargoyle.h"
+
+#include "base/plugins.h"
+#include "common/savefile.h"
+#include "common/str-array.h"
+#include "common/memstream.h"
+#include "engines/advancedDetector.h"
+#include "common/system.h"
+#include "graphics/colormasks.h"
+#include "graphics/surface.h"
+
+#define MAX_SAVES 99
+
+namespace Gargoyle {
+
+struct GargoyleGameDescription {
+	ADGameDescription desc;
+};
+
+uint32 GargoyleEngine::getFeatures() const {
+	return _gameDescription->desc.flags;
+}
+
+bool GargoyleEngine::isDemo() const {
+	return (bool)(_gameDescription->desc.flags & ADGF_DEMO);
+}
+
+Common::Language GargoyleEngine::getLanguage() const {
+	return _gameDescription->desc.language;
+}
+
+} // End of namespace Gargoyle
+
+static const PlainGameDescriptor GargoyleGames[] = {
+	{"Gargoyle", "Gargoyle Games"},
+	{0, 0}
+};
+
+#include "gargoyle/detection_tables.h"
+
+class GargoyleMetaEngine : public AdvancedMetaEngine {
+public:
+	GargoyleMetaEngine() : AdvancedMetaEngine(Gargoyle::gameDescriptions, sizeof(Gargoyle::GargoyleGameDescription), GargoyleGames) {
+		_maxScanDepth = 3;
+	}
+
+	virtual const char *getName() const {
+		return "Gargoyle Engine";
+	}
+
+	virtual const char *getOriginalCopyright() const {
+		return "Gargoyle Engine (c)";
+	}
+
+	virtual bool hasFeature(MetaEngineFeature f) const;
+	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+	virtual SaveStateList listSaves(const char *target) const;
+	virtual int getMaximumSaveSlot() const;
+	virtual void removeSaveState(const char *target, int slot) const;
+	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+};
+
+bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return
+	    (f == kSupportsListSaves) ||
+		(f == kSupportsLoadingDuringStartup) ||
+		(f == kSupportsDeleteSave) ||
+		(f == kSavesSupportMetaInfo) ||
+		(f == kSavesSupportThumbnail);
+}
+
+bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
+	return
+		(f == kSupportsRTL) ||
+		(f == kSupportsLoadingDuringRuntime) ||
+		(f == kSupportsSavingDuringRuntime);
+}
+
+bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+	const Gargoyle::GargoyleGameDescription *gd = (const Gargoyle::GargoyleGameDescription *)desc;
+	*engine = new Gargoyle::GargoyleEngine(syst, gd);
+
+	return gd != 0;
+}
+
+SaveStateList GargoyleMetaEngine::listSaves(const char *target) const {
+	SaveStateList saveList;
+	return saveList;
+}
+
+int GargoyleMetaEngine::getMaximumSaveSlot() const {
+	return MAX_SAVES;
+}
+
+void GargoyleMetaEngine::removeSaveState(const char *target, int slot) const {
+}
+
+SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+	return SaveStateDescriptor();
+}
+
+
+#if PLUGIN_ENABLED_DYNAMIC(GARGOYLE)
+	REGISTER_PLUGIN_DYNAMIC(Gargoyle, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
+#else
+	REGISTER_PLUGIN_STATIC(GARGOYLE, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
+#endif
diff --git a/engines/gargoyle/detection_tables.h b/engines/gargoyle/detection_tables.h
new file mode 100644
index 0000000..780cfe2
--- /dev/null
+++ b/engines/gargoyle/detection_tables.h
@@ -0,0 +1,41 @@
+/* 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.
+ *
+ */
+
+namespace Gargoyle {
+	
+static const GargoyleGameDescription gameDescriptions[] = {
+	{
+		{
+			"Gargoyle",
+			0,
+			AD_ENTRY1s("dummy", "0", 0),
+			Common::EN_ANY,
+			Common::kPlatformDOS,
+			ADGF_NO_FLAGS,
+			GUIO0()
+		},
+	},
+
+	{ AD_TABLE_END_MARKER }
+};
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
new file mode 100644
index 0000000..79cbaf2
--- /dev/null
+++ b/engines/gargoyle/gargoyle.cpp
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "engines/util.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
+#include "gargoyle/gargoyle.h"
+
+namespace Gargoyle {
+
+GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc)
+	: _gameDescription(gameDesc), Engine(syst) {
+}
+
+GargoyleEngine::~GargoyleEngine() {
+}
+
+void GargoyleEngine::initialize() {
+	// Set up debug channels
+	DebugMan.addDebugChannel(kDebugCore, "core", "Core engine debug level");
+	DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
+	DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling");
+	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
+
+	initGraphics(640, 480, false);
+}
+
+Common::Error GargoyleEngine::run() {
+	initialize();
+
+	return Common::kNoError;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
new file mode 100644
index 0000000..79016ea
--- /dev/null
+++ b/engines/gargoyle/gargoyle.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_GARGOLE_H
+#define GARGOYLE_GARGOLE_H
+
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/serializer.h"
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
+
+namespace Gargoyle {
+
+enum GargoyleDebugChannels {
+	kDebugCore      = 1 << 0,
+	kDebugScripts	= 1 << 1,
+	kDebugGraphics	= 1 << 2,
+	kDebugSound     = 1 << 3
+};
+
+#define GARGOYLE_SAVEGAME_VERSION 1
+
+struct GargoyleGameDescription;
+
+struct GargoyleSavegameHeader {
+	uint8 _version;
+	Common::String _saveName;
+	Graphics::Surface *_thumbnail;
+	int _year, _month, _day;
+	int _hour, _minute;
+	int _totalFrames;
+};
+
+class GargoyleEngine : public Engine {
+private:
+	/**
+	 * Handles basic initialization
+	 */
+	void initialize();
+protected:
+	const GargoyleGameDescription *_gameDescription;
+	int _loadSaveSlot;
+
+	// Engine APIs
+	virtual Common::Error run();
+	virtual bool hasFeature(EngineFeature f) const;
+public:
+	GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
+	virtual ~GargoyleEngine();
+
+	uint32 getFeatures() const;
+	bool isDemo() const;
+	Common::Language getLanguage() const;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
new file mode 100644
index 0000000..81b1389
--- /dev/null
+++ b/engines/gargoyle/module.mk
@@ -0,0 +1,13 @@
+MODULE := engines/gargoyle
+
+MODULE_OBJS := \
+	detection.o \
+	gargoyle.o
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_GARGOYLE), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk


Commit: 9c536cb6bff1744b39b20a9d29c9e33315025e2d
    https://github.com/scummvm/scummvm/commit/9c536cb6bff1744b39b20a9d29c9e33315025e2d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add stub classes for sub-engine components

Changed paths:
  A engines/gargoyle/events.cpp
  A engines/gargoyle/events.h
  A engines/gargoyle/glk.cpp
  A engines/gargoyle/glk.h
  A engines/gargoyle/interpreter.cpp
  A engines/gargoyle/interpreter.h
  A engines/gargoyle/scott/scott.cpp
  A engines/gargoyle/scott/scott.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
new file mode 100644
index 0000000..93021ef
--- /dev/null
+++ b/engines/gargoyle/events.cpp
@@ -0,0 +1,31 @@
+/* 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 "gargoyle/events.h"
+
+namespace Gargoyle {
+
+void Events::pollEvents() {
+	// TODO
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
new file mode 100644
index 0000000..ac66d0e
--- /dev/null
+++ b/engines/gargoyle/events.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_EVENTS_H
+#define GARGOYLE_EVENTS_H
+
+#include "common/events.h"
+
+namespace Gargoyle {
+
+class Events {
+public:
+	/**
+	 * Checks for new events
+	 */
+	void pollEvents();
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 79cbaf2..e55e4c5 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -31,8 +31,9 @@
 
 namespace Gargoyle {
 
-GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc)
-	: _gameDescription(gameDesc), Engine(syst) {
+GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+	_gameDescription(gameDesc), Engine(syst), _glk(&_screen),
+	_scott(_glk) {
 }
 
 GargoyleEngine::~GargoyleEngine() {
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 79016ea..5e014b5 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -28,6 +28,10 @@
 #include "common/serializer.h"
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
+#include "graphics/screen.h"
+#include "gargoyle/events.h"
+#include "gargoyle/glk.h"
+#include "gargoyle/scott/scott.h"
 
 namespace Gargoyle {
 
@@ -57,9 +61,13 @@ private:
 	 * Handles basic initialization
 	 */
 	void initialize();
-protected:
+private:
 	const GargoyleGameDescription *_gameDescription;
 	int _loadSaveSlot;
+	Graphics::Screen _screen;
+	Events _events;
+	Glk _glk;
+	Scott::Scott _scott;
 
 	// Engine APIs
 	virtual Common::Error run();
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
new file mode 100644
index 0000000..82447b5
--- /dev/null
+++ b/engines/gargoyle/glk.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 "gargoyle/glk.h"
+
+namespace Gargoyle {
+
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
new file mode 100644
index 0000000..86c2353
--- /dev/null
+++ b/engines/gargoyle/glk.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_GLK_H
+#define GARGOYLE_GLK_H
+
+#include "graphics/managed_surface.h"
+
+namespace Gargoyle {
+
+/**
+ * Implements the GLK interface
+ */
+class Glk {
+private:
+	Graphics::ManagedSurface *_surface;
+public:
+	/**
+	 * Constructor
+	 */
+	Glk(Graphics::ManagedSurface *surface) : _surface(surface) {}
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/interpreter.cpp b/engines/gargoyle/interpreter.cpp
new file mode 100644
index 0000000..10cefd7
--- /dev/null
+++ b/engines/gargoyle/interpreter.cpp
@@ -0,0 +1,28 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 "gargoyle/interpreter.h"
+
+namespace Gargoyle {
+
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/interpreter.h b/engines/gargoyle/interpreter.h
new file mode 100644
index 0000000..002b46c
--- /dev/null
+++ b/engines/gargoyle/interpreter.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_INTERPRETER_H
+#define GARGOYLE_INTERPRETER_H
+
+#include "gargoyle/glk.h"
+
+namespace Gargoyle {
+
+/**
+ * Base class for specific interpreters
+ */
+class Interpreter {
+protected:
+	Glk &_glk;
+public:
+	/**
+	 * Constructor
+	 */
+	Interpreter(Glk &glk) : _glk(glk) {}
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 81b1389..937b814 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -2,7 +2,11 @@ MODULE := engines/gargoyle
 
 MODULE_OBJS := \
 	detection.o \
-	gargoyle.o
+	events.o \
+	gargoyle.o \
+	glk.o \
+	interpreter.o \
+	scott/scott.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_GARGOYLE), DYNAMIC_PLUGIN)
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
new file mode 100644
index 0000000..b5e537c
--- /dev/null
+++ b/engines/gargoyle/scott/scott.cpp
@@ -0,0 +1,30 @@
+/* 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 "gargoyle/scott/scott.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
new file mode 100644
index 0000000..a5d734f
--- /dev/null
+++ b/engines/gargoyle/scott/scott.h
@@ -0,0 +1,98 @@
+/* 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 GARGOYLE_SCOTT
+#define GARGOYLE_SCOTT
+
+/*
+ *	Controlling block
+ */
+
+#include "common/scummsys.h"
+#include "gargoyle/interpreter.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+#define LIGHT_SOURCE	9	// Always 9 how odd
+#define CARRIED		255		// Carried
+#define DESTROYED	0		// Destroyed
+#define DARKBIT		15
+#define LIGHTOUTBIT	16		// Light gone out
+
+#define YOUARE		1		// You are not I am
+#define SCOTTLIGHT	2		// Authentic Scott Adams light messages
+#define DEBUGGING	4		// Info from database load
+#define TRS80_STYLE	8		// Display in style used on TRS-80
+#define PREHISTORIC_LAMP 16	// Destroy the lamp (very old databases)
+
+struct Header {
+ 	int Unknown;
+	int NumItems;
+	int NumActions;
+	int NumWords;			// Smaller of verb/noun is padded to same size
+	int NumRooms;
+	int MaxCarry;
+	int PlayerRoom;
+	int Treasures;
+	int WordLength;
+	int LightTime;
+	int NumMessages;
+	int TreasureRoom;
+};
+
+struct Action {
+	uint Vocab;
+	uint Condition[5];
+	uint action[2];
+};
+
+struct Room {
+	char *Text;
+	short Exits[6];
+};
+
+struct Item {
+	char *Text;		// PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
+	byte Location;
+	byte InitialLoc;
+	char *AutoGet;
+};
+
+struct Tail {
+	int Version;
+	int AdventureNumber;
+	int Unknown;
+};
+
+class Scott : public Interpreter {
+public:
+	/**
+	 * Constructor
+	 */
+	Scott(Glk &glk) : Interpreter(glk) {}
+};
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
+
+#endif


Commit: 46aafc35583baefec5b94bde726ea9bddbdacc59
    https://github.com/scummvm/scummvm/commit/46aafc35583baefec5b94bde726ea9bddbdacc59
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Refactor Glk to derice from Interpreter

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


diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index e55e4c5..731e38d 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -32,8 +32,7 @@
 namespace Gargoyle {
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-	_gameDescription(gameDesc), Engine(syst), _glk(&_screen),
-	_scott(_glk) {
+	_gameDescription(gameDesc), Engine(syst), _interpreter(nullptr) {
 }
 
 GargoyleEngine::~GargoyleEngine() {
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 5e014b5..c43b5fe 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -66,8 +66,7 @@ private:
 	int _loadSaveSlot;
 	Graphics::Screen _screen;
 	Events _events;
-	Glk _glk;
-	Scott::Scott _scott;
+	Interpreter *_interpreter;
 
 	// Engine APIs
 	virtual Common::Error run();
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 86c2353..4aade7a 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -24,20 +24,21 @@
 #define GARGOYLE_GLK_H
 
 #include "graphics/managed_surface.h"
+#include "gargoyle/interpreter.h"
 
 namespace Gargoyle {
 
 /**
  * Implements the GLK interface
  */
-class Glk {
+class Glk : public Interpreter {
 private:
 	Graphics::ManagedSurface *_surface;
 public:
 	/**
 	 * Constructor
 	 */
-	Glk(Graphics::ManagedSurface *surface) : _surface(surface) {}
+	Glk() {}
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/interpreter.h b/engines/gargoyle/interpreter.h
index 002b46c..df9172d 100644
--- a/engines/gargoyle/interpreter.h
+++ b/engines/gargoyle/interpreter.h
@@ -23,21 +23,27 @@
 #ifndef GARGOYLE_INTERPRETER_H
 #define GARGOYLE_INTERPRETER_H
 
-#include "gargoyle/glk.h"
-
 namespace Gargoyle {
 
 /**
  * Base class for specific interpreters
  */
 class Interpreter {
-protected:
-	Glk &_glk;
 public:
 	/**
 	 * Constructor
 	 */
-	Interpreter(Glk &glk) : _glk(glk) {}
+	Interpreter() {}
+
+	/**
+	 * Destructor
+	 */
+	virtual ~Interpreter() {}
+
+	/**
+	 * Main execution method
+	 */
+	virtual void execute() = 0;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
index a5d734f..bf13cf8 100644
--- a/engines/gargoyle/scott/scott.h
+++ b/engines/gargoyle/scott/scott.h
@@ -28,7 +28,7 @@
  */
 
 #include "common/scummsys.h"
-#include "gargoyle/interpreter.h"
+#include "gargoyle/glk.h"
 
 namespace Gargoyle {
 namespace Scott {
@@ -84,12 +84,15 @@ struct Tail {
 	int Unknown;
 };
 
-class Scott : public Interpreter {
+/**
+ * Scott Adams game interpreter
+ */
+class Scott : public Glk {
 public:
 	/**
 	 * Constructor
 	 */
-	Scott(Glk &glk) : Interpreter(glk) {}
+	Scott() : Glk() {}
 };
 
 } // End of namespace Scott


Commit: b2067f3be797352cf2735e985e2a130a7bf386cb
    https://github.com/scummvm/scummvm/commit/b2067f3be797352cf2735e985e2a130a7bf386cb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added Glk enums and structures

Changed paths:
    engines/gargoyle/glk.h


diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 4aade7a..781b196 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -28,6 +28,199 @@
 
 namespace Gargoyle {
 
+typedef uint32 glui32;
+typedef int32 glsi32;
+
+/**
+ * These are the compile-time conditionals that reveal various Glk optional modules.
+ */
+#define GLK_MODULE_LINE_ECHO
+#define GLK_MODULE_LINE_TERMINATORS
+#define GLK_MODULE_UNICODE
+#define GLK_MODULE_UNICODE_NORM
+#define GLK_MODULE_IMAGE
+#define GLK_MODULE_SOUND
+#define GLK_MODULE_SOUND2
+#define GLK_MODULE_HYPERLINKS
+#define GLK_MODULE_DATETIME
+#define GLK_MODULE_GARGLKTEXT
+
+/**
+ * These types are opaque object identifiers. They're pointers to opaque
+ * C structures, which are defined differently by each library.
+ */
+typedef struct glk_window_struct  *winid_t;
+typedef struct glk_stream_struct  *strid_t;
+typedef struct glk_fileref_struct *frefid_t;
+typedef struct glk_schannel_struct *schanid_t;
+
+enum Gestalt {
+	gestalt_Version                = 0,
+	gestalt_CharInput              = 1,
+	gestalt_LineInput              = 2,
+	gestalt_CharOutput             = 3,
+	gestalt_CharOutput_CannotPrint = 0,
+	gestalt_CharOutput_ApproxPrint = 1,
+	gestalt_CharOutput_ExactPrint  = 2,
+	gestalt_MouseInput             = 4,
+	gestalt_Timer                  = 5,
+	gestalt_Graphics               = 6,
+	gestalt_DrawImage              = 7,
+	gestalt_Sound                  = 8,
+	gestalt_SoundVolume            = 9,
+	gestalt_SoundNotify            = 10,
+	gestalt_Hyperlinks             = 11,
+	gestalt_HyperlinkInput         = 12,
+	gestalt_SoundMusic             = 13,
+	gestalt_GraphicsTransparency   = 14,
+	gestalt_Unicode                = 15,
+	gestalt_UnicodeNorm            = 16,
+	gestalt_LineInputEcho          = 17,
+	gestalt_LineTerminators        = 18,
+	gestalt_LineTerminatorKey      = 19,
+	gestalt_DateTime               = 20,
+	gestalt_Sound2                 = 21,
+	gestalt_GarglkText             = 0x1100,
+};
+
+enum EvType {
+	evtype_None         = 0,
+	evtype_Timer        = 1,
+	evtype_CharInput    = 2,
+	evtype_LineInput    = 3,
+	evtype_MouseInput   = 4,
+	evtype_Arrange      = 5,
+	evtype_Redraw       = 6,
+	evtype_SoundNotify  = 7,
+	evtype_Hyperlink    = 8,
+	evtype_VolumeNotify = 9,
+};
+
+enum Keycode {
+	keycode_Unknown  = 0xffffffffU,
+	keycode_Left     = 0xfffffffeU,
+	keycode_Right    = 0xfffffffdU,
+	keycode_Up       = 0xfffffffcU,
+	keycode_Down     = 0xfffffffbU,
+	keycode_Return   = 0xfffffffaU,
+	keycode_Delete   = 0xfffffff9U,
+	keycode_Escape   = 0xfffffff8U,
+	keycode_Tab      = 0xfffffff7U,
+	keycode_PageUp   = 0xfffffff6U,
+	keycode_PageDown = 0xfffffff5U,
+	keycode_Home     = 0xfffffff4U,
+	keycode_End      = 0xfffffff3U,
+	keycode_Func1    = 0xffffffefU,
+	keycode_Func2    = 0xffffffeeU,
+	keycode_Func3    = 0xffffffedU,
+	keycode_Func4    = 0xffffffecU,
+	keycode_Func5    = 0xffffffebU,
+	keycode_Func6    = 0xffffffeaU,
+	keycode_Func7    = 0xffffffe9U,
+	keycode_Func8    = 0xffffffe8U,
+	keycode_Func9    = 0xffffffe7U,
+	keycode_Func10   = 0xffffffe6U,
+	keycode_Func11   = 0xffffffe5U,
+	keycode_Func12   = 0xffffffe4U,
+	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
+	keycode_MAXVAL   = 28U,
+};
+
+enum Style {
+	style_Normal       = 0,
+	style_Emphasized   = 1,
+	style_Preformatted = 2,
+	style_Header       = 3,
+	style_Subheader    = 4,
+	style_Alert        = 5,
+	style_Note         = 6,
+	style_BlockQuote   = 7,
+	style_Input        = 8,
+	style_User1        = 9,
+	style_User2        = 10,
+	style_NUMSTYLES    = 11,
+};
+
+enum WinType {
+	wintype_AllTypes   = 0,
+	wintype_Pair       = 1,
+	wintype_Blank      = 2,
+	wintype_TextBuffer = 3,
+	wintype_TextGrid   = 4,
+	wintype_Graphics   = 5,
+};
+
+enum WinMethod {
+	winmethod_Left    = 0x00,
+	winmethod_Right   = 0x01,
+	winmethod_Above   = 0x02,
+	winmethod_Below   = 0x03,
+	winmethod_DirMask = 0x0f,
+
+	winmethod_Fixed        = 0x10,
+	winmethod_Proportional = 0x20,
+	winmethod_DivisionMask = 0xf0,
+
+	winmethod_Border     = 0x000,
+	winmethod_NoBorder   = 0x100,
+	winmethod_BorderMask = 0x100,
+};
+
+enum FileUsage {
+	fileusage_Data        = 0x00,
+	fileusage_SavedGame   = 0x01,
+	fileusage_Transcript  = 0x02,
+	fileusage_InputRecord = 0x03,
+	fileusage_TypeMask    = 0x0f,
+
+	fileusage_TextMode    = 0x100,
+	fileusage_BinaryMode  = 0x000,
+};
+
+enum FileMode {
+	filemode_Write       = 0x01,
+	filemode_Read        = 0x02,
+	filemode_ReadWrite   = 0x03,
+	filemode_WriteAppend = 0x05,
+};
+
+enum SeekMode {
+	seekmode_Start   = 0,
+	seekmode_Current = 1,
+	seekmode_End     = 2,
+};
+
+enum StyleHint {
+	stylehint_Indentation     = 0,
+	stylehint_ParaIndentation = 1,
+	stylehint_Justification  = 2,
+	stylehint_Size            = 3,
+	stylehint_Weight          = 4,
+	stylehint_Oblique         = 5,
+	stylehint_Proportional    = 6,
+	stylehint_TextColor       = 7,
+	stylehint_BackColor       = 8,
+	stylehint_ReverseColor    = 9,
+	stylehint_NUMHINTS        = 10,
+
+	stylehint_just_LeftFlush  = 0,
+	stylehint_just_LeftRight  = 1,
+	stylehint_just_Centered   = 2,
+	stylehint_just_RightFlush = 3,
+};
+
+struct event_t {
+	glui32 type;
+	winid_t win;
+	glui32 val1, val2;
+};
+
+struct stream_result_t {
+	glui32 readcount;
+	glui32 writecount;
+};
+
+
 /**
  * Implements the GLK interface
  */


Commit: fa253ddba703f68fa60fc57316228d69b936a36e
    https://github.com/scummvm/scummvm/commit/fa253ddba703f68fa60fc57316228d69b936a36e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added stubs for Glk methods

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


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 82447b5..4ea4ce4 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -4,18 +4,18 @@
  * 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
+ * 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
+ * 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
+ * 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
+ * along with this program{} if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  */
@@ -24,5 +24,510 @@
 
 namespace Gargoyle {
 
+void Glk::glk_exit(void) {
+}
+
+void Glk::glk_set_interrupt_handler(void(*func)(void)) {
+}
+
+void Glk::glk_tick(void) {
+}
+
+glui32 Glk::glk_gestalt(glui32 sel, glui32 val) {
+	return 0;
+}
+
+glui32 Glk::glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) {
+	return 0;
+}
+
+unsigned char Glk::glk_char_to_lower(unsigned char ch) {
+	return '\0';
+}
+
+unsigned char Glk::glk_char_to_upper(unsigned char ch) {
+	return '\0';
+}
+
+winid_t Glk::glk_window_get_root(void) {
+	return nullptr;
+}
+
+winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size,
+	glui32 wintype, glui32 rock) {
+	return nullptr;
+}
+
+void Glk::glk_window_close(winid_t win, stream_result_t *result) {
+}
+
+void Glk::glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) {
+}
+
+void Glk::glk_window_set_arrangement(winid_t win, glui32 method,
+	glui32 size, winid_t keywin) {
+}
+
+void Glk::glk_window_get_arrangement(winid_t win, glui32 *methodptr,
+	glui32 *sizeptr, winid_t *keywinptr) {
+}
+
+winid_t Glk::glk_window_iterate(winid_t win, glui32 *rockptr) {
+	return nullptr;
+}
+
+glui32 Glk::glk_window_get_rock(winid_t win) {
+	return 0;
+}
+
+glui32 Glk::glk_window_get_type(winid_t win) {
+	return 0;
+}
+
+winid_t Glk::glk_window_get_parent(winid_t win) {
+	return nullptr;
+}
+
+winid_t Glk::glk_window_get_sibling(winid_t win) {
+	return nullptr;
+}
+
+void Glk::glk_window_clear(winid_t win) {
+}
+
+void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
+}
+
+strid_t Glk::glk_window_get_stream(winid_t win) {
+	return nullptr;
+}
+
+void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
+}
+
+strid_t Glk::glk_window_get_echo_stream(winid_t win) {
+	return nullptr;
+}
+
+void Glk::glk_set_window(winid_t win) {
+}
+
+strid_t Glk::glk_stream_open_file(frefid_t fileref, glui32 fmode,
+	glui32 rock) {
+	return nullptr;
+}
+
+strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) {
+	return nullptr;
+}
+
+void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
+}
+
+strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) {
+	return nullptr;
+}
+
+glui32 Glk::glk_stream_get_rock(strid_t str) {
+	return 0;
+}
+
+void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) {
+}
+
+glui32 Glk::glk_stream_get_position(strid_t str) {
+	return 0;
+}
+
+void Glk::glk_stream_set_current(strid_t str) {
+}
+
+strid_t Glk::glk_stream_get_current(void) {
+	return nullptr;
+}
+
+void Glk::glk_put_char(unsigned char ch) {
+}
+
+void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
+}
+
+void Glk::glk_put_string(char *s) {
+}
+
+void Glk::glk_put_string_stream(strid_t str, char *s) {
+}
+
+void Glk::glk_put_buffer(char *buf, glui32 len) {
+}
+
+void Glk::glk_put_buffer_stream(strid_t str, char *buf, glui32 len) {
+}
+
+void Glk::glk_set_style(glui32 styl) {
+}
+
+void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
+}
+
+glsi32 Glk::glk_get_char_stream(strid_t str) {
+	return 0;
+}
+
+glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
+	return 0;
+}
+
+glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
+	return 0;
+}
+
+void Glk::glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val) {
+}
+
+void Glk::glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint) {
+}
+
+glui32 Glk::glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) {
+	return 0;
+}
+
+glui32 Glk::glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result) {
+	return 0;
+}
+
+frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock) {
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock) {
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
+	return nullptr;
+}
+
+void Glk::glk_fileref_destroy(frefid_t fref) {
+}
+
+frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
+	return nullptr;
+}
+
+glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
+	return 0;
+}
+
+void Glk::glk_fileref_delete_file(frefid_t fref) {
+}
+
+glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
+	return 0;
+}
+
+void Glk::glk_select(event_t *event) {
+}
+
+void Glk::glk_select_poll(event_t *event) {
+}
+
+void Glk::glk_request_timer_events(glui32 millisecs) {
+}
+
+void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
+}
+
+void Glk::glk_request_char_event(winid_t win) {
+}
+
+void Glk::glk_request_mouse_event(winid_t win) {
+}
+
+void Glk::glk_cancel_line_event(winid_t win, event_t *event) {
+}
+
+void Glk::glk_cancel_char_event(winid_t win) {
+}
+
+void Glk::glk_cancel_mouse_event(winid_t win) {
+}
+
+#ifdef GLK_MODULE_LINE_ECHO
+
+void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
+}
+
+#endif /* GLK_MODULE_LINE_ECHO */
+
+#ifdef GLK_MODULE_LINE_TERMINATORS
+
+void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
+}
+
+#endif /* GLK_MODULE_LINE_TERMINATORS */
+
+#ifdef GLK_MODULE_UNICODE
+
+glui32 Glk::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	return 0;
+}
+
+glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	return 0;
+}
+
+glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+	glui32 numchars, glui32 lowerrest) {
+	return 0;
+}
+
+void Glk::glk_put_char_uni(glui32 ch) {
+}
+
+void Glk::glk_put_string_uni(glui32 *s) {
+}
+
+void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
+}
+
+void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
+}
+
+void Glk::glk_put_string_stream_uni(strid_t str, glui32 *s) {
+}
+
+void Glk::glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+}
+
+glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
+	return 0;
+}
+
+glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	return 0;
+}
+
+glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	return 0;
+}
+
+strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock) {
+	return nullptr;
+}
+
+strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
+	glui32 fmode, glui32 rock) {
+	return nullptr;
+}
+
+void Glk::glk_request_char_event_uni(winid_t win) {
+}
+
+void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf,
+	glui32 maxlen, glui32 initlen) {
+}
+
+#endif /* GLK_MODULE_UNICODE */
+
+#ifdef GLK_MODULE_UNICODE_NORM
+
+glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+	glui32 numchars) {
+	return 0;
+}
+
+glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	return 0;
+}
+
+#endif /* GLK_MODULE_UNICODE_NORM */
+
+#ifdef GLK_MODULE_IMAGE
+
+glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
+	return 0;
+}
+
+glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image,
+	glsi32 val1, glsi32 val2, glui32 width, glui32 height) {
+	return 0;
+}
+
+glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
+	return 0;
+}
+
+void Glk::glk_window_flow_break(winid_t win) {
+}
+
+void Glk::glk_window_erase_rect(winid_t win,
+	glsi32 left, glsi32 top, glui32 width, glui32 height) {
+}
+
+void Glk::glk_window_fill_rect(winid_t win, glui32 color,
+	glsi32 left, glsi32 top, glui32 width, glui32 height) {
+}
+
+void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
+}
+
+#endif /* GLK_MODULE_IMAGE */
+
+#ifdef GLK_MODULE_SOUND
+
+schanid_t Glk::glk_schannel_create(glui32 rock) {
+	return nullptr;
+}
+
+void Glk::glk_schannel_destroy(schanid_t chan) {
+}
+
+schanid_t Glk::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
+	return nullptr;
+}
+
+glui32 Glk::glk_schannel_get_rock(schanid_t chan) {
+	return 0;
+}
+
+glui32 Glk::glk_schannel_play(schanid_t chan, glui32 snd) {
+	return 0;
+}
+
+glui32 Glk::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
+	return 0;
+}
+
+void Glk::glk_schannel_stop(schanid_t chan) {
+}
+
+void Glk::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
+}
+
+void Glk::glk_sound_load_hint(glui32 snd, glui32 flag) {
+}
+
+#ifdef GLK_MODULE_SOUND2
+
+schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
+	return nullptr;
+}
+
+glui32 Glk::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+	glui32 *sndarray, glui32 soundcount, glui32 notify) {
+	return 0;
+}
+
+void Glk::glk_schannel_pause(schanid_t chan) {
+}
+
+void Glk::glk_schannel_unpause(schanid_t chan) {
+}
+
+void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+	glui32 duration, glui32 notify) {
+}
+
+#endif /* GLK_MODULE_SOUND2 */
+#endif /* GLK_MODULE_SOUND */
+
+#ifdef GLK_MODULE_HYPERLINKS
+
+void Glk::glk_set_hyperlink(glui32 linkval) {
+}
+
+void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
+}
+
+void Glk::glk_request_hyperlink_event(winid_t win) {
+}
+
+void Glk::glk_cancel_hyperlink_event(winid_t win) {
+}
+
+#endif /* GLK_MODULE_HYPERLINKS */
+
+#ifdef GLK_MODULE_DATETIME
+
+void Glk::glk_current_time(glktimeval_t *time) {
+}
+
+glsi32 Glk::glk_current_simple_time(glui32 factor) {
+	return 0;
+}
+
+void Glk::glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date) {
+}
+
+void Glk::glk_time_to_date_local(glktimeval_t *time, glkdate_t *date) {
+}
+
+void Glk::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
+}
+
+void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
+}
+
+void Glk::glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time) {
+}
+
+void Glk::glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) {
+}
+
+glsi32 Glk::glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor) {
+	return 0;
+}
+
+glsi32 Glk::glk_date_to_simple_time_local(glkdate_t *date, glui32 factor) {
+	return 0;
+}
+
+#endif /* GLK_MODULE_DATETIME */
+
+/* XXX non-official Glk functions that may or may not exist */
+
+char *garglk_fileref_get_name(frefid_t fref) {
+	return nullptr;
+}
+
+void Glk::garglk_set_program_name(const char *name) {
+}
+
+void Glk::garglk_set_program_info(const char *info) {
+}
+
+void Glk::garglk_set_story_name(const char *name) {
+}
+
+void Glk::garglk_set_story_title(const char *title) {
+}
+
+void Glk::garglk_set_config(const char *name) {
+}
+
+/* garglk_unput_string - removes the specified string from the end of the output buffer, if
+* indeed it is there. */
+void Glk::garglk_unput_string(char *str) {
+}
+
+void Glk::garglk_unput_string_uni(glui32 *str) {
+}
+
+void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
+}
+
+void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
+}
+
+void Glk::garglk_set_reversevideo(glui32 reverse) {
+}
+
+void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
+}
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 781b196..2e5637b 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -122,8 +122,16 @@ enum Keycode {
 	keycode_Func10   = 0xffffffe6U,
 	keycode_Func11   = 0xffffffe5U,
 	keycode_Func12   = 0xffffffe4U,
+
+	// non standard keycodes
+	keycode_Erase          = 0xffffef7fU,
+	keycode_MouseWheelUp   = 0xffffeffeU,
+	keycode_MouseWheelDown = 0xffffefffU,
+	keycode_SkipWordLeft   = 0xfffff000U,
+	keycode_SkipWordRight  = 0xfffff001U,
+
 	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
-	keycode_MAXVAL   = 28U,
+	keycode_MAXVAL = 28U
 };
 
 enum Style {
@@ -209,17 +217,53 @@ enum StyleHint {
 	stylehint_just_RightFlush = 3,
 };
 
-struct event_t {
+#ifdef GLK_MODULE_IMAGE
+
+enum ImageAlign {
+	imagealign_InlineUp     = 1,
+	imagealign_InlineDown   = 2,
+	imagealign_InlineCenter = 3,
+	imagealign_MarginLeft   = 4,
+	imagealign_MarginRight  = 5
+};
+
+#endif /* GLK_MODULE_IMAGE */
+
+struct event_struct {
 	glui32 type;
 	winid_t win;
 	glui32 val1, val2;
 };
+typedef event_struct *event_t;
 
-struct stream_result_t {
+struct stream_result_struct {
 	glui32 readcount;
 	glui32 writecount;
 };
+typedef stream_result_struct *stream_result_t;
 
+#ifdef GLK_MODULE_DATETIME
+
+struct glktimeval_struct {
+	glsi32 high_sec;
+	glui32 low_sec;
+	glsi32 microsec;
+};
+typedef glktimeval_struct *glktimeval_t;
+
+struct glkdate_struct {
+	glsi32 year;     /* full (four-digit) year */
+	glsi32 month;    /* 1-12, 1 is January */
+	glsi32 day;      /* 1-31 */
+	glsi32 weekday;  /* 0-6, 0 is Sunday */
+	glsi32 hour;     /* 0-23 */
+	glsi32 minute;   /* 0-59 */
+	glsi32 second;   /* 0-59, maybe 60 during a leap second */
+	glsi32 microsec; /* 0-999999 */
+};
+typedef glkdate_struct *glkdate_t;
+
+#endif /* GLK_MODULE_DATETIME */
 
 /**
  * Implements the GLK interface
@@ -232,6 +276,241 @@ public:
 	 * Constructor
 	 */
 	Glk() {}
+
+	void glk_exit(void);
+	void glk_set_interrupt_handler(void(*func)(void));
+	void glk_tick(void);
+
+	glui32 glk_gestalt(glui32 sel, glui32 val);
+	glui32 glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen);
+
+	unsigned char glk_char_to_lower(unsigned char ch);
+	unsigned char glk_char_to_upper(unsigned char ch);
+
+	winid_t glk_window_get_root(void);
+	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
+		glui32 wintype, glui32 rock);
+	void glk_window_close(winid_t win, stream_result_t *result);
+	void glk_window_get_size(winid_t win, glui32 *widthptr,
+		glui32 *heightptr);
+	void glk_window_set_arrangement(winid_t win, glui32 method,
+		glui32 size, winid_t keywin);
+	void glk_window_get_arrangement(winid_t win, glui32 *methodptr,
+		glui32 *sizeptr, winid_t *keywinptr);
+	winid_t glk_window_iterate(winid_t win, glui32 *rockptr);
+	glui32 glk_window_get_rock(winid_t win);
+	glui32 glk_window_get_type(winid_t win);
+	winid_t glk_window_get_parent(winid_t win);
+	winid_t glk_window_get_sibling(winid_t win);
+	void glk_window_clear(winid_t win);
+	void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);
+
+	strid_t glk_window_get_stream(winid_t win);
+	void glk_window_set_echo_stream(winid_t win, strid_t str);
+	strid_t glk_window_get_echo_stream(winid_t win);
+	void glk_set_window(winid_t win);
+
+	strid_t glk_stream_open_file(frefid_t fileref, glui32 fmode,
+		glui32 rock);
+	strid_t glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode,
+		glui32 rock);
+	void glk_stream_close(strid_t str, stream_result_t *result);
+	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr);
+	glui32 glk_stream_get_rock(strid_t str);
+	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode);
+	glui32 glk_stream_get_position(strid_t str);
+	void glk_stream_set_current(strid_t str);
+	strid_t glk_stream_get_current(void);
+
+	void glk_put_char(unsigned char ch);
+	void glk_put_char_stream(strid_t str, unsigned char ch);
+	void glk_put_string(char *s);
+	void glk_put_string_stream(strid_t str, char *s);
+	void glk_put_buffer(char *buf, glui32 len);
+	void glk_put_buffer_stream(strid_t str, char *buf, glui32 len);
+	void glk_set_style(glui32 styl);
+	void glk_set_style_stream(strid_t str, glui32 styl);
+
+	glsi32 glk_get_char_stream(strid_t str);
+	glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);
+	glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
+
+	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
+		glsi32 val);
+	void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint);
+	glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2);
+	glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint,
+		glui32 *result);
+
+	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
+	frefid_t glk_fileref_create_by_name(glui32 usage, char *name,
+		glui32 rock);
+	frefid_t glk_fileref_create_by_prompt(glui32 usage, glui32 fmode,
+		glui32 rock);
+	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref,
+		glui32 rock);
+	void glk_fileref_destroy(frefid_t fref);
+	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
+	glui32 glk_fileref_get_rock(frefid_t fref);
+	void glk_fileref_delete_file(frefid_t fref);
+	glui32 glk_fileref_does_file_exist(frefid_t fref);
+
+	void glk_select(event_t *event);
+	void glk_select_poll(event_t *event);
+
+	void glk_request_timer_events(glui32 millisecs);
+
+	void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
+		glui32 initlen);
+	void glk_request_char_event(winid_t win);
+	void glk_request_mouse_event(winid_t win);
+
+	void glk_cancel_line_event(winid_t win, event_t *event);
+	void glk_cancel_char_event(winid_t win);
+	void glk_cancel_mouse_event(winid_t win);
+
+#ifdef GLK_MODULE_LINE_ECHO
+	void glk_set_echo_line_event(winid_t win, glui32 val);
+#endif /* GLK_MODULE_LINE_ECHO */
+
+#ifdef GLK_MODULE_LINE_TERMINATORS
+	void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
+		glui32 count);
+#endif /* GLK_MODULE_LINE_TERMINATORS */
+
+#ifdef GLK_MODULE_UNICODE
+
+	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars, glui32 lowerrest);
+
+	void glk_put_char_uni(glui32 ch);
+	void glk_put_string_uni(glui32 *s);
+	void glk_put_buffer_uni(glui32 *buf, glui32 len);
+	void glk_put_char_stream_uni(strid_t str, glui32 ch);
+	void glk_put_string_stream_uni(strid_t str, glui32 *s);
+	void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+
+	glsi32 glk_get_char_stream_uni(strid_t str);
+	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
+
+	strid_t glk_stream_open_file_uni(frefid_t fileref, glui32 fmode,
+		glui32 rock);
+	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
+		glui32 fmode, glui32 rock);
+
+	void glk_request_char_event_uni(winid_t win);
+	void glk_request_line_event_uni(winid_t win, glui32 *buf,
+		glui32 maxlen, glui32 initlen);
+
+#endif /* GLK_MODULE_UNICODE */
+
+#ifdef GLK_MODULE_UNICODE_NORM
+
+	glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+
+#endif /* GLK_MODULE_UNICODE_NORM */
+
+#ifdef GLK_MODULE_IMAGE
+
+	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
+	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
+		glsi32 val1, glsi32 val2, glui32 width, glui32 height);
+	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
+
+	void glk_window_flow_break(winid_t win);
+
+	void glk_window_erase_rect(winid_t win,
+		glsi32 left, glsi32 top, glui32 width, glui32 height);
+	void glk_window_fill_rect(winid_t win, glui32 color,
+		glsi32 left, glsi32 top, glui32 width, glui32 height);
+	void glk_window_set_background_color(winid_t win, glui32 color);
+
+#endif /* GLK_MODULE_IMAGE */
+
+#ifdef GLK_MODULE_SOUND
+
+	schanid_t glk_schannel_create(glui32 rock);
+	void glk_schannel_destroy(schanid_t chan);
+	schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
+	glui32 glk_schannel_get_rock(schanid_t chan);
+
+	glui32 glk_schannel_play(schanid_t chan, glui32 snd);
+	glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
+		glui32 notify);
+	void glk_schannel_stop(schanid_t chan);
+	void glk_schannel_set_volume(schanid_t chan, glui32 vol);
+
+	void glk_sound_load_hint(glui32 snd, glui32 flag);
+
+#ifdef GLK_MODULE_SOUND2
+	/* Note that this section is nested inside the #ifdef GLK_MODULE_SOUND.
+	GLK_MODULE_SOUND must be defined if GLK_MODULE_SOUND2 is. */
+
+	schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
+	glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+		glui32 *sndarray, glui32 soundcount, glui32 notify);
+	void glk_schannel_pause(schanid_t chan);
+	void glk_schannel_unpause(schanid_t chan);
+	void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+		glui32 duration, glui32 notify);
+
+#endif /* GLK_MODULE_SOUND2 */
+#endif /* GLK_MODULE_SOUND */
+
+#ifdef GLK_MODULE_HYPERLINKS
+
+	void glk_set_hyperlink(glui32 linkval);
+	void glk_set_hyperlink_stream(strid_t str, glui32 linkval);
+	void glk_request_hyperlink_event(winid_t win);
+	void glk_cancel_hyperlink_event(winid_t win);
+
+#endif /* GLK_MODULE_HYPERLINKS */
+
+#ifdef GLK_MODULE_DATETIME
+
+	void glk_current_time(glktimeval_t *time);
+	glsi32 glk_current_simple_time(glui32 factor);
+	void glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date);
+	void glk_time_to_date_local(glktimeval_t *time, glkdate_t *date);
+	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor,
+		glkdate_t *date);
+	void glk_simple_time_to_date_local(glsi32 time, glui32 factor,
+		glkdate_t *date);
+	void glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time);
+	void glk_date_to_time_local(glkdate_t *date, glktimeval_t *time);
+	glsi32 glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor);
+	glsi32 glk_date_to_simple_time_local(glkdate_t *date, glui32 factor);
+
+#endif /* GLK_MODULE_DATETIME */
+
+	/* XXX non-official Glk functions that may or may not exist */
+	#define GARGLK 1
+
+	char* garglk_fileref_get_name(frefid_t fref);
+
+	void garglk_set_program_name(const char *name);
+	void garglk_set_program_info(const char *info);
+	void garglk_set_story_name(const char *name);
+	void garglk_set_story_title(const char *title);
+	void garglk_set_config(const char *name);
+
+	/* garglk_unput_string - removes the specified string from the end of the output buffer, if
+	* indeed it is there. */
+	void garglk_unput_string(char *str);
+	void garglk_unput_string_uni(glui32 *str);
+
+	void garglk_set_zcolors(glui32 fg, glui32 bg);
+	void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
+	void garglk_set_reversevideo(glui32 reverse);
+	void garglk_set_reversevideo_stream(strid_t str, glui32 reverse);
 };
 
 } // End of namespace Gargoyle


Commit: dcefdd6f620b07da6f70b01f784716b2e73adcf7
    https://github.com/scummvm/scummvm/commit/dcefdd6f620b07da6f70b01f784716b2e73adcf7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: More outer engine structure, and cleanup

Changed paths:
  A engines/gargoyle/glk/glk.cpp
  A engines/gargoyle/glk/glk.h
  A engines/gargoyle/glk/glk_types.h
  A engines/gargoyle/interps/scott/scott.cpp
  A engines/gargoyle/interps/scott/scott.h
  R engines/gargoyle/glk.cpp
  R engines/gargoyle/glk.h
  R engines/gargoyle/scott/scott.cpp
  R engines/gargoyle/scott/scott.h
    engines/gargoyle/detection.cpp
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/interpreter.h
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 94fb90f..f10a70b 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -37,6 +37,7 @@ namespace Gargoyle {
 
 struct GargoyleGameDescription {
 	ADGameDescription desc;
+	InterpreterType interpType;
 };
 
 uint32 GargoyleEngine::getFeatures() const {
@@ -51,6 +52,10 @@ Common::Language GargoyleEngine::getLanguage() const {
 	return _gameDescription->desc.language;
 }
 
+InterpreterType GargoyleEngine::getInterpreterType() const {
+	return _gameDescription->interpType;
+}
+
 } // End of namespace Gargoyle
 
 static const PlainGameDescriptor GargoyleGames[] = {
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 731e38d..b46f2fc 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -28,6 +28,7 @@
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/interps/scott/scott.h"
 
 namespace Gargoyle {
 
@@ -36,6 +37,7 @@ GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gam
 }
 
 GargoyleEngine::~GargoyleEngine() {
+	delete _interpreter;
 }
 
 void GargoyleEngine::initialize() {
@@ -46,10 +48,19 @@ void GargoyleEngine::initialize() {
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
 
 	initGraphics(640, 480, false);
+
+	switch (getInterpreterType()) {
+	case INTERPRETER_SCOTT:
+		_interpreter = new Scott::Scott();
+		break;
+	default:
+		error("Unknown interpreter type");
+	}
 }
 
 Common::Error GargoyleEngine::run() {
 	initialize();
+	_interpreter->execute();
 
 	return Common::kNoError;
 }
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index c43b5fe..375d9ad 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -28,13 +28,14 @@
 #include "common/serializer.h"
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
-#include "graphics/screen.h"
-#include "gargoyle/events.h"
-#include "gargoyle/glk.h"
-#include "gargoyle/scott/scott.h"
+#include "gargoyle/interpreter.h"
 
 namespace Gargoyle {
 
+enum InterpreterType {
+	INTERPRETER_SCOTT
+};
+
 enum GargoyleDebugChannels {
 	kDebugCore      = 1 << 0,
 	kDebugScripts	= 1 << 1,
@@ -42,6 +43,7 @@ enum GargoyleDebugChannels {
 	kDebugSound     = 1 << 3
 };
 
+
 #define GARGOYLE_SAVEGAME_VERSION 1
 
 struct GargoyleGameDescription;
@@ -64,8 +66,6 @@ private:
 private:
 	const GargoyleGameDescription *_gameDescription;
 	int _loadSaveSlot;
-	Graphics::Screen _screen;
-	Events _events;
 	Interpreter *_interpreter;
 
 	// Engine APIs
@@ -78,6 +78,7 @@ public:
 	uint32 getFeatures() const;
 	bool isDemo() const;
 	Common::Language getLanguage() const;
+	InterpreterType getInterpreterType() const;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
deleted file mode 100644
index 4ea4ce4..0000000
--- a/engines/gargoyle/glk.cpp
+++ /dev/null
@@ -1,533 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/glk.h"
-
-namespace Gargoyle {
-
-void Glk::glk_exit(void) {
-}
-
-void Glk::glk_set_interrupt_handler(void(*func)(void)) {
-}
-
-void Glk::glk_tick(void) {
-}
-
-glui32 Glk::glk_gestalt(glui32 sel, glui32 val) {
-	return 0;
-}
-
-glui32 Glk::glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) {
-	return 0;
-}
-
-unsigned char Glk::glk_char_to_lower(unsigned char ch) {
-	return '\0';
-}
-
-unsigned char Glk::glk_char_to_upper(unsigned char ch) {
-	return '\0';
-}
-
-winid_t Glk::glk_window_get_root(void) {
-	return nullptr;
-}
-
-winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size,
-	glui32 wintype, glui32 rock) {
-	return nullptr;
-}
-
-void Glk::glk_window_close(winid_t win, stream_result_t *result) {
-}
-
-void Glk::glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) {
-}
-
-void Glk::glk_window_set_arrangement(winid_t win, glui32 method,
-	glui32 size, winid_t keywin) {
-}
-
-void Glk::glk_window_get_arrangement(winid_t win, glui32 *methodptr,
-	glui32 *sizeptr, winid_t *keywinptr) {
-}
-
-winid_t Glk::glk_window_iterate(winid_t win, glui32 *rockptr) {
-	return nullptr;
-}
-
-glui32 Glk::glk_window_get_rock(winid_t win) {
-	return 0;
-}
-
-glui32 Glk::glk_window_get_type(winid_t win) {
-	return 0;
-}
-
-winid_t Glk::glk_window_get_parent(winid_t win) {
-	return nullptr;
-}
-
-winid_t Glk::glk_window_get_sibling(winid_t win) {
-	return nullptr;
-}
-
-void Glk::glk_window_clear(winid_t win) {
-}
-
-void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
-}
-
-strid_t Glk::glk_window_get_stream(winid_t win) {
-	return nullptr;
-}
-
-void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
-}
-
-strid_t Glk::glk_window_get_echo_stream(winid_t win) {
-	return nullptr;
-}
-
-void Glk::glk_set_window(winid_t win) {
-}
-
-strid_t Glk::glk_stream_open_file(frefid_t fileref, glui32 fmode,
-	glui32 rock) {
-	return nullptr;
-}
-
-strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) {
-	return nullptr;
-}
-
-void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
-}
-
-strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) {
-	return nullptr;
-}
-
-glui32 Glk::glk_stream_get_rock(strid_t str) {
-	return 0;
-}
-
-void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) {
-}
-
-glui32 Glk::glk_stream_get_position(strid_t str) {
-	return 0;
-}
-
-void Glk::glk_stream_set_current(strid_t str) {
-}
-
-strid_t Glk::glk_stream_get_current(void) {
-	return nullptr;
-}
-
-void Glk::glk_put_char(unsigned char ch) {
-}
-
-void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
-}
-
-void Glk::glk_put_string(char *s) {
-}
-
-void Glk::glk_put_string_stream(strid_t str, char *s) {
-}
-
-void Glk::glk_put_buffer(char *buf, glui32 len) {
-}
-
-void Glk::glk_put_buffer_stream(strid_t str, char *buf, glui32 len) {
-}
-
-void Glk::glk_set_style(glui32 styl) {
-}
-
-void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
-}
-
-glsi32 Glk::glk_get_char_stream(strid_t str) {
-	return 0;
-}
-
-glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
-	return 0;
-}
-
-glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
-	return 0;
-}
-
-void Glk::glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val) {
-}
-
-void Glk::glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint) {
-}
-
-glui32 Glk::glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) {
-	return 0;
-}
-
-glui32 Glk::glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result) {
-	return 0;
-}
-
-frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
-	return nullptr;
-}
-
-frefid_t Glk::glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock) {
-	return nullptr;
-}
-
-frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock) {
-	return nullptr;
-}
-
-frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
-	return nullptr;
-}
-
-void Glk::glk_fileref_destroy(frefid_t fref) {
-}
-
-frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
-	return nullptr;
-}
-
-glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
-	return 0;
-}
-
-void Glk::glk_fileref_delete_file(frefid_t fref) {
-}
-
-glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
-	return 0;
-}
-
-void Glk::glk_select(event_t *event) {
-}
-
-void Glk::glk_select_poll(event_t *event) {
-}
-
-void Glk::glk_request_timer_events(glui32 millisecs) {
-}
-
-void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
-}
-
-void Glk::glk_request_char_event(winid_t win) {
-}
-
-void Glk::glk_request_mouse_event(winid_t win) {
-}
-
-void Glk::glk_cancel_line_event(winid_t win, event_t *event) {
-}
-
-void Glk::glk_cancel_char_event(winid_t win) {
-}
-
-void Glk::glk_cancel_mouse_event(winid_t win) {
-}
-
-#ifdef GLK_MODULE_LINE_ECHO
-
-void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
-}
-
-#endif /* GLK_MODULE_LINE_ECHO */
-
-#ifdef GLK_MODULE_LINE_TERMINATORS
-
-void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
-}
-
-#endif /* GLK_MODULE_LINE_TERMINATORS */
-
-#ifdef GLK_MODULE_UNICODE
-
-glui32 Glk::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	return 0;
-}
-
-glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	return 0;
-}
-
-glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
-	glui32 numchars, glui32 lowerrest) {
-	return 0;
-}
-
-void Glk::glk_put_char_uni(glui32 ch) {
-}
-
-void Glk::glk_put_string_uni(glui32 *s) {
-}
-
-void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
-}
-
-void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
-}
-
-void Glk::glk_put_string_stream_uni(strid_t str, glui32 *s) {
-}
-
-void Glk::glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-}
-
-glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
-	return 0;
-}
-
-glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	return 0;
-}
-
-glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	return 0;
-}
-
-strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock) {
-	return nullptr;
-}
-
-strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
-	glui32 fmode, glui32 rock) {
-	return nullptr;
-}
-
-void Glk::glk_request_char_event_uni(winid_t win) {
-}
-
-void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf,
-	glui32 maxlen, glui32 initlen) {
-}
-
-#endif /* GLK_MODULE_UNICODE */
-
-#ifdef GLK_MODULE_UNICODE_NORM
-
-glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
-	glui32 numchars) {
-	return 0;
-}
-
-glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	return 0;
-}
-
-#endif /* GLK_MODULE_UNICODE_NORM */
-
-#ifdef GLK_MODULE_IMAGE
-
-glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
-	return 0;
-}
-
-glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image,
-	glsi32 val1, glsi32 val2, glui32 width, glui32 height) {
-	return 0;
-}
-
-glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
-	return 0;
-}
-
-void Glk::glk_window_flow_break(winid_t win) {
-}
-
-void Glk::glk_window_erase_rect(winid_t win,
-	glsi32 left, glsi32 top, glui32 width, glui32 height) {
-}
-
-void Glk::glk_window_fill_rect(winid_t win, glui32 color,
-	glsi32 left, glsi32 top, glui32 width, glui32 height) {
-}
-
-void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
-}
-
-#endif /* GLK_MODULE_IMAGE */
-
-#ifdef GLK_MODULE_SOUND
-
-schanid_t Glk::glk_schannel_create(glui32 rock) {
-	return nullptr;
-}
-
-void Glk::glk_schannel_destroy(schanid_t chan) {
-}
-
-schanid_t Glk::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
-	return nullptr;
-}
-
-glui32 Glk::glk_schannel_get_rock(schanid_t chan) {
-	return 0;
-}
-
-glui32 Glk::glk_schannel_play(schanid_t chan, glui32 snd) {
-	return 0;
-}
-
-glui32 Glk::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
-	return 0;
-}
-
-void Glk::glk_schannel_stop(schanid_t chan) {
-}
-
-void Glk::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
-}
-
-void Glk::glk_sound_load_hint(glui32 snd, glui32 flag) {
-}
-
-#ifdef GLK_MODULE_SOUND2
-
-schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
-	return nullptr;
-}
-
-glui32 Glk::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
-	glui32 *sndarray, glui32 soundcount, glui32 notify) {
-	return 0;
-}
-
-void Glk::glk_schannel_pause(schanid_t chan) {
-}
-
-void Glk::glk_schannel_unpause(schanid_t chan) {
-}
-
-void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
-	glui32 duration, glui32 notify) {
-}
-
-#endif /* GLK_MODULE_SOUND2 */
-#endif /* GLK_MODULE_SOUND */
-
-#ifdef GLK_MODULE_HYPERLINKS
-
-void Glk::glk_set_hyperlink(glui32 linkval) {
-}
-
-void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
-}
-
-void Glk::glk_request_hyperlink_event(winid_t win) {
-}
-
-void Glk::glk_cancel_hyperlink_event(winid_t win) {
-}
-
-#endif /* GLK_MODULE_HYPERLINKS */
-
-#ifdef GLK_MODULE_DATETIME
-
-void Glk::glk_current_time(glktimeval_t *time) {
-}
-
-glsi32 Glk::glk_current_simple_time(glui32 factor) {
-	return 0;
-}
-
-void Glk::glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date) {
-}
-
-void Glk::glk_time_to_date_local(glktimeval_t *time, glkdate_t *date) {
-}
-
-void Glk::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
-}
-
-void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
-}
-
-void Glk::glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time) {
-}
-
-void Glk::glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) {
-}
-
-glsi32 Glk::glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor) {
-	return 0;
-}
-
-glsi32 Glk::glk_date_to_simple_time_local(glkdate_t *date, glui32 factor) {
-	return 0;
-}
-
-#endif /* GLK_MODULE_DATETIME */
-
-/* XXX non-official Glk functions that may or may not exist */
-
-char *garglk_fileref_get_name(frefid_t fref) {
-	return nullptr;
-}
-
-void Glk::garglk_set_program_name(const char *name) {
-}
-
-void Glk::garglk_set_program_info(const char *info) {
-}
-
-void Glk::garglk_set_story_name(const char *name) {
-}
-
-void Glk::garglk_set_story_title(const char *title) {
-}
-
-void Glk::garglk_set_config(const char *name) {
-}
-
-/* garglk_unput_string - removes the specified string from the end of the output buffer, if
-* indeed it is there. */
-void Glk::garglk_unput_string(char *str) {
-}
-
-void Glk::garglk_unput_string_uni(glui32 *str) {
-}
-
-void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
-}
-
-void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
-}
-
-void Glk::garglk_set_reversevideo(glui32 reverse) {
-}
-
-void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
deleted file mode 100644
index 2e5637b..0000000
--- a/engines/gargoyle/glk.h
+++ /dev/null
@@ -1,518 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_GLK_H
-#define GARGOYLE_GLK_H
-
-#include "graphics/managed_surface.h"
-#include "gargoyle/interpreter.h"
-
-namespace Gargoyle {
-
-typedef uint32 glui32;
-typedef int32 glsi32;
-
-/**
- * These are the compile-time conditionals that reveal various Glk optional modules.
- */
-#define GLK_MODULE_LINE_ECHO
-#define GLK_MODULE_LINE_TERMINATORS
-#define GLK_MODULE_UNICODE
-#define GLK_MODULE_UNICODE_NORM
-#define GLK_MODULE_IMAGE
-#define GLK_MODULE_SOUND
-#define GLK_MODULE_SOUND2
-#define GLK_MODULE_HYPERLINKS
-#define GLK_MODULE_DATETIME
-#define GLK_MODULE_GARGLKTEXT
-
-/**
- * These types are opaque object identifiers. They're pointers to opaque
- * C structures, which are defined differently by each library.
- */
-typedef struct glk_window_struct  *winid_t;
-typedef struct glk_stream_struct  *strid_t;
-typedef struct glk_fileref_struct *frefid_t;
-typedef struct glk_schannel_struct *schanid_t;
-
-enum Gestalt {
-	gestalt_Version                = 0,
-	gestalt_CharInput              = 1,
-	gestalt_LineInput              = 2,
-	gestalt_CharOutput             = 3,
-	gestalt_CharOutput_CannotPrint = 0,
-	gestalt_CharOutput_ApproxPrint = 1,
-	gestalt_CharOutput_ExactPrint  = 2,
-	gestalt_MouseInput             = 4,
-	gestalt_Timer                  = 5,
-	gestalt_Graphics               = 6,
-	gestalt_DrawImage              = 7,
-	gestalt_Sound                  = 8,
-	gestalt_SoundVolume            = 9,
-	gestalt_SoundNotify            = 10,
-	gestalt_Hyperlinks             = 11,
-	gestalt_HyperlinkInput         = 12,
-	gestalt_SoundMusic             = 13,
-	gestalt_GraphicsTransparency   = 14,
-	gestalt_Unicode                = 15,
-	gestalt_UnicodeNorm            = 16,
-	gestalt_LineInputEcho          = 17,
-	gestalt_LineTerminators        = 18,
-	gestalt_LineTerminatorKey      = 19,
-	gestalt_DateTime               = 20,
-	gestalt_Sound2                 = 21,
-	gestalt_GarglkText             = 0x1100,
-};
-
-enum EvType {
-	evtype_None         = 0,
-	evtype_Timer        = 1,
-	evtype_CharInput    = 2,
-	evtype_LineInput    = 3,
-	evtype_MouseInput   = 4,
-	evtype_Arrange      = 5,
-	evtype_Redraw       = 6,
-	evtype_SoundNotify  = 7,
-	evtype_Hyperlink    = 8,
-	evtype_VolumeNotify = 9,
-};
-
-enum Keycode {
-	keycode_Unknown  = 0xffffffffU,
-	keycode_Left     = 0xfffffffeU,
-	keycode_Right    = 0xfffffffdU,
-	keycode_Up       = 0xfffffffcU,
-	keycode_Down     = 0xfffffffbU,
-	keycode_Return   = 0xfffffffaU,
-	keycode_Delete   = 0xfffffff9U,
-	keycode_Escape   = 0xfffffff8U,
-	keycode_Tab      = 0xfffffff7U,
-	keycode_PageUp   = 0xfffffff6U,
-	keycode_PageDown = 0xfffffff5U,
-	keycode_Home     = 0xfffffff4U,
-	keycode_End      = 0xfffffff3U,
-	keycode_Func1    = 0xffffffefU,
-	keycode_Func2    = 0xffffffeeU,
-	keycode_Func3    = 0xffffffedU,
-	keycode_Func4    = 0xffffffecU,
-	keycode_Func5    = 0xffffffebU,
-	keycode_Func6    = 0xffffffeaU,
-	keycode_Func7    = 0xffffffe9U,
-	keycode_Func8    = 0xffffffe8U,
-	keycode_Func9    = 0xffffffe7U,
-	keycode_Func10   = 0xffffffe6U,
-	keycode_Func11   = 0xffffffe5U,
-	keycode_Func12   = 0xffffffe4U,
-
-	// non standard keycodes
-	keycode_Erase          = 0xffffef7fU,
-	keycode_MouseWheelUp   = 0xffffeffeU,
-	keycode_MouseWheelDown = 0xffffefffU,
-	keycode_SkipWordLeft   = 0xfffff000U,
-	keycode_SkipWordRight  = 0xfffff001U,
-
-	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
-	keycode_MAXVAL = 28U
-};
-
-enum Style {
-	style_Normal       = 0,
-	style_Emphasized   = 1,
-	style_Preformatted = 2,
-	style_Header       = 3,
-	style_Subheader    = 4,
-	style_Alert        = 5,
-	style_Note         = 6,
-	style_BlockQuote   = 7,
-	style_Input        = 8,
-	style_User1        = 9,
-	style_User2        = 10,
-	style_NUMSTYLES    = 11,
-};
-
-enum WinType {
-	wintype_AllTypes   = 0,
-	wintype_Pair       = 1,
-	wintype_Blank      = 2,
-	wintype_TextBuffer = 3,
-	wintype_TextGrid   = 4,
-	wintype_Graphics   = 5,
-};
-
-enum WinMethod {
-	winmethod_Left    = 0x00,
-	winmethod_Right   = 0x01,
-	winmethod_Above   = 0x02,
-	winmethod_Below   = 0x03,
-	winmethod_DirMask = 0x0f,
-
-	winmethod_Fixed        = 0x10,
-	winmethod_Proportional = 0x20,
-	winmethod_DivisionMask = 0xf0,
-
-	winmethod_Border     = 0x000,
-	winmethod_NoBorder   = 0x100,
-	winmethod_BorderMask = 0x100,
-};
-
-enum FileUsage {
-	fileusage_Data        = 0x00,
-	fileusage_SavedGame   = 0x01,
-	fileusage_Transcript  = 0x02,
-	fileusage_InputRecord = 0x03,
-	fileusage_TypeMask    = 0x0f,
-
-	fileusage_TextMode    = 0x100,
-	fileusage_BinaryMode  = 0x000,
-};
-
-enum FileMode {
-	filemode_Write       = 0x01,
-	filemode_Read        = 0x02,
-	filemode_ReadWrite   = 0x03,
-	filemode_WriteAppend = 0x05,
-};
-
-enum SeekMode {
-	seekmode_Start   = 0,
-	seekmode_Current = 1,
-	seekmode_End     = 2,
-};
-
-enum StyleHint {
-	stylehint_Indentation     = 0,
-	stylehint_ParaIndentation = 1,
-	stylehint_Justification  = 2,
-	stylehint_Size            = 3,
-	stylehint_Weight          = 4,
-	stylehint_Oblique         = 5,
-	stylehint_Proportional    = 6,
-	stylehint_TextColor       = 7,
-	stylehint_BackColor       = 8,
-	stylehint_ReverseColor    = 9,
-	stylehint_NUMHINTS        = 10,
-
-	stylehint_just_LeftFlush  = 0,
-	stylehint_just_LeftRight  = 1,
-	stylehint_just_Centered   = 2,
-	stylehint_just_RightFlush = 3,
-};
-
-#ifdef GLK_MODULE_IMAGE
-
-enum ImageAlign {
-	imagealign_InlineUp     = 1,
-	imagealign_InlineDown   = 2,
-	imagealign_InlineCenter = 3,
-	imagealign_MarginLeft   = 4,
-	imagealign_MarginRight  = 5
-};
-
-#endif /* GLK_MODULE_IMAGE */
-
-struct event_struct {
-	glui32 type;
-	winid_t win;
-	glui32 val1, val2;
-};
-typedef event_struct *event_t;
-
-struct stream_result_struct {
-	glui32 readcount;
-	glui32 writecount;
-};
-typedef stream_result_struct *stream_result_t;
-
-#ifdef GLK_MODULE_DATETIME
-
-struct glktimeval_struct {
-	glsi32 high_sec;
-	glui32 low_sec;
-	glsi32 microsec;
-};
-typedef glktimeval_struct *glktimeval_t;
-
-struct glkdate_struct {
-	glsi32 year;     /* full (four-digit) year */
-	glsi32 month;    /* 1-12, 1 is January */
-	glsi32 day;      /* 1-31 */
-	glsi32 weekday;  /* 0-6, 0 is Sunday */
-	glsi32 hour;     /* 0-23 */
-	glsi32 minute;   /* 0-59 */
-	glsi32 second;   /* 0-59, maybe 60 during a leap second */
-	glsi32 microsec; /* 0-999999 */
-};
-typedef glkdate_struct *glkdate_t;
-
-#endif /* GLK_MODULE_DATETIME */
-
-/**
- * Implements the GLK interface
- */
-class Glk : public Interpreter {
-private:
-	Graphics::ManagedSurface *_surface;
-public:
-	/**
-	 * Constructor
-	 */
-	Glk() {}
-
-	void glk_exit(void);
-	void glk_set_interrupt_handler(void(*func)(void));
-	void glk_tick(void);
-
-	glui32 glk_gestalt(glui32 sel, glui32 val);
-	glui32 glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen);
-
-	unsigned char glk_char_to_lower(unsigned char ch);
-	unsigned char glk_char_to_upper(unsigned char ch);
-
-	winid_t glk_window_get_root(void);
-	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
-		glui32 wintype, glui32 rock);
-	void glk_window_close(winid_t win, stream_result_t *result);
-	void glk_window_get_size(winid_t win, glui32 *widthptr,
-		glui32 *heightptr);
-	void glk_window_set_arrangement(winid_t win, glui32 method,
-		glui32 size, winid_t keywin);
-	void glk_window_get_arrangement(winid_t win, glui32 *methodptr,
-		glui32 *sizeptr, winid_t *keywinptr);
-	winid_t glk_window_iterate(winid_t win, glui32 *rockptr);
-	glui32 glk_window_get_rock(winid_t win);
-	glui32 glk_window_get_type(winid_t win);
-	winid_t glk_window_get_parent(winid_t win);
-	winid_t glk_window_get_sibling(winid_t win);
-	void glk_window_clear(winid_t win);
-	void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);
-
-	strid_t glk_window_get_stream(winid_t win);
-	void glk_window_set_echo_stream(winid_t win, strid_t str);
-	strid_t glk_window_get_echo_stream(winid_t win);
-	void glk_set_window(winid_t win);
-
-	strid_t glk_stream_open_file(frefid_t fileref, glui32 fmode,
-		glui32 rock);
-	strid_t glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode,
-		glui32 rock);
-	void glk_stream_close(strid_t str, stream_result_t *result);
-	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr);
-	glui32 glk_stream_get_rock(strid_t str);
-	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode);
-	glui32 glk_stream_get_position(strid_t str);
-	void glk_stream_set_current(strid_t str);
-	strid_t glk_stream_get_current(void);
-
-	void glk_put_char(unsigned char ch);
-	void glk_put_char_stream(strid_t str, unsigned char ch);
-	void glk_put_string(char *s);
-	void glk_put_string_stream(strid_t str, char *s);
-	void glk_put_buffer(char *buf, glui32 len);
-	void glk_put_buffer_stream(strid_t str, char *buf, glui32 len);
-	void glk_set_style(glui32 styl);
-	void glk_set_style_stream(strid_t str, glui32 styl);
-
-	glsi32 glk_get_char_stream(strid_t str);
-	glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);
-	glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
-
-	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
-		glsi32 val);
-	void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint);
-	glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2);
-	glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint,
-		glui32 *result);
-
-	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
-	frefid_t glk_fileref_create_by_name(glui32 usage, char *name,
-		glui32 rock);
-	frefid_t glk_fileref_create_by_prompt(glui32 usage, glui32 fmode,
-		glui32 rock);
-	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref,
-		glui32 rock);
-	void glk_fileref_destroy(frefid_t fref);
-	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
-	glui32 glk_fileref_get_rock(frefid_t fref);
-	void glk_fileref_delete_file(frefid_t fref);
-	glui32 glk_fileref_does_file_exist(frefid_t fref);
-
-	void glk_select(event_t *event);
-	void glk_select_poll(event_t *event);
-
-	void glk_request_timer_events(glui32 millisecs);
-
-	void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
-		glui32 initlen);
-	void glk_request_char_event(winid_t win);
-	void glk_request_mouse_event(winid_t win);
-
-	void glk_cancel_line_event(winid_t win, event_t *event);
-	void glk_cancel_char_event(winid_t win);
-	void glk_cancel_mouse_event(winid_t win);
-
-#ifdef GLK_MODULE_LINE_ECHO
-	void glk_set_echo_line_event(winid_t win, glui32 val);
-#endif /* GLK_MODULE_LINE_ECHO */
-
-#ifdef GLK_MODULE_LINE_TERMINATORS
-	void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
-		glui32 count);
-#endif /* GLK_MODULE_LINE_TERMINATORS */
-
-#ifdef GLK_MODULE_UNICODE
-
-	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
-	glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
-	glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars, glui32 lowerrest);
-
-	void glk_put_char_uni(glui32 ch);
-	void glk_put_string_uni(glui32 *s);
-	void glk_put_buffer_uni(glui32 *buf, glui32 len);
-	void glk_put_char_stream_uni(strid_t str, glui32 ch);
-	void glk_put_string_stream_uni(strid_t str, glui32 *s);
-	void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
-
-	glsi32 glk_get_char_stream_uni(strid_t str);
-	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
-	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
-
-	strid_t glk_stream_open_file_uni(frefid_t fileref, glui32 fmode,
-		glui32 rock);
-	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
-		glui32 fmode, glui32 rock);
-
-	void glk_request_char_event_uni(winid_t win);
-	void glk_request_line_event_uni(winid_t win, glui32 *buf,
-		glui32 maxlen, glui32 initlen);
-
-#endif /* GLK_MODULE_UNICODE */
-
-#ifdef GLK_MODULE_UNICODE_NORM
-
-	glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
-	glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
-
-#endif /* GLK_MODULE_UNICODE_NORM */
-
-#ifdef GLK_MODULE_IMAGE
-
-	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
-	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
-		glsi32 val1, glsi32 val2, glui32 width, glui32 height);
-	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
-
-	void glk_window_flow_break(winid_t win);
-
-	void glk_window_erase_rect(winid_t win,
-		glsi32 left, glsi32 top, glui32 width, glui32 height);
-	void glk_window_fill_rect(winid_t win, glui32 color,
-		glsi32 left, glsi32 top, glui32 width, glui32 height);
-	void glk_window_set_background_color(winid_t win, glui32 color);
-
-#endif /* GLK_MODULE_IMAGE */
-
-#ifdef GLK_MODULE_SOUND
-
-	schanid_t glk_schannel_create(glui32 rock);
-	void glk_schannel_destroy(schanid_t chan);
-	schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
-	glui32 glk_schannel_get_rock(schanid_t chan);
-
-	glui32 glk_schannel_play(schanid_t chan, glui32 snd);
-	glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
-		glui32 notify);
-	void glk_schannel_stop(schanid_t chan);
-	void glk_schannel_set_volume(schanid_t chan, glui32 vol);
-
-	void glk_sound_load_hint(glui32 snd, glui32 flag);
-
-#ifdef GLK_MODULE_SOUND2
-	/* Note that this section is nested inside the #ifdef GLK_MODULE_SOUND.
-	GLK_MODULE_SOUND must be defined if GLK_MODULE_SOUND2 is. */
-
-	schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
-	glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
-		glui32 *sndarray, glui32 soundcount, glui32 notify);
-	void glk_schannel_pause(schanid_t chan);
-	void glk_schannel_unpause(schanid_t chan);
-	void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
-		glui32 duration, glui32 notify);
-
-#endif /* GLK_MODULE_SOUND2 */
-#endif /* GLK_MODULE_SOUND */
-
-#ifdef GLK_MODULE_HYPERLINKS
-
-	void glk_set_hyperlink(glui32 linkval);
-	void glk_set_hyperlink_stream(strid_t str, glui32 linkval);
-	void glk_request_hyperlink_event(winid_t win);
-	void glk_cancel_hyperlink_event(winid_t win);
-
-#endif /* GLK_MODULE_HYPERLINKS */
-
-#ifdef GLK_MODULE_DATETIME
-
-	void glk_current_time(glktimeval_t *time);
-	glsi32 glk_current_simple_time(glui32 factor);
-	void glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date);
-	void glk_time_to_date_local(glktimeval_t *time, glkdate_t *date);
-	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor,
-		glkdate_t *date);
-	void glk_simple_time_to_date_local(glsi32 time, glui32 factor,
-		glkdate_t *date);
-	void glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time);
-	void glk_date_to_time_local(glkdate_t *date, glktimeval_t *time);
-	glsi32 glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor);
-	glsi32 glk_date_to_simple_time_local(glkdate_t *date, glui32 factor);
-
-#endif /* GLK_MODULE_DATETIME */
-
-	/* XXX non-official Glk functions that may or may not exist */
-	#define GARGLK 1
-
-	char* garglk_fileref_get_name(frefid_t fref);
-
-	void garglk_set_program_name(const char *name);
-	void garglk_set_program_info(const char *info);
-	void garglk_set_story_name(const char *name);
-	void garglk_set_story_title(const char *title);
-	void garglk_set_config(const char *name);
-
-	/* garglk_unput_string - removes the specified string from the end of the output buffer, if
-	* indeed it is there. */
-	void garglk_unput_string(char *str);
-	void garglk_unput_string_uni(glui32 *str);
-
-	void garglk_set_zcolors(glui32 fg, glui32 bg);
-	void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
-	void garglk_set_reversevideo(glui32 reverse);
-	void garglk_set_reversevideo_stream(strid_t str, glui32 reverse);
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/glk/glk.cpp b/engines/gargoyle/glk/glk.cpp
new file mode 100644
index 0000000..26755a1
--- /dev/null
+++ b/engines/gargoyle/glk/glk.cpp
@@ -0,0 +1,689 @@
+/* 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 "gargoyle/glk/glk.h"
+
+namespace Gargoyle {
+
+Glk::Glk() : Interpreter(), _gliFirstEvent(false) {
+}
+
+void Glk::glk_exit(void) {
+	// TODO
+}
+
+void Glk::glk_set_interrupt_handler(void(*func)(void)) {
+	// This library doesn't handle interrupts.
+}
+
+void Glk::glk_tick(void) {
+	// TODO
+}
+
+glui32 Glk::glk_gestalt(glui32 sel, glui32 val) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) {
+	// TODO
+	return 0;
+}
+
+unsigned char Glk::glk_char_to_lower(unsigned char ch) {
+	// TODO
+	return '\0';
+}
+
+unsigned char Glk::glk_char_to_upper(unsigned char ch) {
+	// TODO
+	return '\0';
+}
+
+winid_t Glk::glk_window_get_root(void) {
+	// TODO
+	return nullptr;
+}
+
+winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size,
+	glui32 wintype, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_window_close(winid_t win, stream_result_t *result) {
+	// TODO
+}
+
+void Glk::glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) {
+	// TODO
+}
+
+void Glk::glk_window_set_arrangement(winid_t win, glui32 method,
+	glui32 size, winid_t keywin) {
+	// TODO
+}
+
+void Glk::glk_window_get_arrangement(winid_t win, glui32 *methodptr,
+	glui32 *sizeptr, winid_t *keywinptr) {
+	// TODO
+}
+
+winid_t Glk::glk_window_iterate(winid_t win, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_window_get_rock(winid_t win) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_window_get_type(winid_t win) {
+	// TODO
+	return 0;
+}
+
+winid_t Glk::glk_window_get_parent(winid_t win) {
+	// TODO
+	return nullptr;
+}
+
+winid_t Glk::glk_window_get_sibling(winid_t win) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_window_clear(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
+	// TODO
+}
+
+strid_t Glk::glk_window_get_stream(winid_t win) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
+	// TODO
+}
+
+strid_t Glk::glk_window_get_echo_stream(winid_t win) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_set_window(winid_t win) {
+	// TODO
+}
+
+strid_t Glk::glk_stream_open_file(frefid_t fileref, glui32 fmode,
+	glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
+	// TODO
+}
+
+strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_stream_get_rock(strid_t str) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) {
+	// TODO
+}
+
+glui32 Glk::glk_stream_get_position(strid_t str) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_stream_set_current(strid_t str) {
+	// TODO
+}
+
+strid_t Glk::glk_stream_get_current(void) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_put_char(unsigned char ch) {
+	// TODO
+}
+
+void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
+	// TODO
+}
+
+void Glk::glk_put_string(char *s) {
+	// TODO
+}
+
+void Glk::glk_put_string_stream(strid_t str, char *s) {
+	// TODO
+}
+
+void Glk::glk_put_buffer(char *buf, glui32 len) {
+	// TODO
+}
+
+void Glk::glk_put_buffer_stream(strid_t str, char *buf, glui32 len) {
+	// TODO
+}
+
+void Glk::glk_set_style(glui32 styl) {
+	// TODO
+}
+
+void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
+	// TODO
+}
+
+glsi32 Glk::glk_get_char_stream(strid_t str) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val) {
+	// TODO
+}
+
+void Glk::glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint) {
+	// TODO
+}
+
+glui32 Glk::glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result) {
+	// TODO
+	return 0;
+}
+
+frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_fileref_destroy(frefid_t fref) {
+	// TODO
+}
+
+frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_fileref_delete_file(frefid_t fref) {
+	// TODO
+}
+
+glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_select(event_t *event) {
+	if (!_gliFirstEvent) {
+		gliInputGuessFocus();
+		_gliFirstEvent = true;
+	}
+	
+	gliSelect(event, false);
+}
+
+void Glk::glk_select_poll(event_t *event) {
+	if (!_gliFirstEvent) {
+		gliInputGuessFocus();
+		_gliFirstEvent = true;
+	}
+
+	gliSelect(event, true);
+}
+
+void Glk::glk_request_timer_events(glui32 millisecs) {
+	// TODO
+}
+
+void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
+	// TODO
+}
+
+void Glk::glk_request_char_event(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_request_mouse_event(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_cancel_line_event(winid_t win, event_t *event) {
+	// TODO
+}
+
+void Glk::glk_cancel_char_event(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_cancel_mouse_event(winid_t win) {
+	// TODO
+}
+
+#ifdef GLK_MODULE_LINE_ECHO
+
+void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_LINE_ECHO */
+
+#ifdef GLK_MODULE_LINE_TERMINATORS
+
+void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_LINE_TERMINATORS */
+
+#ifdef GLK_MODULE_UNICODE
+
+glui32 Glk::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+	glui32 numchars, glui32 lowerrest) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_put_char_uni(glui32 ch) {
+	// TODO
+}
+
+void Glk::glk_put_string_uni(glui32 *s) {
+	// TODO
+}
+
+void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
+	// TODO
+}
+
+void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
+	// TODO
+}
+
+void Glk::glk_put_string_stream_uni(strid_t str, glui32 *s) {
+	// TODO
+}
+
+void Glk::glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	// TODO
+}
+
+glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	// TODO
+	return 0;
+}
+
+strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
+	glui32 fmode, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_request_char_event_uni(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf,
+	glui32 maxlen, glui32 initlen) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_UNICODE */
+
+#ifdef GLK_MODULE_UNICODE_NORM
+
+glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+	glui32 numchars) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	return 0;
+}
+
+#endif /* GLK_MODULE_UNICODE_NORM */
+
+#ifdef GLK_MODULE_IMAGE
+
+glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image,
+	glsi32 val1, glsi32 val2, glui32 width, glui32 height) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_window_flow_break(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_window_erase_rect(winid_t win,
+	glsi32 left, glsi32 top, glui32 width, glui32 height) {
+	// TODO
+}
+
+void Glk::glk_window_fill_rect(winid_t win, glui32 color,
+	glsi32 left, glsi32 top, glui32 width, glui32 height) {
+	// TODO
+}
+
+void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_IMAGE */
+
+#ifdef GLK_MODULE_SOUND
+
+schanid_t Glk::glk_schannel_create(glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_schannel_destroy(schanid_t chan) {
+	// TODO
+}
+
+schanid_t Glk::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_schannel_get_rock(schanid_t chan) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_schannel_play(schanid_t chan, glui32 snd) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_schannel_stop(schanid_t chan) {
+	// TODO
+}
+
+void Glk::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
+	// TODO
+}
+
+void Glk::glk_sound_load_hint(glui32 snd, glui32 flag) {
+	// TODO
+}
+
+#ifdef GLK_MODULE_SOUND2
+
+schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+	glui32 *sndarray, glui32 soundcount, glui32 notify) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_schannel_pause(schanid_t chan) {
+	// TODO
+}
+
+void Glk::glk_schannel_unpause(schanid_t chan) {
+	// TODO
+}
+
+void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+	glui32 duration, glui32 notify) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_SOUND2 */
+#endif /* GLK_MODULE_SOUND */
+
+#ifdef GLK_MODULE_HYPERLINKS
+
+void Glk::glk_set_hyperlink(glui32 linkval) {
+	// TODO
+}
+
+void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
+	// TODO
+}
+
+void Glk::glk_request_hyperlink_event(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_cancel_hyperlink_event(winid_t win) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_HYPERLINKS */
+
+#ifdef GLK_MODULE_DATETIME
+
+void Glk::glk_current_time(glktimeval_t *time) {
+	// TODO
+}
+
+glsi32 Glk::glk_current_simple_time(glui32 factor) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date) {
+	// TODO
+}
+
+void Glk::glk_time_to_date_local(glktimeval_t *time, glkdate_t *date) {
+	// TODO
+}
+
+void Glk::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
+	// TODO
+}
+
+void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
+	// TODO
+}
+
+void Glk::glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time) {
+	// TODO
+}
+
+void Glk::glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) {
+	// TODO
+}
+
+glsi32 Glk::glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor) {
+	// TODO
+	return 0;
+}
+
+glsi32 Glk::glk_date_to_simple_time_local(glkdate_t *date, glui32 factor) {
+	// TODO
+	return 0;
+}
+
+#endif /* GLK_MODULE_DATETIME */
+
+/* XXX non-official Glk functions that may or may not exist */
+
+char *garglk_fileref_get_name(frefid_t fref) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::garglk_set_program_name(const char *name) {
+	// TODO
+}
+
+void Glk::garglk_set_program_info(const char *info) {
+	// TODO
+}
+
+void Glk::garglk_set_story_name(const char *name) {
+	// TODO
+}
+
+void Glk::garglk_set_story_title(const char *title) {
+	// TODO
+}
+
+void Glk::garglk_set_config(const char *name) {
+	// TODO
+}
+
+/* garglk_unput_string - removes the specified string from the end of the output buffer, if
+* indeed it is there. */
+void Glk::garglk_unput_string(char *str) {
+	// TODO
+}
+
+void Glk::garglk_unput_string_uni(glui32 *str) {
+	// TODO
+}
+
+void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
+	// TODO
+}
+
+void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
+	// TODO
+}
+
+void Glk::garglk_set_reversevideo(glui32 reverse) {
+	// TODO
+}
+
+void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
+	// TODO
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Glk::gliInputGuessFocus() {
+	// TODO
+}
+
+void Glk::gliSelect(event_t *event, bool polled) {
+	// TODO
+	event->type = evtype_Quit;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk/glk.h b/engines/gargoyle/glk/glk.h
new file mode 100644
index 0000000..11c8cba
--- /dev/null
+++ b/engines/gargoyle/glk/glk.h
@@ -0,0 +1,289 @@
+/* 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 GARGOYLE_GLK_H
+#define GARGOYLE_GLK_H
+
+#include "graphics/managed_surface.h"
+#include "gargoyle/interpreter.h"
+#include "gargoyle/glk/glk_types.h"
+
+namespace Gargoyle {
+
+/**
+ * Implements the GLK interface
+ */
+class Glk : public Interpreter {
+private:
+	bool _gliFirstEvent;
+private:
+	/**
+	 * Pick first window which might want input. This is called after every keystroke.
+	 */
+	void gliInputGuessFocus();
+
+	void gliSelect(event_t *event, bool polled);
+public:
+	/**
+	 * Constructor
+	 */
+	Glk();
+
+	void glk_exit(void);
+	void glk_set_interrupt_handler(void(*func)(void));
+	void glk_tick(void);
+
+	glui32 glk_gestalt(glui32 sel, glui32 val);
+	glui32 glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen);
+
+	unsigned char glk_char_to_lower(unsigned char ch);
+	unsigned char glk_char_to_upper(unsigned char ch);
+
+	winid_t glk_window_get_root(void);
+	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
+		glui32 wintype, glui32 rock);
+	void glk_window_close(winid_t win, stream_result_t *result);
+	void glk_window_get_size(winid_t win, glui32 *widthptr,
+		glui32 *heightptr);
+	void glk_window_set_arrangement(winid_t win, glui32 method,
+		glui32 size, winid_t keywin);
+	void glk_window_get_arrangement(winid_t win, glui32 *methodptr,
+		glui32 *sizeptr, winid_t *keywinptr);
+	winid_t glk_window_iterate(winid_t win, glui32 *rockptr);
+	glui32 glk_window_get_rock(winid_t win);
+	glui32 glk_window_get_type(winid_t win);
+	winid_t glk_window_get_parent(winid_t win);
+	winid_t glk_window_get_sibling(winid_t win);
+	void glk_window_clear(winid_t win);
+	void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);
+
+	strid_t glk_window_get_stream(winid_t win);
+	void glk_window_set_echo_stream(winid_t win, strid_t str);
+	strid_t glk_window_get_echo_stream(winid_t win);
+	void glk_set_window(winid_t win);
+
+	strid_t glk_stream_open_file(frefid_t fileref, glui32 fmode,
+		glui32 rock);
+	strid_t glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode,
+		glui32 rock);
+	void glk_stream_close(strid_t str, stream_result_t *result);
+	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr);
+	glui32 glk_stream_get_rock(strid_t str);
+	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode);
+	glui32 glk_stream_get_position(strid_t str);
+	void glk_stream_set_current(strid_t str);
+	strid_t glk_stream_get_current(void);
+
+	void glk_put_char(unsigned char ch);
+	void glk_put_char_stream(strid_t str, unsigned char ch);
+	void glk_put_string(char *s);
+	void glk_put_string_stream(strid_t str, char *s);
+	void glk_put_buffer(char *buf, glui32 len);
+	void glk_put_buffer_stream(strid_t str, char *buf, glui32 len);
+	void glk_set_style(glui32 styl);
+	void glk_set_style_stream(strid_t str, glui32 styl);
+
+	glsi32 glk_get_char_stream(strid_t str);
+	glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);
+	glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
+
+	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
+		glsi32 val);
+	void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint);
+	glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2);
+	glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint,
+		glui32 *result);
+
+	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
+	frefid_t glk_fileref_create_by_name(glui32 usage, char *name,
+		glui32 rock);
+	frefid_t glk_fileref_create_by_prompt(glui32 usage, glui32 fmode,
+		glui32 rock);
+	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref,
+		glui32 rock);
+	void glk_fileref_destroy(frefid_t fref);
+	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
+	glui32 glk_fileref_get_rock(frefid_t fref);
+	void glk_fileref_delete_file(frefid_t fref);
+	glui32 glk_fileref_does_file_exist(frefid_t fref);
+
+	void glk_select(event_t *event);
+	void glk_select_poll(event_t *event);
+
+	void glk_request_timer_events(glui32 millisecs);
+
+	void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
+		glui32 initlen);
+	void glk_request_char_event(winid_t win);
+	void glk_request_mouse_event(winid_t win);
+
+	void glk_cancel_line_event(winid_t win, event_t *event);
+	void glk_cancel_char_event(winid_t win);
+	void glk_cancel_mouse_event(winid_t win);
+
+#ifdef GLK_MODULE_LINE_ECHO
+	void glk_set_echo_line_event(winid_t win, glui32 val);
+#endif /* GLK_MODULE_LINE_ECHO */
+
+#ifdef GLK_MODULE_LINE_TERMINATORS
+	void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
+		glui32 count);
+#endif /* GLK_MODULE_LINE_TERMINATORS */
+
+#ifdef GLK_MODULE_UNICODE
+
+	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars, glui32 lowerrest);
+
+	void glk_put_char_uni(glui32 ch);
+	void glk_put_string_uni(glui32 *s);
+	void glk_put_buffer_uni(glui32 *buf, glui32 len);
+	void glk_put_char_stream_uni(strid_t str, glui32 ch);
+	void glk_put_string_stream_uni(strid_t str, glui32 *s);
+	void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+
+	glsi32 glk_get_char_stream_uni(strid_t str);
+	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
+
+	strid_t glk_stream_open_file_uni(frefid_t fileref, glui32 fmode,
+		glui32 rock);
+	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
+		glui32 fmode, glui32 rock);
+
+	void glk_request_char_event_uni(winid_t win);
+	void glk_request_line_event_uni(winid_t win, glui32 *buf,
+		glui32 maxlen, glui32 initlen);
+
+#endif /* GLK_MODULE_UNICODE */
+
+#ifdef GLK_MODULE_UNICODE_NORM
+
+	glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+
+#endif /* GLK_MODULE_UNICODE_NORM */
+
+#ifdef GLK_MODULE_IMAGE
+
+	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
+	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
+		glsi32 val1, glsi32 val2, glui32 width, glui32 height);
+	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
+
+	void glk_window_flow_break(winid_t win);
+
+	void glk_window_erase_rect(winid_t win,
+		glsi32 left, glsi32 top, glui32 width, glui32 height);
+	void glk_window_fill_rect(winid_t win, glui32 color,
+		glsi32 left, glsi32 top, glui32 width, glui32 height);
+	void glk_window_set_background_color(winid_t win, glui32 color);
+
+#endif /* GLK_MODULE_IMAGE */
+
+#ifdef GLK_MODULE_SOUND
+
+	schanid_t glk_schannel_create(glui32 rock);
+	void glk_schannel_destroy(schanid_t chan);
+	schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
+	glui32 glk_schannel_get_rock(schanid_t chan);
+
+	glui32 glk_schannel_play(schanid_t chan, glui32 snd);
+	glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
+		glui32 notify);
+	void glk_schannel_stop(schanid_t chan);
+	void glk_schannel_set_volume(schanid_t chan, glui32 vol);
+
+	void glk_sound_load_hint(glui32 snd, glui32 flag);
+
+#ifdef GLK_MODULE_SOUND2
+	/* Note that this section is nested inside the #ifdef GLK_MODULE_SOUND.
+	GLK_MODULE_SOUND must be defined if GLK_MODULE_SOUND2 is. */
+
+	schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
+	glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+		glui32 *sndarray, glui32 soundcount, glui32 notify);
+	void glk_schannel_pause(schanid_t chan);
+	void glk_schannel_unpause(schanid_t chan);
+	void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+		glui32 duration, glui32 notify);
+
+#endif /* GLK_MODULE_SOUND2 */
+#endif /* GLK_MODULE_SOUND */
+
+#ifdef GLK_MODULE_HYPERLINKS
+
+	void glk_set_hyperlink(glui32 linkval);
+	void glk_set_hyperlink_stream(strid_t str, glui32 linkval);
+	void glk_request_hyperlink_event(winid_t win);
+	void glk_cancel_hyperlink_event(winid_t win);
+
+#endif /* GLK_MODULE_HYPERLINKS */
+
+#ifdef GLK_MODULE_DATETIME
+
+	void glk_current_time(glktimeval_t *time);
+	glsi32 glk_current_simple_time(glui32 factor);
+	void glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date);
+	void glk_time_to_date_local(glktimeval_t *time, glkdate_t *date);
+	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor,
+		glkdate_t *date);
+	void glk_simple_time_to_date_local(glsi32 time, glui32 factor,
+		glkdate_t *date);
+	void glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time);
+	void glk_date_to_time_local(glkdate_t *date, glktimeval_t *time);
+	glsi32 glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor);
+	glsi32 glk_date_to_simple_time_local(glkdate_t *date, glui32 factor);
+
+#endif /* GLK_MODULE_DATETIME */
+
+	/* XXX non-official Glk functions that may or may not exist */
+	#define GARGLK 1
+
+	char* garglk_fileref_get_name(frefid_t fref);
+
+	void garglk_set_program_name(const char *name);
+	void garglk_set_program_info(const char *info);
+	void garglk_set_story_name(const char *name);
+	void garglk_set_story_title(const char *title);
+	void garglk_set_config(const char *name);
+
+	/* garglk_unput_string - removes the specified string from the end of the output buffer, if
+	* indeed it is there. */
+	void garglk_unput_string(char *str);
+	void garglk_unput_string_uni(glui32 *str);
+
+	void garglk_set_zcolors(glui32 fg, glui32 bg);
+	void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
+	void garglk_set_reversevideo(glui32 reverse);
+	void garglk_set_reversevideo_stream(strid_t str, glui32 reverse);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/glk/glk_types.h b/engines/gargoyle/glk/glk_types.h
new file mode 100644
index 0000000..a822741
--- /dev/null
+++ b/engines/gargoyle/glk/glk_types.h
@@ -0,0 +1,272 @@
+/* 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 GARGOYLE_GLK_TYPES_H
+#define GARGOYLE_GLK_TYPES_H
+
+#include "common/scummsys.h"
+
+namespace Gargoyle {
+
+typedef uint32 glui32;
+typedef int32 glsi32;
+
+/**
+ * These are the compile-time conditionals that reveal various Glk optional modules.
+ */
+#define GLK_MODULE_LINE_ECHO
+#define GLK_MODULE_LINE_TERMINATORS
+#define GLK_MODULE_UNICODE
+#define GLK_MODULE_UNICODE_NORM
+#define GLK_MODULE_IMAGE
+#define GLK_MODULE_SOUND
+#define GLK_MODULE_SOUND2
+#define GLK_MODULE_HYPERLINKS
+#define GLK_MODULE_DATETIME
+#define GLK_MODULE_GARGLKTEXT
+
+/**
+ * These types are opaque object identifiers. They're pointers to opaque
+ * C structures, which are defined differently by each library.
+ */
+typedef struct glk_window_struct  *winid_t;
+typedef struct glk_stream_struct  *strid_t;
+typedef struct glk_fileref_struct *frefid_t;
+typedef struct glk_schannel_struct *schanid_t;
+
+enum Gestalt {
+	gestalt_Version                = 0,
+	gestalt_CharInput              = 1,
+	gestalt_LineInput              = 2,
+	gestalt_CharOutput             = 3,
+	gestalt_CharOutput_CannotPrint = 0,
+	gestalt_CharOutput_ApproxPrint = 1,
+	gestalt_CharOutput_ExactPrint  = 2,
+	gestalt_MouseInput             = 4,
+	gestalt_Timer                  = 5,
+	gestalt_Graphics               = 6,
+	gestalt_DrawImage              = 7,
+	gestalt_Sound                  = 8,
+	gestalt_SoundVolume            = 9,
+	gestalt_SoundNotify            = 10,
+	gestalt_Hyperlinks             = 11,
+	gestalt_HyperlinkInput         = 12,
+	gestalt_SoundMusic             = 13,
+	gestalt_GraphicsTransparency   = 14,
+	gestalt_Unicode                = 15,
+	gestalt_UnicodeNorm            = 16,
+	gestalt_LineInputEcho          = 17,
+	gestalt_LineTerminators        = 18,
+	gestalt_LineTerminatorKey      = 19,
+	gestalt_DateTime               = 20,
+	gestalt_Sound2                 = 21,
+	gestalt_GarglkText             = 0x1100,
+};
+
+enum EvType {
+	evtype_None         = 0,
+	evtype_Timer        = 1,
+	evtype_CharInput    = 2,
+	evtype_LineInput    = 3,
+	evtype_MouseInput   = 4,
+	evtype_Arrange      = 5,
+	evtype_Redraw       = 6,
+	evtype_SoundNotify  = 7,
+	evtype_Hyperlink    = 8,
+	evtype_VolumeNotify = 9,
+
+	// ScummVM custom events
+	evtype_Quit			= 99
+};
+
+enum Keycode {
+	keycode_Unknown  = 0xffffffffU,
+	keycode_Left     = 0xfffffffeU,
+	keycode_Right    = 0xfffffffdU,
+	keycode_Up       = 0xfffffffcU,
+	keycode_Down     = 0xfffffffbU,
+	keycode_Return   = 0xfffffffaU,
+	keycode_Delete   = 0xfffffff9U,
+	keycode_Escape   = 0xfffffff8U,
+	keycode_Tab      = 0xfffffff7U,
+	keycode_PageUp   = 0xfffffff6U,
+	keycode_PageDown = 0xfffffff5U,
+	keycode_Home     = 0xfffffff4U,
+	keycode_End      = 0xfffffff3U,
+	keycode_Func1    = 0xffffffefU,
+	keycode_Func2    = 0xffffffeeU,
+	keycode_Func3    = 0xffffffedU,
+	keycode_Func4    = 0xffffffecU,
+	keycode_Func5    = 0xffffffebU,
+	keycode_Func6    = 0xffffffeaU,
+	keycode_Func7    = 0xffffffe9U,
+	keycode_Func8    = 0xffffffe8U,
+	keycode_Func9    = 0xffffffe7U,
+	keycode_Func10   = 0xffffffe6U,
+	keycode_Func11   = 0xffffffe5U,
+	keycode_Func12   = 0xffffffe4U,
+
+	// non standard keycodes
+	keycode_Erase          = 0xffffef7fU,
+	keycode_MouseWheelUp   = 0xffffeffeU,
+	keycode_MouseWheelDown = 0xffffefffU,
+	keycode_SkipWordLeft   = 0xfffff000U,
+	keycode_SkipWordRight  = 0xfffff001U,
+
+	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
+	keycode_MAXVAL = 28U
+};
+
+enum Style {
+	style_Normal       = 0,
+	style_Emphasized   = 1,
+	style_Preformatted = 2,
+	style_Header       = 3,
+	style_Subheader    = 4,
+	style_Alert        = 5,
+	style_Note         = 6,
+	style_BlockQuote   = 7,
+	style_Input        = 8,
+	style_User1        = 9,
+	style_User2        = 10,
+	style_NUMSTYLES    = 11,
+};
+
+enum WinType {
+	wintype_AllTypes   = 0,
+	wintype_Pair       = 1,
+	wintype_Blank      = 2,
+	wintype_TextBuffer = 3,
+	wintype_TextGrid   = 4,
+	wintype_Graphics   = 5,
+};
+
+enum WinMethod {
+	winmethod_Left    = 0x00,
+	winmethod_Right   = 0x01,
+	winmethod_Above   = 0x02,
+	winmethod_Below   = 0x03,
+	winmethod_DirMask = 0x0f,
+
+	winmethod_Fixed        = 0x10,
+	winmethod_Proportional = 0x20,
+	winmethod_DivisionMask = 0xf0,
+
+	winmethod_Border     = 0x000,
+	winmethod_NoBorder   = 0x100,
+	winmethod_BorderMask = 0x100,
+};
+
+enum FileUsage {
+	fileusage_Data        = 0x00,
+	fileusage_SavedGame   = 0x01,
+	fileusage_Transcript  = 0x02,
+	fileusage_InputRecord = 0x03,
+	fileusage_TypeMask    = 0x0f,
+
+	fileusage_TextMode    = 0x100,
+	fileusage_BinaryMode  = 0x000,
+};
+
+enum FileMode {
+	filemode_Write       = 0x01,
+	filemode_Read        = 0x02,
+	filemode_ReadWrite   = 0x03,
+	filemode_WriteAppend = 0x05,
+};
+
+enum SeekMode {
+	seekmode_Start   = 0,
+	seekmode_Current = 1,
+	seekmode_End     = 2,
+};
+
+enum StyleHint {
+	stylehint_Indentation     = 0,
+	stylehint_ParaIndentation = 1,
+	stylehint_Justification  = 2,
+	stylehint_Size            = 3,
+	stylehint_Weight          = 4,
+	stylehint_Oblique         = 5,
+	stylehint_Proportional    = 6,
+	stylehint_TextColor       = 7,
+	stylehint_BackColor       = 8,
+	stylehint_ReverseColor    = 9,
+	stylehint_NUMHINTS        = 10,
+
+	stylehint_just_LeftFlush  = 0,
+	stylehint_just_LeftRight  = 1,
+	stylehint_just_Centered   = 2,
+	stylehint_just_RightFlush = 3,
+};
+
+#ifdef GLK_MODULE_IMAGE
+
+enum ImageAlign {
+	imagealign_InlineUp     = 1,
+	imagealign_InlineDown   = 2,
+	imagealign_InlineCenter = 3,
+	imagealign_MarginLeft   = 4,
+	imagealign_MarginRight  = 5
+};
+
+#endif /* GLK_MODULE_IMAGE */
+
+struct event_struct {
+	glui32 type;
+	winid_t win;
+	glui32 val1, val2;
+};
+typedef event_struct event_t;
+
+struct stream_result_struct {
+	glui32 readcount;
+	glui32 writecount;
+};
+typedef stream_result_struct stream_result_t;
+
+#ifdef GLK_MODULE_DATETIME
+
+struct glktimeval_struct {
+	glsi32 high_sec;
+	glui32 low_sec;
+	glsi32 microsec;
+};
+typedef glktimeval_struct glktimeval_t;
+
+struct glkdate_struct {
+	glsi32 year;     ///< full (four-digit) year */
+	glsi32 month;    ///< 1-12, 1 is January
+	glsi32 day;      ///< 1-31
+	glsi32 weekday;  ///< 0-6, 0 is Sunday
+	glsi32 hour;     ///< 0-23
+	glsi32 minute;   ///< 0-59
+	glsi32 second;   ///< 0-59, maybe 60 during a leap second
+	glsi32 microsec; ///< 0-999999
+};
+typedef glkdate_struct glkdate_t;
+
+#endif /* GLK_MODULE_DATETIME */
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/interpreter.h b/engines/gargoyle/interpreter.h
index df9172d..748d417 100644
--- a/engines/gargoyle/interpreter.h
+++ b/engines/gargoyle/interpreter.h
@@ -23,12 +23,16 @@
 #ifndef GARGOYLE_INTERPRETER_H
 #define GARGOYLE_INTERPRETER_H
 
+#include "graphics/screen.h"
+
 namespace Gargoyle {
 
 /**
  * Base class for specific interpreters
  */
 class Interpreter {
+protected:
+	Graphics::Screen _screen;
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/interps/scott/scott.cpp b/engines/gargoyle/interps/scott/scott.cpp
new file mode 100644
index 0000000..b2ea5e2
--- /dev/null
+++ b/engines/gargoyle/interps/scott/scott.cpp
@@ -0,0 +1,41 @@
+/* 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 "gargoyle/interps/scott/scott.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+void Scott::execute() {
+	event_t ev;
+	do {
+		glk_select(&ev);
+		switch (ev.type) {
+		default:
+			/* do nothing */
+			break;
+		}
+	} while (ev.type != evtype_Quit);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/interps/scott/scott.h b/engines/gargoyle/interps/scott/scott.h
new file mode 100644
index 0000000..7d87ca5
--- /dev/null
+++ b/engines/gargoyle/interps/scott/scott.h
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_SCOTT
+#define GARGOYLE_SCOTT
+
+/*
+ *	Controlling block
+ */
+
+#include "common/scummsys.h"
+#include "gargoyle/glk/glk.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+#define LIGHT_SOURCE	9	// Always 9 how odd
+#define CARRIED		255		// Carried
+#define DESTROYED	0		// Destroyed
+#define DARKBIT		15
+#define LIGHTOUTBIT	16		// Light gone out
+
+#define YOUARE		1		// You are not I am
+#define SCOTTLIGHT	2		// Authentic Scott Adams light messages
+#define DEBUGGING	4		// Info from database load
+#define TRS80_STYLE	8		// Display in style used on TRS-80
+#define PREHISTORIC_LAMP 16	// Destroy the lamp (very old databases)
+
+struct Header {
+ 	int Unknown;
+	int NumItems;
+	int NumActions;
+	int NumWords;			// Smaller of verb/noun is padded to same size
+	int NumRooms;
+	int MaxCarry;
+	int PlayerRoom;
+	int Treasures;
+	int WordLength;
+	int LightTime;
+	int NumMessages;
+	int TreasureRoom;
+};
+
+struct Action {
+	uint Vocab;
+	uint Condition[5];
+	uint action[2];
+};
+
+struct Room {
+	char *Text;
+	short Exits[6];
+};
+
+struct Item {
+	char *Text;		// PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
+	byte Location;
+	byte InitialLoc;
+	char *AutoGet;
+};
+
+struct Tail {
+	int Version;
+	int AdventureNumber;
+	int Unknown;
+};
+
+/**
+ * Scott Adams game interpreter
+ */
+class Scott : public Glk {
+public:
+	/**
+	 * Constructor
+	 */
+	Scott() : Glk() {}
+
+	/**
+	 * Execute the game
+	 */
+	virtual void execute();
+};
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 937b814..6e11ba7 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -4,9 +4,9 @@ MODULE_OBJS := \
 	detection.o \
 	events.o \
 	gargoyle.o \
-	glk.o \
+	glk/glk.o \
 	interpreter.o \
-	scott/scott.o
+	interps/scott/scott.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_GARGOYLE), DYNAMIC_PLUGIN)
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
deleted file mode 100644
index b5e537c..0000000
--- a/engines/gargoyle/scott/scott.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/scott/scott.h"
-
-namespace Gargoyle {
-namespace Scott {
-
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
deleted file mode 100644
index bf13cf8..0000000
--- a/engines/gargoyle/scott/scott.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_SCOTT
-#define GARGOYLE_SCOTT
-
-/*
- *	Controlling block
- */
-
-#include "common/scummsys.h"
-#include "gargoyle/glk.h"
-
-namespace Gargoyle {
-namespace Scott {
-
-#define LIGHT_SOURCE	9	// Always 9 how odd
-#define CARRIED		255		// Carried
-#define DESTROYED	0		// Destroyed
-#define DARKBIT		15
-#define LIGHTOUTBIT	16		// Light gone out
-
-#define YOUARE		1		// You are not I am
-#define SCOTTLIGHT	2		// Authentic Scott Adams light messages
-#define DEBUGGING	4		// Info from database load
-#define TRS80_STYLE	8		// Display in style used on TRS-80
-#define PREHISTORIC_LAMP 16	// Destroy the lamp (very old databases)
-
-struct Header {
- 	int Unknown;
-	int NumItems;
-	int NumActions;
-	int NumWords;			// Smaller of verb/noun is padded to same size
-	int NumRooms;
-	int MaxCarry;
-	int PlayerRoom;
-	int Treasures;
-	int WordLength;
-	int LightTime;
-	int NumMessages;
-	int TreasureRoom;
-};
-
-struct Action {
-	uint Vocab;
-	uint Condition[5];
-	uint action[2];
-};
-
-struct Room {
-	char *Text;
-	short Exits[6];
-};
-
-struct Item {
-	char *Text;		// PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
-	byte Location;
-	byte InitialLoc;
-	char *AutoGet;
-};
-
-struct Tail {
-	int Version;
-	int AdventureNumber;
-	int Unknown;
-};
-
-/**
- * Scott Adams game interpreter
- */
-class Scott : public Glk {
-public:
-	/**
-	 * Constructor
-	 */
-	Scott() : Glk() {}
-};
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
-
-#endif


Commit: b62e30b84c6dfa98942ca094f86ecc0538cebbec
    https://github.com/scummvm/scummvm/commit/b62e30b84c6dfa98942ca094f86ecc0538cebbec
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Interpreters now derive from GargoyleEngine via Glk

Changed paths:
  A engines/gargoyle/glk.cpp
  A engines/gargoyle/glk.h
  A engines/gargoyle/glk_types.h
  A engines/gargoyle/scott/scott.cpp
  A engines/gargoyle/scott/scott.h
  R engines/gargoyle/glk/glk.cpp
  R engines/gargoyle/glk/glk.h
  R engines/gargoyle/glk/glk_types.h
  R engines/gargoyle/interpreter.cpp
  R engines/gargoyle/interpreter.h
  R engines/gargoyle/interps/scott/scott.cpp
  R engines/gargoyle/interps/scott/scott.h
    engines/gargoyle/detection.cpp
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index f10a70b..93859e6 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -58,16 +58,17 @@ InterpreterType GargoyleEngine::getInterpreterType() const {
 
 } // End of namespace Gargoyle
 
-static const PlainGameDescriptor GargoyleGames[] = {
-	{"Gargoyle", "Gargoyle Games"},
+static const PlainGameDescriptor gargoyleGames[] = {
+	{"scott", "Scott Adams Games"},
 	{0, 0}
 };
 
 #include "gargoyle/detection_tables.h"
+#include "gargoyle/scott/scott.h"
 
 class GargoyleMetaEngine : public AdvancedMetaEngine {
 public:
-	GargoyleMetaEngine() : AdvancedMetaEngine(Gargoyle::gameDescriptions, sizeof(Gargoyle::GargoyleGameDescription), GargoyleGames) {
+	GargoyleMetaEngine() : AdvancedMetaEngine(Gargoyle::gameDescriptions, sizeof(Gargoyle::GargoyleGameDescription), gargoyleGames) {
 		_maxScanDepth = 3;
 	}
 
@@ -105,7 +106,13 @@ bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
 
 bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
 	const Gargoyle::GargoyleGameDescription *gd = (const Gargoyle::GargoyleGameDescription *)desc;
-	*engine = new Gargoyle::GargoyleEngine(syst, gd);
+	switch (gd->interpType) {
+	case Gargoyle::INTERPRETER_SCOTT:
+		*engine = new Gargoyle::Scott::Scott(syst, gd);
+		break;
+	default:
+		error("Unknown interpreter");
+	}
 
 	return gd != 0;
 }
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index b46f2fc..3457f5f 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -28,16 +28,14 @@
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
 #include "gargoyle/gargoyle.h"
-#include "gargoyle/interps/scott/scott.h"
 
 namespace Gargoyle {
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-	_gameDescription(gameDesc), Engine(syst), _interpreter(nullptr) {
+	_gameDescription(gameDesc), Engine(syst) {
 }
 
 GargoyleEngine::~GargoyleEngine() {
-	delete _interpreter;
 }
 
 void GargoyleEngine::initialize() {
@@ -48,19 +46,11 @@ void GargoyleEngine::initialize() {
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
 
 	initGraphics(640, 480, false);
-
-	switch (getInterpreterType()) {
-	case INTERPRETER_SCOTT:
-		_interpreter = new Scott::Scott();
-		break;
-	default:
-		error("Unknown interpreter type");
-	}
 }
 
 Common::Error GargoyleEngine::run() {
 	initialize();
-	_interpreter->execute();
+	main();
 
 	return Common::kNoError;
 }
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 375d9ad..0940248 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -28,7 +28,6 @@
 #include "common/serializer.h"
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
-#include "gargoyle/interpreter.h"
 
 namespace Gargoyle {
 
@@ -57,20 +56,27 @@ struct GargoyleSavegameHeader {
 	int _totalFrames;
 };
 
+/**
+ * Base class for the different interpreters
+ */
 class GargoyleEngine : public Engine {
 private:
 	/**
 	 * Handles basic initialization
 	 */
 	void initialize();
-private:
+protected:
 	const GargoyleGameDescription *_gameDescription;
 	int _loadSaveSlot;
-	Interpreter *_interpreter;
 
 	// Engine APIs
 	virtual Common::Error run();
 	virtual bool hasFeature(EngineFeature f) const;
+
+	/**
+	 * Main game loop for the individual interpreters
+	 */
+	virtual void main() = 0;
 public:
 	GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
 	virtual ~GargoyleEngine();
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
new file mode 100644
index 0000000..350b40f
--- /dev/null
+++ b/engines/gargoyle/glk.cpp
@@ -0,0 +1,690 @@
+/* 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 "gargoyle/glk.h"
+
+namespace Gargoyle {
+
+Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
+	GargoyleEngine(syst, gameDesc), _gliFirstEvent(false) {
+}
+
+void Glk::glk_exit(void) {
+	// TODO
+}
+
+void Glk::glk_set_interrupt_handler(void(*func)(void)) {
+	// This library doesn't handle interrupts.
+}
+
+void Glk::glk_tick(void) {
+	// TODO
+}
+
+glui32 Glk::glk_gestalt(glui32 sel, glui32 val) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) {
+	// TODO
+	return 0;
+}
+
+unsigned char Glk::glk_char_to_lower(unsigned char ch) {
+	// TODO
+	return '\0';
+}
+
+unsigned char Glk::glk_char_to_upper(unsigned char ch) {
+	// TODO
+	return '\0';
+}
+
+winid_t Glk::glk_window_get_root(void) {
+	// TODO
+	return nullptr;
+}
+
+winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size,
+	glui32 wintype, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_window_close(winid_t win, stream_result_t *result) {
+	// TODO
+}
+
+void Glk::glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) {
+	// TODO
+}
+
+void Glk::glk_window_set_arrangement(winid_t win, glui32 method,
+	glui32 size, winid_t keywin) {
+	// TODO
+}
+
+void Glk::glk_window_get_arrangement(winid_t win, glui32 *methodptr,
+	glui32 *sizeptr, winid_t *keywinptr) {
+	// TODO
+}
+
+winid_t Glk::glk_window_iterate(winid_t win, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_window_get_rock(winid_t win) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_window_get_type(winid_t win) {
+	// TODO
+	return 0;
+}
+
+winid_t Glk::glk_window_get_parent(winid_t win) {
+	// TODO
+	return nullptr;
+}
+
+winid_t Glk::glk_window_get_sibling(winid_t win) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_window_clear(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
+	// TODO
+}
+
+strid_t Glk::glk_window_get_stream(winid_t win) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
+	// TODO
+}
+
+strid_t Glk::glk_window_get_echo_stream(winid_t win) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_set_window(winid_t win) {
+	// TODO
+}
+
+strid_t Glk::glk_stream_open_file(frefid_t fileref, glui32 fmode,
+	glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
+	// TODO
+}
+
+strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_stream_get_rock(strid_t str) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) {
+	// TODO
+}
+
+glui32 Glk::glk_stream_get_position(strid_t str) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_stream_set_current(strid_t str) {
+	// TODO
+}
+
+strid_t Glk::glk_stream_get_current(void) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_put_char(unsigned char ch) {
+	// TODO
+}
+
+void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
+	// TODO
+}
+
+void Glk::glk_put_string(char *s) {
+	// TODO
+}
+
+void Glk::glk_put_string_stream(strid_t str, char *s) {
+	// TODO
+}
+
+void Glk::glk_put_buffer(char *buf, glui32 len) {
+	// TODO
+}
+
+void Glk::glk_put_buffer_stream(strid_t str, char *buf, glui32 len) {
+	// TODO
+}
+
+void Glk::glk_set_style(glui32 styl) {
+	// TODO
+}
+
+void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
+	// TODO
+}
+
+glsi32 Glk::glk_get_char_stream(strid_t str) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val) {
+	// TODO
+}
+
+void Glk::glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint) {
+	// TODO
+}
+
+glui32 Glk::glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result) {
+	// TODO
+	return 0;
+}
+
+frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_fileref_destroy(frefid_t fref) {
+	// TODO
+}
+
+frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_fileref_delete_file(frefid_t fref) {
+	// TODO
+}
+
+glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_select(event_t *event) {
+	if (!_gliFirstEvent) {
+		gliInputGuessFocus();
+		_gliFirstEvent = true;
+	}
+	
+	gliSelect(event, false);
+}
+
+void Glk::glk_select_poll(event_t *event) {
+	if (!_gliFirstEvent) {
+		gliInputGuessFocus();
+		_gliFirstEvent = true;
+	}
+
+	gliSelect(event, true);
+}
+
+void Glk::glk_request_timer_events(glui32 millisecs) {
+	// TODO
+}
+
+void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
+	// TODO
+}
+
+void Glk::glk_request_char_event(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_request_mouse_event(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_cancel_line_event(winid_t win, event_t *event) {
+	// TODO
+}
+
+void Glk::glk_cancel_char_event(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_cancel_mouse_event(winid_t win) {
+	// TODO
+}
+
+#ifdef GLK_MODULE_LINE_ECHO
+
+void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_LINE_ECHO */
+
+#ifdef GLK_MODULE_LINE_TERMINATORS
+
+void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_LINE_TERMINATORS */
+
+#ifdef GLK_MODULE_UNICODE
+
+glui32 Glk::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+	glui32 numchars, glui32 lowerrest) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_put_char_uni(glui32 ch) {
+	// TODO
+}
+
+void Glk::glk_put_string_uni(glui32 *s) {
+	// TODO
+}
+
+void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
+	// TODO
+}
+
+void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
+	// TODO
+}
+
+void Glk::glk_put_string_stream_uni(strid_t str, glui32 *s) {
+	// TODO
+}
+
+void Glk::glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	// TODO
+}
+
+glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	// TODO
+	return 0;
+}
+
+strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
+	glui32 fmode, glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_request_char_event_uni(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf,
+	glui32 maxlen, glui32 initlen) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_UNICODE */
+
+#ifdef GLK_MODULE_UNICODE_NORM
+
+glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+	glui32 numchars) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	return 0;
+}
+
+#endif /* GLK_MODULE_UNICODE_NORM */
+
+#ifdef GLK_MODULE_IMAGE
+
+glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image,
+	glsi32 val1, glsi32 val2, glui32 width, glui32 height) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_window_flow_break(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_window_erase_rect(winid_t win,
+	glsi32 left, glsi32 top, glui32 width, glui32 height) {
+	// TODO
+}
+
+void Glk::glk_window_fill_rect(winid_t win, glui32 color,
+	glsi32 left, glsi32 top, glui32 width, glui32 height) {
+	// TODO
+}
+
+void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_IMAGE */
+
+#ifdef GLK_MODULE_SOUND
+
+schanid_t Glk::glk_schannel_create(glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::glk_schannel_destroy(schanid_t chan) {
+	// TODO
+}
+
+schanid_t Glk::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_schannel_get_rock(schanid_t chan) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_schannel_play(schanid_t chan, glui32 snd) {
+	// TODO
+	return 0;
+}
+
+glui32 Glk::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_schannel_stop(schanid_t chan) {
+	// TODO
+}
+
+void Glk::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
+	// TODO
+}
+
+void Glk::glk_sound_load_hint(glui32 snd, glui32 flag) {
+	// TODO
+}
+
+#ifdef GLK_MODULE_SOUND2
+
+schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
+	// TODO
+	return nullptr;
+}
+
+glui32 Glk::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+	glui32 *sndarray, glui32 soundcount, glui32 notify) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_schannel_pause(schanid_t chan) {
+	// TODO
+}
+
+void Glk::glk_schannel_unpause(schanid_t chan) {
+	// TODO
+}
+
+void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+	glui32 duration, glui32 notify) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_SOUND2 */
+#endif /* GLK_MODULE_SOUND */
+
+#ifdef GLK_MODULE_HYPERLINKS
+
+void Glk::glk_set_hyperlink(glui32 linkval) {
+	// TODO
+}
+
+void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
+	// TODO
+}
+
+void Glk::glk_request_hyperlink_event(winid_t win) {
+	// TODO
+}
+
+void Glk::glk_cancel_hyperlink_event(winid_t win) {
+	// TODO
+}
+
+#endif /* GLK_MODULE_HYPERLINKS */
+
+#ifdef GLK_MODULE_DATETIME
+
+void Glk::glk_current_time(glktimeval_t *time) {
+	// TODO
+}
+
+glsi32 Glk::glk_current_simple_time(glui32 factor) {
+	// TODO
+	return 0;
+}
+
+void Glk::glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date) {
+	// TODO
+}
+
+void Glk::glk_time_to_date_local(glktimeval_t *time, glkdate_t *date) {
+	// TODO
+}
+
+void Glk::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
+	// TODO
+}
+
+void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
+	// TODO
+}
+
+void Glk::glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time) {
+	// TODO
+}
+
+void Glk::glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) {
+	// TODO
+}
+
+glsi32 Glk::glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor) {
+	// TODO
+	return 0;
+}
+
+glsi32 Glk::glk_date_to_simple_time_local(glkdate_t *date, glui32 factor) {
+	// TODO
+	return 0;
+}
+
+#endif /* GLK_MODULE_DATETIME */
+
+/* XXX non-official Glk functions that may or may not exist */
+
+char *garglk_fileref_get_name(frefid_t fref) {
+	// TODO
+	return nullptr;
+}
+
+void Glk::garglk_set_program_name(const char *name) {
+	// TODO
+}
+
+void Glk::garglk_set_program_info(const char *info) {
+	// TODO
+}
+
+void Glk::garglk_set_story_name(const char *name) {
+	// TODO
+}
+
+void Glk::garglk_set_story_title(const char *title) {
+	// TODO
+}
+
+void Glk::garglk_set_config(const char *name) {
+	// TODO
+}
+
+/* garglk_unput_string - removes the specified string from the end of the output buffer, if
+* indeed it is there. */
+void Glk::garglk_unput_string(char *str) {
+	// TODO
+}
+
+void Glk::garglk_unput_string_uni(glui32 *str) {
+	// TODO
+}
+
+void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
+	// TODO
+}
+
+void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
+	// TODO
+}
+
+void Glk::garglk_set_reversevideo(glui32 reverse) {
+	// TODO
+}
+
+void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
+	// TODO
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Glk::gliInputGuessFocus() {
+	// TODO
+}
+
+void Glk::gliSelect(event_t *event, bool polled) {
+	// TODO
+	event->type = evtype_Quit;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
new file mode 100644
index 0000000..eab6f0b
--- /dev/null
+++ b/engines/gargoyle/glk.h
@@ -0,0 +1,288 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_GLK_H
+#define GARGOYLE_GLK_H
+
+#include "gargoyle/gargoyle.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+/**
+ * Implements the GLK interface
+ */
+class Glk : public GargoyleEngine {
+private:
+	bool _gliFirstEvent;
+private:
+	/**
+	 * Pick first window which might want input. This is called after every keystroke.
+	 */
+	void gliInputGuessFocus();
+
+	void gliSelect(event_t *event, bool polled);
+public:
+	/**
+	 * Constructor
+	 */
+	Glk(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+	void glk_exit(void);
+	void glk_set_interrupt_handler(void(*func)(void));
+	void glk_tick(void);
+
+	glui32 glk_gestalt(glui32 sel, glui32 val);
+	glui32 glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen);
+
+	unsigned char glk_char_to_lower(unsigned char ch);
+	unsigned char glk_char_to_upper(unsigned char ch);
+
+	winid_t glk_window_get_root(void);
+	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
+		glui32 wintype, glui32 rock);
+	void glk_window_close(winid_t win, stream_result_t *result);
+	void glk_window_get_size(winid_t win, glui32 *widthptr,
+		glui32 *heightptr);
+	void glk_window_set_arrangement(winid_t win, glui32 method,
+		glui32 size, winid_t keywin);
+	void glk_window_get_arrangement(winid_t win, glui32 *methodptr,
+		glui32 *sizeptr, winid_t *keywinptr);
+	winid_t glk_window_iterate(winid_t win, glui32 *rockptr);
+	glui32 glk_window_get_rock(winid_t win);
+	glui32 glk_window_get_type(winid_t win);
+	winid_t glk_window_get_parent(winid_t win);
+	winid_t glk_window_get_sibling(winid_t win);
+	void glk_window_clear(winid_t win);
+	void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);
+
+	strid_t glk_window_get_stream(winid_t win);
+	void glk_window_set_echo_stream(winid_t win, strid_t str);
+	strid_t glk_window_get_echo_stream(winid_t win);
+	void glk_set_window(winid_t win);
+
+	strid_t glk_stream_open_file(frefid_t fileref, glui32 fmode,
+		glui32 rock);
+	strid_t glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode,
+		glui32 rock);
+	void glk_stream_close(strid_t str, stream_result_t *result);
+	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr);
+	glui32 glk_stream_get_rock(strid_t str);
+	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode);
+	glui32 glk_stream_get_position(strid_t str);
+	void glk_stream_set_current(strid_t str);
+	strid_t glk_stream_get_current(void);
+
+	void glk_put_char(unsigned char ch);
+	void glk_put_char_stream(strid_t str, unsigned char ch);
+	void glk_put_string(char *s);
+	void glk_put_string_stream(strid_t str, char *s);
+	void glk_put_buffer(char *buf, glui32 len);
+	void glk_put_buffer_stream(strid_t str, char *buf, glui32 len);
+	void glk_set_style(glui32 styl);
+	void glk_set_style_stream(strid_t str, glui32 styl);
+
+	glsi32 glk_get_char_stream(strid_t str);
+	glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);
+	glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
+
+	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
+		glsi32 val);
+	void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint);
+	glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2);
+	glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint,
+		glui32 *result);
+
+	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
+	frefid_t glk_fileref_create_by_name(glui32 usage, char *name,
+		glui32 rock);
+	frefid_t glk_fileref_create_by_prompt(glui32 usage, glui32 fmode,
+		glui32 rock);
+	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref,
+		glui32 rock);
+	void glk_fileref_destroy(frefid_t fref);
+	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
+	glui32 glk_fileref_get_rock(frefid_t fref);
+	void glk_fileref_delete_file(frefid_t fref);
+	glui32 glk_fileref_does_file_exist(frefid_t fref);
+
+	void glk_select(event_t *event);
+	void glk_select_poll(event_t *event);
+
+	void glk_request_timer_events(glui32 millisecs);
+
+	void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
+		glui32 initlen);
+	void glk_request_char_event(winid_t win);
+	void glk_request_mouse_event(winid_t win);
+
+	void glk_cancel_line_event(winid_t win, event_t *event);
+	void glk_cancel_char_event(winid_t win);
+	void glk_cancel_mouse_event(winid_t win);
+
+#ifdef GLK_MODULE_LINE_ECHO
+	void glk_set_echo_line_event(winid_t win, glui32 val);
+#endif /* GLK_MODULE_LINE_ECHO */
+
+#ifdef GLK_MODULE_LINE_TERMINATORS
+	void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
+		glui32 count);
+#endif /* GLK_MODULE_LINE_TERMINATORS */
+
+#ifdef GLK_MODULE_UNICODE
+
+	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+		glui32 numchars, glui32 lowerrest);
+
+	void glk_put_char_uni(glui32 ch);
+	void glk_put_string_uni(glui32 *s);
+	void glk_put_buffer_uni(glui32 *buf, glui32 len);
+	void glk_put_char_stream_uni(strid_t str, glui32 ch);
+	void glk_put_string_stream_uni(strid_t str, glui32 *s);
+	void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+
+	glsi32 glk_get_char_stream_uni(strid_t str);
+	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
+
+	strid_t glk_stream_open_file_uni(frefid_t fileref, glui32 fmode,
+		glui32 rock);
+	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
+		glui32 fmode, glui32 rock);
+
+	void glk_request_char_event_uni(winid_t win);
+	void glk_request_line_event_uni(winid_t win, glui32 *buf,
+		glui32 maxlen, glui32 initlen);
+
+#endif /* GLK_MODULE_UNICODE */
+
+#ifdef GLK_MODULE_UNICODE_NORM
+
+	glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+	glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
+		glui32 numchars);
+
+#endif /* GLK_MODULE_UNICODE_NORM */
+
+#ifdef GLK_MODULE_IMAGE
+
+	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
+	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
+		glsi32 val1, glsi32 val2, glui32 width, glui32 height);
+	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
+
+	void glk_window_flow_break(winid_t win);
+
+	void glk_window_erase_rect(winid_t win,
+		glsi32 left, glsi32 top, glui32 width, glui32 height);
+	void glk_window_fill_rect(winid_t win, glui32 color,
+		glsi32 left, glsi32 top, glui32 width, glui32 height);
+	void glk_window_set_background_color(winid_t win, glui32 color);
+
+#endif /* GLK_MODULE_IMAGE */
+
+#ifdef GLK_MODULE_SOUND
+
+	schanid_t glk_schannel_create(glui32 rock);
+	void glk_schannel_destroy(schanid_t chan);
+	schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
+	glui32 glk_schannel_get_rock(schanid_t chan);
+
+	glui32 glk_schannel_play(schanid_t chan, glui32 snd);
+	glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
+		glui32 notify);
+	void glk_schannel_stop(schanid_t chan);
+	void glk_schannel_set_volume(schanid_t chan, glui32 vol);
+
+	void glk_sound_load_hint(glui32 snd, glui32 flag);
+
+#ifdef GLK_MODULE_SOUND2
+	/* Note that this section is nested inside the #ifdef GLK_MODULE_SOUND.
+	GLK_MODULE_SOUND must be defined if GLK_MODULE_SOUND2 is. */
+
+	schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
+	glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+		glui32 *sndarray, glui32 soundcount, glui32 notify);
+	void glk_schannel_pause(schanid_t chan);
+	void glk_schannel_unpause(schanid_t chan);
+	void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+		glui32 duration, glui32 notify);
+
+#endif /* GLK_MODULE_SOUND2 */
+#endif /* GLK_MODULE_SOUND */
+
+#ifdef GLK_MODULE_HYPERLINKS
+
+	void glk_set_hyperlink(glui32 linkval);
+	void glk_set_hyperlink_stream(strid_t str, glui32 linkval);
+	void glk_request_hyperlink_event(winid_t win);
+	void glk_cancel_hyperlink_event(winid_t win);
+
+#endif /* GLK_MODULE_HYPERLINKS */
+
+#ifdef GLK_MODULE_DATETIME
+
+	void glk_current_time(glktimeval_t *time);
+	glsi32 glk_current_simple_time(glui32 factor);
+	void glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date);
+	void glk_time_to_date_local(glktimeval_t *time, glkdate_t *date);
+	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor,
+		glkdate_t *date);
+	void glk_simple_time_to_date_local(glsi32 time, glui32 factor,
+		glkdate_t *date);
+	void glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time);
+	void glk_date_to_time_local(glkdate_t *date, glktimeval_t *time);
+	glsi32 glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor);
+	glsi32 glk_date_to_simple_time_local(glkdate_t *date, glui32 factor);
+
+#endif /* GLK_MODULE_DATETIME */
+
+	/* XXX non-official Glk functions that may or may not exist */
+	#define GARGLK 1
+
+	char* garglk_fileref_get_name(frefid_t fref);
+
+	void garglk_set_program_name(const char *name);
+	void garglk_set_program_info(const char *info);
+	void garglk_set_story_name(const char *name);
+	void garglk_set_story_title(const char *title);
+	void garglk_set_config(const char *name);
+
+	/* garglk_unput_string - removes the specified string from the end of the output buffer, if
+	* indeed it is there. */
+	void garglk_unput_string(char *str);
+	void garglk_unput_string_uni(glui32 *str);
+
+	void garglk_set_zcolors(glui32 fg, glui32 bg);
+	void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
+	void garglk_set_reversevideo(glui32 reverse);
+	void garglk_set_reversevideo_stream(strid_t str, glui32 reverse);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/glk/glk.cpp b/engines/gargoyle/glk/glk.cpp
deleted file mode 100644
index 26755a1..0000000
--- a/engines/gargoyle/glk/glk.cpp
+++ /dev/null
@@ -1,689 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/glk/glk.h"
-
-namespace Gargoyle {
-
-Glk::Glk() : Interpreter(), _gliFirstEvent(false) {
-}
-
-void Glk::glk_exit(void) {
-	// TODO
-}
-
-void Glk::glk_set_interrupt_handler(void(*func)(void)) {
-	// This library doesn't handle interrupts.
-}
-
-void Glk::glk_tick(void) {
-	// TODO
-}
-
-glui32 Glk::glk_gestalt(glui32 sel, glui32 val) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) {
-	// TODO
-	return 0;
-}
-
-unsigned char Glk::glk_char_to_lower(unsigned char ch) {
-	// TODO
-	return '\0';
-}
-
-unsigned char Glk::glk_char_to_upper(unsigned char ch) {
-	// TODO
-	return '\0';
-}
-
-winid_t Glk::glk_window_get_root(void) {
-	// TODO
-	return nullptr;
-}
-
-winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size,
-	glui32 wintype, glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_window_close(winid_t win, stream_result_t *result) {
-	// TODO
-}
-
-void Glk::glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) {
-	// TODO
-}
-
-void Glk::glk_window_set_arrangement(winid_t win, glui32 method,
-	glui32 size, winid_t keywin) {
-	// TODO
-}
-
-void Glk::glk_window_get_arrangement(winid_t win, glui32 *methodptr,
-	glui32 *sizeptr, winid_t *keywinptr) {
-	// TODO
-}
-
-winid_t Glk::glk_window_iterate(winid_t win, glui32 *rockptr) {
-	// TODO
-	return nullptr;
-}
-
-glui32 Glk::glk_window_get_rock(winid_t win) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_window_get_type(winid_t win) {
-	// TODO
-	return 0;
-}
-
-winid_t Glk::glk_window_get_parent(winid_t win) {
-	// TODO
-	return nullptr;
-}
-
-winid_t Glk::glk_window_get_sibling(winid_t win) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_window_clear(winid_t win) {
-	// TODO
-}
-
-void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
-	// TODO
-}
-
-strid_t Glk::glk_window_get_stream(winid_t win) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
-	// TODO
-}
-
-strid_t Glk::glk_window_get_echo_stream(winid_t win) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_set_window(winid_t win) {
-	// TODO
-}
-
-strid_t Glk::glk_stream_open_file(frefid_t fileref, glui32 fmode,
-	glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
-	// TODO
-}
-
-strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) {
-	// TODO
-	return nullptr;
-}
-
-glui32 Glk::glk_stream_get_rock(strid_t str) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) {
-	// TODO
-}
-
-glui32 Glk::glk_stream_get_position(strid_t str) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_stream_set_current(strid_t str) {
-	// TODO
-}
-
-strid_t Glk::glk_stream_get_current(void) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_put_char(unsigned char ch) {
-	// TODO
-}
-
-void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
-	// TODO
-}
-
-void Glk::glk_put_string(char *s) {
-	// TODO
-}
-
-void Glk::glk_put_string_stream(strid_t str, char *s) {
-	// TODO
-}
-
-void Glk::glk_put_buffer(char *buf, glui32 len) {
-	// TODO
-}
-
-void Glk::glk_put_buffer_stream(strid_t str, char *buf, glui32 len) {
-	// TODO
-}
-
-void Glk::glk_set_style(glui32 styl) {
-	// TODO
-}
-
-void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
-	// TODO
-}
-
-glsi32 Glk::glk_get_char_stream(strid_t str) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val) {
-	// TODO
-}
-
-void Glk::glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint) {
-	// TODO
-}
-
-glui32 Glk::glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result) {
-	// TODO
-	return 0;
-}
-
-frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-frefid_t Glk::glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_fileref_destroy(frefid_t fref) {
-	// TODO
-}
-
-frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
-	// TODO
-	return nullptr;
-}
-
-glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_fileref_delete_file(frefid_t fref) {
-	// TODO
-}
-
-glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_select(event_t *event) {
-	if (!_gliFirstEvent) {
-		gliInputGuessFocus();
-		_gliFirstEvent = true;
-	}
-	
-	gliSelect(event, false);
-}
-
-void Glk::glk_select_poll(event_t *event) {
-	if (!_gliFirstEvent) {
-		gliInputGuessFocus();
-		_gliFirstEvent = true;
-	}
-
-	gliSelect(event, true);
-}
-
-void Glk::glk_request_timer_events(glui32 millisecs) {
-	// TODO
-}
-
-void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
-	// TODO
-}
-
-void Glk::glk_request_char_event(winid_t win) {
-	// TODO
-}
-
-void Glk::glk_request_mouse_event(winid_t win) {
-	// TODO
-}
-
-void Glk::glk_cancel_line_event(winid_t win, event_t *event) {
-	// TODO
-}
-
-void Glk::glk_cancel_char_event(winid_t win) {
-	// TODO
-}
-
-void Glk::glk_cancel_mouse_event(winid_t win) {
-	// TODO
-}
-
-#ifdef GLK_MODULE_LINE_ECHO
-
-void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
-	// TODO
-}
-
-#endif /* GLK_MODULE_LINE_ECHO */
-
-#ifdef GLK_MODULE_LINE_TERMINATORS
-
-void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
-	// TODO
-}
-
-#endif /* GLK_MODULE_LINE_TERMINATORS */
-
-#ifdef GLK_MODULE_UNICODE
-
-glui32 Glk::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
-	glui32 numchars, glui32 lowerrest) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_put_char_uni(glui32 ch) {
-	// TODO
-}
-
-void Glk::glk_put_string_uni(glui32 *s) {
-	// TODO
-}
-
-void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
-	// TODO
-}
-
-void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
-	// TODO
-}
-
-void Glk::glk_put_string_stream_uni(strid_t str, glui32 *s) {
-	// TODO
-}
-
-void Glk::glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	// TODO
-}
-
-glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	// TODO
-	return 0;
-}
-
-strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
-	glui32 fmode, glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_request_char_event_uni(winid_t win) {
-	// TODO
-}
-
-void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf,
-	glui32 maxlen, glui32 initlen) {
-	// TODO
-}
-
-#endif /* GLK_MODULE_UNICODE */
-
-#ifdef GLK_MODULE_UNICODE_NORM
-
-glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
-	glui32 numchars) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	return 0;
-}
-
-#endif /* GLK_MODULE_UNICODE_NORM */
-
-#ifdef GLK_MODULE_IMAGE
-
-glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image,
-	glsi32 val1, glsi32 val2, glui32 width, glui32 height) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_window_flow_break(winid_t win) {
-	// TODO
-}
-
-void Glk::glk_window_erase_rect(winid_t win,
-	glsi32 left, glsi32 top, glui32 width, glui32 height) {
-	// TODO
-}
-
-void Glk::glk_window_fill_rect(winid_t win, glui32 color,
-	glsi32 left, glsi32 top, glui32 width, glui32 height) {
-	// TODO
-}
-
-void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
-	// TODO
-}
-
-#endif /* GLK_MODULE_IMAGE */
-
-#ifdef GLK_MODULE_SOUND
-
-schanid_t Glk::glk_schannel_create(glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_schannel_destroy(schanid_t chan) {
-	// TODO
-}
-
-schanid_t Glk::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
-	// TODO
-	return nullptr;
-}
-
-glui32 Glk::glk_schannel_get_rock(schanid_t chan) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_schannel_play(schanid_t chan, glui32 snd) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_schannel_stop(schanid_t chan) {
-	// TODO
-}
-
-void Glk::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
-	// TODO
-}
-
-void Glk::glk_sound_load_hint(glui32 snd, glui32 flag) {
-	// TODO
-}
-
-#ifdef GLK_MODULE_SOUND2
-
-schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
-	// TODO
-	return nullptr;
-}
-
-glui32 Glk::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
-	glui32 *sndarray, glui32 soundcount, glui32 notify) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_schannel_pause(schanid_t chan) {
-	// TODO
-}
-
-void Glk::glk_schannel_unpause(schanid_t chan) {
-	// TODO
-}
-
-void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
-	glui32 duration, glui32 notify) {
-	// TODO
-}
-
-#endif /* GLK_MODULE_SOUND2 */
-#endif /* GLK_MODULE_SOUND */
-
-#ifdef GLK_MODULE_HYPERLINKS
-
-void Glk::glk_set_hyperlink(glui32 linkval) {
-	// TODO
-}
-
-void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
-	// TODO
-}
-
-void Glk::glk_request_hyperlink_event(winid_t win) {
-	// TODO
-}
-
-void Glk::glk_cancel_hyperlink_event(winid_t win) {
-	// TODO
-}
-
-#endif /* GLK_MODULE_HYPERLINKS */
-
-#ifdef GLK_MODULE_DATETIME
-
-void Glk::glk_current_time(glktimeval_t *time) {
-	// TODO
-}
-
-glsi32 Glk::glk_current_simple_time(glui32 factor) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date) {
-	// TODO
-}
-
-void Glk::glk_time_to_date_local(glktimeval_t *time, glkdate_t *date) {
-	// TODO
-}
-
-void Glk::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
-	// TODO
-}
-
-void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
-	// TODO
-}
-
-void Glk::glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time) {
-	// TODO
-}
-
-void Glk::glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) {
-	// TODO
-}
-
-glsi32 Glk::glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor) {
-	// TODO
-	return 0;
-}
-
-glsi32 Glk::glk_date_to_simple_time_local(glkdate_t *date, glui32 factor) {
-	// TODO
-	return 0;
-}
-
-#endif /* GLK_MODULE_DATETIME */
-
-/* XXX non-official Glk functions that may or may not exist */
-
-char *garglk_fileref_get_name(frefid_t fref) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::garglk_set_program_name(const char *name) {
-	// TODO
-}
-
-void Glk::garglk_set_program_info(const char *info) {
-	// TODO
-}
-
-void Glk::garglk_set_story_name(const char *name) {
-	// TODO
-}
-
-void Glk::garglk_set_story_title(const char *title) {
-	// TODO
-}
-
-void Glk::garglk_set_config(const char *name) {
-	// TODO
-}
-
-/* garglk_unput_string - removes the specified string from the end of the output buffer, if
-* indeed it is there. */
-void Glk::garglk_unput_string(char *str) {
-	// TODO
-}
-
-void Glk::garglk_unput_string_uni(glui32 *str) {
-	// TODO
-}
-
-void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
-	// TODO
-}
-
-void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
-	// TODO
-}
-
-void Glk::garglk_set_reversevideo(glui32 reverse) {
-	// TODO
-}
-
-void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
-	// TODO
-}
-
-/*--------------------------------------------------------------------------*/
-
-void Glk::gliInputGuessFocus() {
-	// TODO
-}
-
-void Glk::gliSelect(event_t *event, bool polled) {
-	// TODO
-	event->type = evtype_Quit;
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk/glk.h b/engines/gargoyle/glk/glk.h
deleted file mode 100644
index 11c8cba..0000000
--- a/engines/gargoyle/glk/glk.h
+++ /dev/null
@@ -1,289 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_GLK_H
-#define GARGOYLE_GLK_H
-
-#include "graphics/managed_surface.h"
-#include "gargoyle/interpreter.h"
-#include "gargoyle/glk/glk_types.h"
-
-namespace Gargoyle {
-
-/**
- * Implements the GLK interface
- */
-class Glk : public Interpreter {
-private:
-	bool _gliFirstEvent;
-private:
-	/**
-	 * Pick first window which might want input. This is called after every keystroke.
-	 */
-	void gliInputGuessFocus();
-
-	void gliSelect(event_t *event, bool polled);
-public:
-	/**
-	 * Constructor
-	 */
-	Glk();
-
-	void glk_exit(void);
-	void glk_set_interrupt_handler(void(*func)(void));
-	void glk_tick(void);
-
-	glui32 glk_gestalt(glui32 sel, glui32 val);
-	glui32 glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen);
-
-	unsigned char glk_char_to_lower(unsigned char ch);
-	unsigned char glk_char_to_upper(unsigned char ch);
-
-	winid_t glk_window_get_root(void);
-	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
-		glui32 wintype, glui32 rock);
-	void glk_window_close(winid_t win, stream_result_t *result);
-	void glk_window_get_size(winid_t win, glui32 *widthptr,
-		glui32 *heightptr);
-	void glk_window_set_arrangement(winid_t win, glui32 method,
-		glui32 size, winid_t keywin);
-	void glk_window_get_arrangement(winid_t win, glui32 *methodptr,
-		glui32 *sizeptr, winid_t *keywinptr);
-	winid_t glk_window_iterate(winid_t win, glui32 *rockptr);
-	glui32 glk_window_get_rock(winid_t win);
-	glui32 glk_window_get_type(winid_t win);
-	winid_t glk_window_get_parent(winid_t win);
-	winid_t glk_window_get_sibling(winid_t win);
-	void glk_window_clear(winid_t win);
-	void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);
-
-	strid_t glk_window_get_stream(winid_t win);
-	void glk_window_set_echo_stream(winid_t win, strid_t str);
-	strid_t glk_window_get_echo_stream(winid_t win);
-	void glk_set_window(winid_t win);
-
-	strid_t glk_stream_open_file(frefid_t fileref, glui32 fmode,
-		glui32 rock);
-	strid_t glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode,
-		glui32 rock);
-	void glk_stream_close(strid_t str, stream_result_t *result);
-	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr);
-	glui32 glk_stream_get_rock(strid_t str);
-	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode);
-	glui32 glk_stream_get_position(strid_t str);
-	void glk_stream_set_current(strid_t str);
-	strid_t glk_stream_get_current(void);
-
-	void glk_put_char(unsigned char ch);
-	void glk_put_char_stream(strid_t str, unsigned char ch);
-	void glk_put_string(char *s);
-	void glk_put_string_stream(strid_t str, char *s);
-	void glk_put_buffer(char *buf, glui32 len);
-	void glk_put_buffer_stream(strid_t str, char *buf, glui32 len);
-	void glk_set_style(glui32 styl);
-	void glk_set_style_stream(strid_t str, glui32 styl);
-
-	glsi32 glk_get_char_stream(strid_t str);
-	glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);
-	glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
-
-	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
-		glsi32 val);
-	void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint);
-	glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2);
-	glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint,
-		glui32 *result);
-
-	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
-	frefid_t glk_fileref_create_by_name(glui32 usage, char *name,
-		glui32 rock);
-	frefid_t glk_fileref_create_by_prompt(glui32 usage, glui32 fmode,
-		glui32 rock);
-	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref,
-		glui32 rock);
-	void glk_fileref_destroy(frefid_t fref);
-	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
-	glui32 glk_fileref_get_rock(frefid_t fref);
-	void glk_fileref_delete_file(frefid_t fref);
-	glui32 glk_fileref_does_file_exist(frefid_t fref);
-
-	void glk_select(event_t *event);
-	void glk_select_poll(event_t *event);
-
-	void glk_request_timer_events(glui32 millisecs);
-
-	void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
-		glui32 initlen);
-	void glk_request_char_event(winid_t win);
-	void glk_request_mouse_event(winid_t win);
-
-	void glk_cancel_line_event(winid_t win, event_t *event);
-	void glk_cancel_char_event(winid_t win);
-	void glk_cancel_mouse_event(winid_t win);
-
-#ifdef GLK_MODULE_LINE_ECHO
-	void glk_set_echo_line_event(winid_t win, glui32 val);
-#endif /* GLK_MODULE_LINE_ECHO */
-
-#ifdef GLK_MODULE_LINE_TERMINATORS
-	void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
-		glui32 count);
-#endif /* GLK_MODULE_LINE_TERMINATORS */
-
-#ifdef GLK_MODULE_UNICODE
-
-	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
-	glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
-	glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars, glui32 lowerrest);
-
-	void glk_put_char_uni(glui32 ch);
-	void glk_put_string_uni(glui32 *s);
-	void glk_put_buffer_uni(glui32 *buf, glui32 len);
-	void glk_put_char_stream_uni(strid_t str, glui32 ch);
-	void glk_put_string_stream_uni(strid_t str, glui32 *s);
-	void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
-
-	glsi32 glk_get_char_stream_uni(strid_t str);
-	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
-	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
-
-	strid_t glk_stream_open_file_uni(frefid_t fileref, glui32 fmode,
-		glui32 rock);
-	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
-		glui32 fmode, glui32 rock);
-
-	void glk_request_char_event_uni(winid_t win);
-	void glk_request_line_event_uni(winid_t win, glui32 *buf,
-		glui32 maxlen, glui32 initlen);
-
-#endif /* GLK_MODULE_UNICODE */
-
-#ifdef GLK_MODULE_UNICODE_NORM
-
-	glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
-	glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
-
-#endif /* GLK_MODULE_UNICODE_NORM */
-
-#ifdef GLK_MODULE_IMAGE
-
-	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
-	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
-		glsi32 val1, glsi32 val2, glui32 width, glui32 height);
-	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
-
-	void glk_window_flow_break(winid_t win);
-
-	void glk_window_erase_rect(winid_t win,
-		glsi32 left, glsi32 top, glui32 width, glui32 height);
-	void glk_window_fill_rect(winid_t win, glui32 color,
-		glsi32 left, glsi32 top, glui32 width, glui32 height);
-	void glk_window_set_background_color(winid_t win, glui32 color);
-
-#endif /* GLK_MODULE_IMAGE */
-
-#ifdef GLK_MODULE_SOUND
-
-	schanid_t glk_schannel_create(glui32 rock);
-	void glk_schannel_destroy(schanid_t chan);
-	schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
-	glui32 glk_schannel_get_rock(schanid_t chan);
-
-	glui32 glk_schannel_play(schanid_t chan, glui32 snd);
-	glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
-		glui32 notify);
-	void glk_schannel_stop(schanid_t chan);
-	void glk_schannel_set_volume(schanid_t chan, glui32 vol);
-
-	void glk_sound_load_hint(glui32 snd, glui32 flag);
-
-#ifdef GLK_MODULE_SOUND2
-	/* Note that this section is nested inside the #ifdef GLK_MODULE_SOUND.
-	GLK_MODULE_SOUND must be defined if GLK_MODULE_SOUND2 is. */
-
-	schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
-	glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
-		glui32 *sndarray, glui32 soundcount, glui32 notify);
-	void glk_schannel_pause(schanid_t chan);
-	void glk_schannel_unpause(schanid_t chan);
-	void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
-		glui32 duration, glui32 notify);
-
-#endif /* GLK_MODULE_SOUND2 */
-#endif /* GLK_MODULE_SOUND */
-
-#ifdef GLK_MODULE_HYPERLINKS
-
-	void glk_set_hyperlink(glui32 linkval);
-	void glk_set_hyperlink_stream(strid_t str, glui32 linkval);
-	void glk_request_hyperlink_event(winid_t win);
-	void glk_cancel_hyperlink_event(winid_t win);
-
-#endif /* GLK_MODULE_HYPERLINKS */
-
-#ifdef GLK_MODULE_DATETIME
-
-	void glk_current_time(glktimeval_t *time);
-	glsi32 glk_current_simple_time(glui32 factor);
-	void glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date);
-	void glk_time_to_date_local(glktimeval_t *time, glkdate_t *date);
-	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor,
-		glkdate_t *date);
-	void glk_simple_time_to_date_local(glsi32 time, glui32 factor,
-		glkdate_t *date);
-	void glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time);
-	void glk_date_to_time_local(glkdate_t *date, glktimeval_t *time);
-	glsi32 glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor);
-	glsi32 glk_date_to_simple_time_local(glkdate_t *date, glui32 factor);
-
-#endif /* GLK_MODULE_DATETIME */
-
-	/* XXX non-official Glk functions that may or may not exist */
-	#define GARGLK 1
-
-	char* garglk_fileref_get_name(frefid_t fref);
-
-	void garglk_set_program_name(const char *name);
-	void garglk_set_program_info(const char *info);
-	void garglk_set_story_name(const char *name);
-	void garglk_set_story_title(const char *title);
-	void garglk_set_config(const char *name);
-
-	/* garglk_unput_string - removes the specified string from the end of the output buffer, if
-	* indeed it is there. */
-	void garglk_unput_string(char *str);
-	void garglk_unput_string_uni(glui32 *str);
-
-	void garglk_set_zcolors(glui32 fg, glui32 bg);
-	void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
-	void garglk_set_reversevideo(glui32 reverse);
-	void garglk_set_reversevideo_stream(strid_t str, glui32 reverse);
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/glk/glk_types.h b/engines/gargoyle/glk/glk_types.h
deleted file mode 100644
index a822741..0000000
--- a/engines/gargoyle/glk/glk_types.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_GLK_TYPES_H
-#define GARGOYLE_GLK_TYPES_H
-
-#include "common/scummsys.h"
-
-namespace Gargoyle {
-
-typedef uint32 glui32;
-typedef int32 glsi32;
-
-/**
- * These are the compile-time conditionals that reveal various Glk optional modules.
- */
-#define GLK_MODULE_LINE_ECHO
-#define GLK_MODULE_LINE_TERMINATORS
-#define GLK_MODULE_UNICODE
-#define GLK_MODULE_UNICODE_NORM
-#define GLK_MODULE_IMAGE
-#define GLK_MODULE_SOUND
-#define GLK_MODULE_SOUND2
-#define GLK_MODULE_HYPERLINKS
-#define GLK_MODULE_DATETIME
-#define GLK_MODULE_GARGLKTEXT
-
-/**
- * These types are opaque object identifiers. They're pointers to opaque
- * C structures, which are defined differently by each library.
- */
-typedef struct glk_window_struct  *winid_t;
-typedef struct glk_stream_struct  *strid_t;
-typedef struct glk_fileref_struct *frefid_t;
-typedef struct glk_schannel_struct *schanid_t;
-
-enum Gestalt {
-	gestalt_Version                = 0,
-	gestalt_CharInput              = 1,
-	gestalt_LineInput              = 2,
-	gestalt_CharOutput             = 3,
-	gestalt_CharOutput_CannotPrint = 0,
-	gestalt_CharOutput_ApproxPrint = 1,
-	gestalt_CharOutput_ExactPrint  = 2,
-	gestalt_MouseInput             = 4,
-	gestalt_Timer                  = 5,
-	gestalt_Graphics               = 6,
-	gestalt_DrawImage              = 7,
-	gestalt_Sound                  = 8,
-	gestalt_SoundVolume            = 9,
-	gestalt_SoundNotify            = 10,
-	gestalt_Hyperlinks             = 11,
-	gestalt_HyperlinkInput         = 12,
-	gestalt_SoundMusic             = 13,
-	gestalt_GraphicsTransparency   = 14,
-	gestalt_Unicode                = 15,
-	gestalt_UnicodeNorm            = 16,
-	gestalt_LineInputEcho          = 17,
-	gestalt_LineTerminators        = 18,
-	gestalt_LineTerminatorKey      = 19,
-	gestalt_DateTime               = 20,
-	gestalt_Sound2                 = 21,
-	gestalt_GarglkText             = 0x1100,
-};
-
-enum EvType {
-	evtype_None         = 0,
-	evtype_Timer        = 1,
-	evtype_CharInput    = 2,
-	evtype_LineInput    = 3,
-	evtype_MouseInput   = 4,
-	evtype_Arrange      = 5,
-	evtype_Redraw       = 6,
-	evtype_SoundNotify  = 7,
-	evtype_Hyperlink    = 8,
-	evtype_VolumeNotify = 9,
-
-	// ScummVM custom events
-	evtype_Quit			= 99
-};
-
-enum Keycode {
-	keycode_Unknown  = 0xffffffffU,
-	keycode_Left     = 0xfffffffeU,
-	keycode_Right    = 0xfffffffdU,
-	keycode_Up       = 0xfffffffcU,
-	keycode_Down     = 0xfffffffbU,
-	keycode_Return   = 0xfffffffaU,
-	keycode_Delete   = 0xfffffff9U,
-	keycode_Escape   = 0xfffffff8U,
-	keycode_Tab      = 0xfffffff7U,
-	keycode_PageUp   = 0xfffffff6U,
-	keycode_PageDown = 0xfffffff5U,
-	keycode_Home     = 0xfffffff4U,
-	keycode_End      = 0xfffffff3U,
-	keycode_Func1    = 0xffffffefU,
-	keycode_Func2    = 0xffffffeeU,
-	keycode_Func3    = 0xffffffedU,
-	keycode_Func4    = 0xffffffecU,
-	keycode_Func5    = 0xffffffebU,
-	keycode_Func6    = 0xffffffeaU,
-	keycode_Func7    = 0xffffffe9U,
-	keycode_Func8    = 0xffffffe8U,
-	keycode_Func9    = 0xffffffe7U,
-	keycode_Func10   = 0xffffffe6U,
-	keycode_Func11   = 0xffffffe5U,
-	keycode_Func12   = 0xffffffe4U,
-
-	// non standard keycodes
-	keycode_Erase          = 0xffffef7fU,
-	keycode_MouseWheelUp   = 0xffffeffeU,
-	keycode_MouseWheelDown = 0xffffefffU,
-	keycode_SkipWordLeft   = 0xfffff000U,
-	keycode_SkipWordRight  = 0xfffff001U,
-
-	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
-	keycode_MAXVAL = 28U
-};
-
-enum Style {
-	style_Normal       = 0,
-	style_Emphasized   = 1,
-	style_Preformatted = 2,
-	style_Header       = 3,
-	style_Subheader    = 4,
-	style_Alert        = 5,
-	style_Note         = 6,
-	style_BlockQuote   = 7,
-	style_Input        = 8,
-	style_User1        = 9,
-	style_User2        = 10,
-	style_NUMSTYLES    = 11,
-};
-
-enum WinType {
-	wintype_AllTypes   = 0,
-	wintype_Pair       = 1,
-	wintype_Blank      = 2,
-	wintype_TextBuffer = 3,
-	wintype_TextGrid   = 4,
-	wintype_Graphics   = 5,
-};
-
-enum WinMethod {
-	winmethod_Left    = 0x00,
-	winmethod_Right   = 0x01,
-	winmethod_Above   = 0x02,
-	winmethod_Below   = 0x03,
-	winmethod_DirMask = 0x0f,
-
-	winmethod_Fixed        = 0x10,
-	winmethod_Proportional = 0x20,
-	winmethod_DivisionMask = 0xf0,
-
-	winmethod_Border     = 0x000,
-	winmethod_NoBorder   = 0x100,
-	winmethod_BorderMask = 0x100,
-};
-
-enum FileUsage {
-	fileusage_Data        = 0x00,
-	fileusage_SavedGame   = 0x01,
-	fileusage_Transcript  = 0x02,
-	fileusage_InputRecord = 0x03,
-	fileusage_TypeMask    = 0x0f,
-
-	fileusage_TextMode    = 0x100,
-	fileusage_BinaryMode  = 0x000,
-};
-
-enum FileMode {
-	filemode_Write       = 0x01,
-	filemode_Read        = 0x02,
-	filemode_ReadWrite   = 0x03,
-	filemode_WriteAppend = 0x05,
-};
-
-enum SeekMode {
-	seekmode_Start   = 0,
-	seekmode_Current = 1,
-	seekmode_End     = 2,
-};
-
-enum StyleHint {
-	stylehint_Indentation     = 0,
-	stylehint_ParaIndentation = 1,
-	stylehint_Justification  = 2,
-	stylehint_Size            = 3,
-	stylehint_Weight          = 4,
-	stylehint_Oblique         = 5,
-	stylehint_Proportional    = 6,
-	stylehint_TextColor       = 7,
-	stylehint_BackColor       = 8,
-	stylehint_ReverseColor    = 9,
-	stylehint_NUMHINTS        = 10,
-
-	stylehint_just_LeftFlush  = 0,
-	stylehint_just_LeftRight  = 1,
-	stylehint_just_Centered   = 2,
-	stylehint_just_RightFlush = 3,
-};
-
-#ifdef GLK_MODULE_IMAGE
-
-enum ImageAlign {
-	imagealign_InlineUp     = 1,
-	imagealign_InlineDown   = 2,
-	imagealign_InlineCenter = 3,
-	imagealign_MarginLeft   = 4,
-	imagealign_MarginRight  = 5
-};
-
-#endif /* GLK_MODULE_IMAGE */
-
-struct event_struct {
-	glui32 type;
-	winid_t win;
-	glui32 val1, val2;
-};
-typedef event_struct event_t;
-
-struct stream_result_struct {
-	glui32 readcount;
-	glui32 writecount;
-};
-typedef stream_result_struct stream_result_t;
-
-#ifdef GLK_MODULE_DATETIME
-
-struct glktimeval_struct {
-	glsi32 high_sec;
-	glui32 low_sec;
-	glsi32 microsec;
-};
-typedef glktimeval_struct glktimeval_t;
-
-struct glkdate_struct {
-	glsi32 year;     ///< full (four-digit) year */
-	glsi32 month;    ///< 1-12, 1 is January
-	glsi32 day;      ///< 1-31
-	glsi32 weekday;  ///< 0-6, 0 is Sunday
-	glsi32 hour;     ///< 0-23
-	glsi32 minute;   ///< 0-59
-	glsi32 second;   ///< 0-59, maybe 60 during a leap second
-	glsi32 microsec; ///< 0-999999
-};
-typedef glkdate_struct glkdate_t;
-
-#endif /* GLK_MODULE_DATETIME */
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
new file mode 100644
index 0000000..a822741
--- /dev/null
+++ b/engines/gargoyle/glk_types.h
@@ -0,0 +1,272 @@
+/* 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 GARGOYLE_GLK_TYPES_H
+#define GARGOYLE_GLK_TYPES_H
+
+#include "common/scummsys.h"
+
+namespace Gargoyle {
+
+typedef uint32 glui32;
+typedef int32 glsi32;
+
+/**
+ * These are the compile-time conditionals that reveal various Glk optional modules.
+ */
+#define GLK_MODULE_LINE_ECHO
+#define GLK_MODULE_LINE_TERMINATORS
+#define GLK_MODULE_UNICODE
+#define GLK_MODULE_UNICODE_NORM
+#define GLK_MODULE_IMAGE
+#define GLK_MODULE_SOUND
+#define GLK_MODULE_SOUND2
+#define GLK_MODULE_HYPERLINKS
+#define GLK_MODULE_DATETIME
+#define GLK_MODULE_GARGLKTEXT
+
+/**
+ * These types are opaque object identifiers. They're pointers to opaque
+ * C structures, which are defined differently by each library.
+ */
+typedef struct glk_window_struct  *winid_t;
+typedef struct glk_stream_struct  *strid_t;
+typedef struct glk_fileref_struct *frefid_t;
+typedef struct glk_schannel_struct *schanid_t;
+
+enum Gestalt {
+	gestalt_Version                = 0,
+	gestalt_CharInput              = 1,
+	gestalt_LineInput              = 2,
+	gestalt_CharOutput             = 3,
+	gestalt_CharOutput_CannotPrint = 0,
+	gestalt_CharOutput_ApproxPrint = 1,
+	gestalt_CharOutput_ExactPrint  = 2,
+	gestalt_MouseInput             = 4,
+	gestalt_Timer                  = 5,
+	gestalt_Graphics               = 6,
+	gestalt_DrawImage              = 7,
+	gestalt_Sound                  = 8,
+	gestalt_SoundVolume            = 9,
+	gestalt_SoundNotify            = 10,
+	gestalt_Hyperlinks             = 11,
+	gestalt_HyperlinkInput         = 12,
+	gestalt_SoundMusic             = 13,
+	gestalt_GraphicsTransparency   = 14,
+	gestalt_Unicode                = 15,
+	gestalt_UnicodeNorm            = 16,
+	gestalt_LineInputEcho          = 17,
+	gestalt_LineTerminators        = 18,
+	gestalt_LineTerminatorKey      = 19,
+	gestalt_DateTime               = 20,
+	gestalt_Sound2                 = 21,
+	gestalt_GarglkText             = 0x1100,
+};
+
+enum EvType {
+	evtype_None         = 0,
+	evtype_Timer        = 1,
+	evtype_CharInput    = 2,
+	evtype_LineInput    = 3,
+	evtype_MouseInput   = 4,
+	evtype_Arrange      = 5,
+	evtype_Redraw       = 6,
+	evtype_SoundNotify  = 7,
+	evtype_Hyperlink    = 8,
+	evtype_VolumeNotify = 9,
+
+	// ScummVM custom events
+	evtype_Quit			= 99
+};
+
+enum Keycode {
+	keycode_Unknown  = 0xffffffffU,
+	keycode_Left     = 0xfffffffeU,
+	keycode_Right    = 0xfffffffdU,
+	keycode_Up       = 0xfffffffcU,
+	keycode_Down     = 0xfffffffbU,
+	keycode_Return   = 0xfffffffaU,
+	keycode_Delete   = 0xfffffff9U,
+	keycode_Escape   = 0xfffffff8U,
+	keycode_Tab      = 0xfffffff7U,
+	keycode_PageUp   = 0xfffffff6U,
+	keycode_PageDown = 0xfffffff5U,
+	keycode_Home     = 0xfffffff4U,
+	keycode_End      = 0xfffffff3U,
+	keycode_Func1    = 0xffffffefU,
+	keycode_Func2    = 0xffffffeeU,
+	keycode_Func3    = 0xffffffedU,
+	keycode_Func4    = 0xffffffecU,
+	keycode_Func5    = 0xffffffebU,
+	keycode_Func6    = 0xffffffeaU,
+	keycode_Func7    = 0xffffffe9U,
+	keycode_Func8    = 0xffffffe8U,
+	keycode_Func9    = 0xffffffe7U,
+	keycode_Func10   = 0xffffffe6U,
+	keycode_Func11   = 0xffffffe5U,
+	keycode_Func12   = 0xffffffe4U,
+
+	// non standard keycodes
+	keycode_Erase          = 0xffffef7fU,
+	keycode_MouseWheelUp   = 0xffffeffeU,
+	keycode_MouseWheelDown = 0xffffefffU,
+	keycode_SkipWordLeft   = 0xfffff000U,
+	keycode_SkipWordRight  = 0xfffff001U,
+
+	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
+	keycode_MAXVAL = 28U
+};
+
+enum Style {
+	style_Normal       = 0,
+	style_Emphasized   = 1,
+	style_Preformatted = 2,
+	style_Header       = 3,
+	style_Subheader    = 4,
+	style_Alert        = 5,
+	style_Note         = 6,
+	style_BlockQuote   = 7,
+	style_Input        = 8,
+	style_User1        = 9,
+	style_User2        = 10,
+	style_NUMSTYLES    = 11,
+};
+
+enum WinType {
+	wintype_AllTypes   = 0,
+	wintype_Pair       = 1,
+	wintype_Blank      = 2,
+	wintype_TextBuffer = 3,
+	wintype_TextGrid   = 4,
+	wintype_Graphics   = 5,
+};
+
+enum WinMethod {
+	winmethod_Left    = 0x00,
+	winmethod_Right   = 0x01,
+	winmethod_Above   = 0x02,
+	winmethod_Below   = 0x03,
+	winmethod_DirMask = 0x0f,
+
+	winmethod_Fixed        = 0x10,
+	winmethod_Proportional = 0x20,
+	winmethod_DivisionMask = 0xf0,
+
+	winmethod_Border     = 0x000,
+	winmethod_NoBorder   = 0x100,
+	winmethod_BorderMask = 0x100,
+};
+
+enum FileUsage {
+	fileusage_Data        = 0x00,
+	fileusage_SavedGame   = 0x01,
+	fileusage_Transcript  = 0x02,
+	fileusage_InputRecord = 0x03,
+	fileusage_TypeMask    = 0x0f,
+
+	fileusage_TextMode    = 0x100,
+	fileusage_BinaryMode  = 0x000,
+};
+
+enum FileMode {
+	filemode_Write       = 0x01,
+	filemode_Read        = 0x02,
+	filemode_ReadWrite   = 0x03,
+	filemode_WriteAppend = 0x05,
+};
+
+enum SeekMode {
+	seekmode_Start   = 0,
+	seekmode_Current = 1,
+	seekmode_End     = 2,
+};
+
+enum StyleHint {
+	stylehint_Indentation     = 0,
+	stylehint_ParaIndentation = 1,
+	stylehint_Justification  = 2,
+	stylehint_Size            = 3,
+	stylehint_Weight          = 4,
+	stylehint_Oblique         = 5,
+	stylehint_Proportional    = 6,
+	stylehint_TextColor       = 7,
+	stylehint_BackColor       = 8,
+	stylehint_ReverseColor    = 9,
+	stylehint_NUMHINTS        = 10,
+
+	stylehint_just_LeftFlush  = 0,
+	stylehint_just_LeftRight  = 1,
+	stylehint_just_Centered   = 2,
+	stylehint_just_RightFlush = 3,
+};
+
+#ifdef GLK_MODULE_IMAGE
+
+enum ImageAlign {
+	imagealign_InlineUp     = 1,
+	imagealign_InlineDown   = 2,
+	imagealign_InlineCenter = 3,
+	imagealign_MarginLeft   = 4,
+	imagealign_MarginRight  = 5
+};
+
+#endif /* GLK_MODULE_IMAGE */
+
+struct event_struct {
+	glui32 type;
+	winid_t win;
+	glui32 val1, val2;
+};
+typedef event_struct event_t;
+
+struct stream_result_struct {
+	glui32 readcount;
+	glui32 writecount;
+};
+typedef stream_result_struct stream_result_t;
+
+#ifdef GLK_MODULE_DATETIME
+
+struct glktimeval_struct {
+	glsi32 high_sec;
+	glui32 low_sec;
+	glsi32 microsec;
+};
+typedef glktimeval_struct glktimeval_t;
+
+struct glkdate_struct {
+	glsi32 year;     ///< full (four-digit) year */
+	glsi32 month;    ///< 1-12, 1 is January
+	glsi32 day;      ///< 1-31
+	glsi32 weekday;  ///< 0-6, 0 is Sunday
+	glsi32 hour;     ///< 0-23
+	glsi32 minute;   ///< 0-59
+	glsi32 second;   ///< 0-59, maybe 60 during a leap second
+	glsi32 microsec; ///< 0-999999
+};
+typedef glkdate_struct glkdate_t;
+
+#endif /* GLK_MODULE_DATETIME */
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/interpreter.cpp b/engines/gargoyle/interpreter.cpp
deleted file mode 100644
index 10cefd7..0000000
--- a/engines/gargoyle/interpreter.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/interpreter.h"
-
-namespace Gargoyle {
-
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/interpreter.h b/engines/gargoyle/interpreter.h
deleted file mode 100644
index 748d417..0000000
--- a/engines/gargoyle/interpreter.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_INTERPRETER_H
-#define GARGOYLE_INTERPRETER_H
-
-#include "graphics/screen.h"
-
-namespace Gargoyle {
-
-/**
- * Base class for specific interpreters
- */
-class Interpreter {
-protected:
-	Graphics::Screen _screen;
-public:
-	/**
-	 * Constructor
-	 */
-	Interpreter() {}
-
-	/**
-	 * Destructor
-	 */
-	virtual ~Interpreter() {}
-
-	/**
-	 * Main execution method
-	 */
-	virtual void execute() = 0;
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/interps/scott/scott.cpp b/engines/gargoyle/interps/scott/scott.cpp
deleted file mode 100644
index b2ea5e2..0000000
--- a/engines/gargoyle/interps/scott/scott.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/interps/scott/scott.h"
-
-namespace Gargoyle {
-namespace Scott {
-
-void Scott::execute() {
-	event_t ev;
-	do {
-		glk_select(&ev);
-		switch (ev.type) {
-		default:
-			/* do nothing */
-			break;
-		}
-	} while (ev.type != evtype_Quit);
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/interps/scott/scott.h b/engines/gargoyle/interps/scott/scott.h
deleted file mode 100644
index 7d87ca5..0000000
--- a/engines/gargoyle/interps/scott/scott.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_SCOTT
-#define GARGOYLE_SCOTT
-
-/*
- *	Controlling block
- */
-
-#include "common/scummsys.h"
-#include "gargoyle/glk/glk.h"
-
-namespace Gargoyle {
-namespace Scott {
-
-#define LIGHT_SOURCE	9	// Always 9 how odd
-#define CARRIED		255		// Carried
-#define DESTROYED	0		// Destroyed
-#define DARKBIT		15
-#define LIGHTOUTBIT	16		// Light gone out
-
-#define YOUARE		1		// You are not I am
-#define SCOTTLIGHT	2		// Authentic Scott Adams light messages
-#define DEBUGGING	4		// Info from database load
-#define TRS80_STYLE	8		// Display in style used on TRS-80
-#define PREHISTORIC_LAMP 16	// Destroy the lamp (very old databases)
-
-struct Header {
- 	int Unknown;
-	int NumItems;
-	int NumActions;
-	int NumWords;			// Smaller of verb/noun is padded to same size
-	int NumRooms;
-	int MaxCarry;
-	int PlayerRoom;
-	int Treasures;
-	int WordLength;
-	int LightTime;
-	int NumMessages;
-	int TreasureRoom;
-};
-
-struct Action {
-	uint Vocab;
-	uint Condition[5];
-	uint action[2];
-};
-
-struct Room {
-	char *Text;
-	short Exits[6];
-};
-
-struct Item {
-	char *Text;		// PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
-	byte Location;
-	byte InitialLoc;
-	char *AutoGet;
-};
-
-struct Tail {
-	int Version;
-	int AdventureNumber;
-	int Unknown;
-};
-
-/**
- * Scott Adams game interpreter
- */
-class Scott : public Glk {
-public:
-	/**
-	 * Constructor
-	 */
-	Scott() : Glk() {}
-
-	/**
-	 * Execute the game
-	 */
-	virtual void execute();
-};
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 6e11ba7..c1a2867 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -4,9 +4,8 @@ MODULE_OBJS := \
 	detection.o \
 	events.o \
 	gargoyle.o \
-	glk/glk.o \
-	interpreter.o \
-	interps/scott/scott.o
+	glk.o \
+	scott/scott.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_GARGOYLE), DYNAMIC_PLUGIN)
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
new file mode 100644
index 0000000..9a1a6e3
--- /dev/null
+++ b/engines/gargoyle/scott/scott.cpp
@@ -0,0 +1,41 @@
+/* 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 "gargoyle/scott/scott.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+void Scott::main() {
+	event_t ev;
+	do {
+		glk_select(&ev);
+		switch (ev.type) {
+		default:
+			/* do nothing */
+			break;
+		}
+	} while (ev.type != evtype_Quit);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
new file mode 100644
index 0000000..0e7d85c
--- /dev/null
+++ b/engines/gargoyle/scott/scott.h
@@ -0,0 +1,106 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_SCOTT
+#define GARGOYLE_SCOTT
+
+/*
+ *	Controlling block
+ */
+
+#include "common/scummsys.h"
+#include "gargoyle/glk.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+#define LIGHT_SOURCE	9	// Always 9 how odd
+#define CARRIED		255		// Carried
+#define DESTROYED	0		// Destroyed
+#define DARKBIT		15
+#define LIGHTOUTBIT	16		// Light gone out
+
+#define YOUARE		1		// You are not I am
+#define SCOTTLIGHT	2		// Authentic Scott Adams light messages
+#define DEBUGGING	4		// Info from database load
+#define TRS80_STYLE	8		// Display in style used on TRS-80
+#define PREHISTORIC_LAMP 16	// Destroy the lamp (very old databases)
+
+struct Header {
+ 	int Unknown;
+	int NumItems;
+	int NumActions;
+	int NumWords;			// Smaller of verb/noun is padded to same size
+	int NumRooms;
+	int MaxCarry;
+	int PlayerRoom;
+	int Treasures;
+	int WordLength;
+	int LightTime;
+	int NumMessages;
+	int TreasureRoom;
+};
+
+struct Action {
+	uint Vocab;
+	uint Condition[5];
+	uint action[2];
+};
+
+struct Room {
+	char *Text;
+	short Exits[6];
+};
+
+struct Item {
+	char *Text;		// PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
+	byte Location;
+	byte InitialLoc;
+	char *AutoGet;
+};
+
+struct Tail {
+	int Version;
+	int AdventureNumber;
+	int Unknown;
+};
+
+/**
+ * Scott Adams game interpreter
+ */
+class Scott : public Glk {
+public:
+	/**
+	 * Constructor
+	 */
+	Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc) {}
+
+	/**
+	 * Execute the game
+	 */
+	virtual void main();
+};
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
+
+#endif


Commit: 22648145a91ae42ed0e1ab69b1dc9ad564e9fd1d
    https://github.com/scummvm/scummvm/commit/22648145a91ae42ed0e1ab69b1dc9ad564e9fd1d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Initial conversion of ScottFree

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


diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 3457f5f..7953c68 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -32,10 +32,12 @@
 namespace Gargoyle {
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-	_gameDescription(gameDesc), Engine(syst) {
+	_gameDescription(gameDesc), Engine(syst), _random("Gargoyle") {
+	_screen = nullptr;
 }
 
 GargoyleEngine::~GargoyleEngine() {
+	delete _screen;
 }
 
 void GargoyleEngine::initialize() {
@@ -46,11 +48,14 @@ void GargoyleEngine::initialize() {
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
 
 	initGraphics(640, 480, false);
+	_screen = new Graphics::Screen();
 }
 
 Common::Error GargoyleEngine::run() {
 	initialize();
-	main();
+
+	// TODO: Pass proper gamefile
+	runGame(nullptr);
 
 	return Common::kNoError;
 }
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 0940248..37c1a22 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -24,10 +24,12 @@
 #define GARGOYLE_GARGOLE_H
 
 #include "common/scummsys.h"
+#include "common/random.h"
 #include "common/system.h"
 #include "common/serializer.h"
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
+#include "graphics/screen.h"
 
 namespace Gargoyle {
 
@@ -67,6 +69,8 @@ private:
 	void initialize();
 protected:
 	const GargoyleGameDescription *_gameDescription;
+	Graphics::Screen *_screen;
+	Common::RandomSource _random;
 	int _loadSaveSlot;
 
 	// Engine APIs
@@ -76,7 +80,7 @@ protected:
 	/**
 	 * Main game loop for the individual interpreters
 	 */
-	virtual void main() = 0;
+	virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
 public:
 	GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
 	virtual ~GargoyleEngine();
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 350b40f..82943bd 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -191,11 +191,11 @@ void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
 	// TODO
 }
 
-void Glk::glk_put_string(char *s) {
+void Glk::glk_put_string(const char *s) {
 	// TODO
 }
 
-void Glk::glk_put_string_stream(strid_t str, char *s) {
+void Glk::glk_put_string_stream(strid_t str, const char *s) {
 	// TODO
 }
 
@@ -203,7 +203,7 @@ void Glk::glk_put_buffer(char *buf, glui32 len) {
 	// TODO
 }
 
-void Glk::glk_put_buffer_stream(strid_t str, char *buf, glui32 len) {
+void Glk::glk_put_buffer_stream(strid_t str, const char *buf, glui32 len) {
 	// TODO
 }
 
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index eab6f0b..55cd563 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -94,10 +94,10 @@ public:
 
 	void glk_put_char(unsigned char ch);
 	void glk_put_char_stream(strid_t str, unsigned char ch);
-	void glk_put_string(char *s);
-	void glk_put_string_stream(strid_t str, char *s);
+	void glk_put_string(const char *s);
+	void glk_put_string_stream(strid_t str, const char *s);
 	void glk_put_buffer(char *buf, glui32 len);
-	void glk_put_buffer_stream(strid_t str, char *buf, glui32 len);
+	void glk_put_buffer_stream(strid_t str, const char *buf, glui32 len);
 	void glk_set_style(glui32 styl);
 	void glk_set_style_stream(strid_t str, glui32 styl);
 
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 9a1a6e3..02096c6 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -25,16 +25,1265 @@
 namespace Gargoyle {
 namespace Scott {
 
-void Scott::main() {
+/*
+glkunix_argumentlist_t glkunix_arguments[] =
+{
+	{ "-y",		glkunix_arg_NoValue,		"-y		Generate 'You are', 'You are carrying' type messages for games that use these instead (eg Robin Of Sherwood)" },
+	{ "-i",		glkunix_arg_NoValue,		"-i		Generate 'I am' type messages (default)" },
+	{ "-d",		glkunix_arg_NoValue,		"-d		Debugging info on load " },
+	{ "-s",		glkunix_arg_NoValue,		"-s		Generate authentic Scott Adams driver light messages rather than other driver style ones (Light goes out in n turns..)" },
+	{ "-t",		glkunix_arg_NoValue,		"-t		Generate TRS80 style display (terminal width is 64 characters; a line <-----------------> is displayed after the top stuff; objects have periods after them instead of hyphens" },
+	{ "-p",		glkunix_arg_NoValue,		"-p		Use for prehistoric databases which don't use bit 16" },
+	{ "-w",		glkunix_arg_NoValue,		"-w		Disable upper window" },
+	{ "",		glkunix_arg_ValueFollows,	"filename	file to load" },
+
+	{ nullptr, glkunix_arg_End, nullptr }
+};
+*/
+
+Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
+		Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
+		Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
+		split_screen(true), Bottom(0), Top(0), BitFlags(0) {
+	Common::fill(&NounText[0], &NounText[16], '\0');
+	Common::fill(&Counters[0], &Counters[16], 0);
+	Common::fill(&RoomSaved[0], &RoomSaved[16], 0);
+}
+
+void Scott::runGame(Common::SeekableReadStream *gameFile) {
+	int vb, no;
+	initialize();
+
+	Bottom = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
+	if (Bottom == nullptr)
+		glk_exit();
+	glk_set_window(Bottom);
+
+	if (Options & TRS80_STYLE) {
+		Width = 64;
+		TopHeight = 11;
+	} else {
+		Width = 80;
+		TopHeight = 10;
+	}
+
+	if (split_screen) {
+		Top = glk_window_open(Bottom, winmethod_Above | winmethod_Fixed, TopHeight, wintype_TextGrid, 0);
+		if (Top == nullptr) {
+			split_screen = 0;
+			Top = Bottom;
+		}
+	} else {
+		Top = Bottom;
+	}
+
+	Output("\
+Scott Free, A Scott Adams game driver in C.\n\
+Release 1.14, (c) 1993,1994,1995 Swansea University Computer Society.\n\
+Distributed under the GNU software license\n\n");
+	LoadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
+
+	while (!shouldQuit()) {
+		glk_tick();
+
+		PerformActions(0, 0);
+
+		Look();
+
+		if (GetInput(&vb, &no) == -1)
+			continue;
+		switch (PerformActions(vb, no)) {
+		case -1:
+			Output("I don't understand your command. ");
+			break;
+		case -2:
+			Output("I can't do that yet. ");
+			break;
+		default:
+			break;
+		}
+
+		/* Brian Howarth games seem to use -1 for forever */
+		if (Items[LIGHT_SOURCE].Location/*==-1*/ != DESTROYED && GameHeader.LightTime != -1) {
+			GameHeader.LightTime--;
+			if (GameHeader.LightTime < 1) {
+				BitFlags |= (1 << LIGHTOUTBIT);
+				if (Items[LIGHT_SOURCE].Location == CARRIED ||
+					Items[LIGHT_SOURCE].Location == MyLoc) {
+					if (Options&SCOTTLIGHT)
+						Output("Light has run out! ");
+					else
+						Output("Your light has run out. ");
+				}
+				if (Options&PREHISTORIC_LAMP)
+					Items[LIGHT_SOURCE].Location = DESTROYED;
+			} else if (GameHeader.LightTime < 25) {
+				if (Items[LIGHT_SOURCE].Location == CARRIED ||
+					Items[LIGHT_SOURCE].Location == MyLoc) {
+
+					if (Options&SCOTTLIGHT) {
+						Output("Light runs out in ");
+						OutputNumber(GameHeader.LightTime);
+						Output(" turns. ");
+					} else {
+						if (GameHeader.LightTime % 5 == 0)
+							Output("Your light is growing dim. ");
+					}
+				}
+			}
+		}
+	}
+}
+
+void Scott::initialize() {
+	/*
+	int argc = data->argc;
+	char **argv = data->argv;
+
+	if (argc < 1)
+		return 0;
+
+	while (argv[1])
+	{
+		if (*argv[1] != '-')
+			break;
+		switch (argv[1][1])
+		{
+		case 'y':
+			Options |= YOUARE;
+			break;
+		case 'i':
+			Options &= ~YOUARE;
+			break;
+		case 'd':
+			Options |= DEBUGGING;
+			break;
+		case 's':
+			Options |= SCOTTLIGHT;
+			break;
+		case 't':
+			Options |= TRS80_STYLE;
+			break;
+		case 'p':
+			Options |= PREHISTORIC_LAMP;
+			break;
+		case 'w':
+			split_screen = 0;
+			break;
+		}
+		argv++;
+		argc--;
+	}
+
+	if (argc == 2)
+	{
+		game_file = argv[1];
+#ifdef GARGLK
+		const char *s;
+		if ((s = strrchr(game_file, '/')) != nullptr || (s = strrchr(game_file, '\\')) != nullptr)
+		{
+			garglk_set_story_name(s + 1);
+		}
+		else
+		{
+			garglk_set_story_name(game_file);
+		}
+#endif
+	}
+	*/
+}
+
+void Scott::Display(winid_t w, const char *fmt, ...) {
+	va_list ap;
+
+	va_start(ap, fmt);
+	Common::String msg = Common::String::vformat(fmt, ap);
+	va_end(ap);
+
+	glk_put_string_stream(glk_window_get_stream(w), msg.c_str());
+}
+
+void Scott::Delay(int seconds) {
 	event_t ev;
+
+	if (!glk_gestalt(gestalt_Timer, 0))
+		return;
+
+	glk_request_timer_events(1000 * seconds);
+
+	do
+	{
+		glk_select(&ev);
+	} while (ev.type != evtype_Timer);
+
+	glk_request_timer_events(0);
+}
+
+void Scott::Fatal(const char *x) {
+	error(x);
+}
+
+void Scott::ClearScreen(void) {
+	glk_window_clear(Bottom);
+}
+
+void *Scott::MemAlloc(int size) {
+	void *t = (void *)malloc(size);
+	if (t == nullptr)
+		Fatal("Out of memory");
+	return t;
+}
+
+bool Scott::RandomPercent(uint n) {
+	return _random.getRandomNumber(99) < n;
+}
+
+int Scott::CountCarried(void) {
+	int ct = 0;
+	int n = 0;
+	while (ct <= GameHeader.NumItems) {
+		if (Items[ct].Location == CARRIED)
+			n++;
+		ct++;
+	}
+	return n;
+}
+
+const char *Scott::MapSynonym(const char *word) {
+	int n = 1;
+	const char *tp;
+	static char lastword[16];	/* Last non synonym */
+	while (n <= GameHeader.NumWords) {
+		tp = Nouns[n];
+		if (*tp == '*')
+			tp++;
+		else
+			strcpy(lastword, tp);
+		if (xstrncasecmp(word, tp, GameHeader.WordLength) == 0)
+			return lastword;
+		n++;
+	}
+	return nullptr;
+}
+
+int Scott::MatchUpItem(const char *text, int loc) {
+	const char *word = MapSynonym(text);
+	int ct = 0;
+
+	if (word == nullptr)
+		word = text;
+
+	while (ct <= GameHeader.NumItems) {
+		if (Items[ct].AutoGet && Items[ct].Location == loc &&
+			xstrncasecmp(Items[ct].AutoGet, word, GameHeader.WordLength) == 0)
+			return ct;
+		ct++;
+	}
+
+	return -1;
+}
+
+char *Scott::ReadString(Common::SeekableReadStream *f)
+{
+	char tmp[1024];
+	char *t;
+	int c, nc;
+	int ct = 0;
+	do {
+		c = f->readByte();
+	} while (f->pos() < f->size() && Common::isSpace(c));
+	if (c != '"') {
+		Fatal("Initial quote expected");
+	}
+
+	do {
+		c = f->readByte();
+		if (c == EOF)
+			Fatal("EOF in string");
+		if (c == '"')
+		{
+			nc = f->readByte();
+			if (nc != '"') {
+				f->seek(-1, SEEK_CUR);
+				break;
+			}
+		}
+		if (c == '`')
+			c = '"'; /* pdd */
+
+					 /* Ensure a valid Glk newline is sent. */
+		if (c == '\n')
+			tmp[ct++] = 10;
+		/* Special case: assume CR is part of CRLF in a
+		* DOS-formatted file, and ignore it.
+		*/
+		else if (c == 13)
+			;
+		/* Pass only ASCII to Glk; the other reasonable option
+		* would be to pass Latin-1, but it's probably safe to
+		* assume that Scott Adams games are ASCII only.
+		*/
+		else if ((c >= 32 && c <= 126))
+			tmp[ct++] = c;
+		else
+			tmp[ct++] = '?';
+	} while (1);
+
+	tmp[ct] = 0;
+	t = (char *)MemAlloc(ct + 1);
+	memcpy(t, tmp, ct + 1);
+	return t;
+}
+
+void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
+	int unused, ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
+	int ct;
+	int lo;
+	Action *ap;
+	Room *rp;
+	Item *ip;
+	/* Load the header */
+
+	readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
+
+	GameHeader.NumItems = ni;
+	Items = (Item *)MemAlloc(sizeof(Item)*(ni + 1));
+	GameHeader.NumActions = na;
+	Actions = (Action *)MemAlloc(sizeof(Action)*(na + 1));
+	GameHeader.NumWords = nw;
+	GameHeader.WordLength = wl;
+	Verbs = (const char **)MemAlloc(sizeof(char *)*(nw + 1));
+	Nouns = (const char **)MemAlloc(sizeof(char *)*(nw + 1));
+	GameHeader.NumRooms = nr;
+	Rooms = (Room *)MemAlloc(sizeof(Room)*(nr + 1));
+	GameHeader.MaxCarry = mc;
+	GameHeader.PlayerRoom = pr;
+	GameHeader.Treasures = tr;
+	GameHeader.LightTime = lt;
+	LightRefill = lt;
+	GameHeader.NumMessages = mn;
+	Messages = (const char **)MemAlloc(sizeof(char *)*(mn + 1));
+	GameHeader.TreasureRoom = trm;
+
+	/* Load the actions */
+
+	ct = 0;
+	ap = Actions;
+	if (loud)
+		debug("Reading %d actions.", na);
+	while (ct < na + 1) {
+		readInts(f, 8,
+			&ap->Vocab,
+			&ap->Condition[0],
+			&ap->Condition[1],
+			&ap->Condition[2],
+			&ap->Condition[3],
+			&ap->Condition[4],
+			&ap->action[0],
+			&ap->action[1]);
+		ap++;
+		ct++;
+	}
+
+	ct = 0;
+	if (loud)
+		debug("Reading %d word pairs.", nw);
+	while (ct<nw + 1) {
+		Verbs[ct] = ReadString(f);
+		Nouns[ct] = ReadString(f);
+		ct++;
+	}
+	ct = 0;
+	rp = Rooms;
+	if (loud)
+		debug("Reading %d rooms.", nr);
+	while (ct<nr + 1) {
+		readInts(f, 6,
+				&rp->Exits[0], &rp->Exits[1], &rp->Exits[2],
+				&rp->Exits[3], &rp->Exits[4], &rp->Exits[5]);
+	
+		rp->Text = ReadString(f);
+		ct++;
+		rp++;
+	}
+
+	ct = 0;
+	if (loud)
+		debug("Reading %d messages.", mn);
+	while (ct<mn + 1) {
+		Messages[ct] = ReadString(f);
+		ct++;
+	}
+
+	ct = 0;
+	if (loud)
+		debug("Reading %d items.", ni);
+	ip = Items;
+	while (ct < ni + 1) {
+		ip->Text = ReadString(f);
+		ip->AutoGet = strchr(ip->Text, '/');
+		/* Some games use // to mean no auto get/drop word! */
+		if (ip->AutoGet && strcmp(ip->AutoGet, "//") && strcmp(ip->AutoGet, "/*")) {
+			char *t;
+			*ip->AutoGet++ = 0;
+			t = strchr(ip->AutoGet, '/');
+			if (t != nullptr)
+				*t = 0;
+		}
+
+		readInts(f, 1, &lo);
+		ip->Location = (unsigned char)lo;
+		ip->InitialLoc = ip->Location;
+		ip++;
+		ct++;
+	}
+	ct = 0;
+	/* Discard Comment Strings */
+	while (ct<na + 1) {
+		free(ReadString(f));
+		ct++;
+	}
+
+	readInts(f, 1, &ct);
+	if (loud)
+		debug("Version %d.%02d of Adventure ", ct / 100, ct % 100);
+	readInts(f, 1, &ct);
+
+	if (loud)
+		debug("%d.\nLoad Complete.\n", ct);
+}
+
+void Scott::Output(const char *a) {
+	Display(Bottom, "%s", a);
+}
+
+void Scott::OutputNumber(int a) {
+	Display(Bottom, "%d", a);
+}
+
+void Scott::Look(void) {
+	static char *ExitNames[6] = { "North", "South", "East", "West", "Up", "Down" };
+	Room *r;
+	int ct, f;
+	int pos;
+
+	if (split_screen)
+		glk_window_clear(Top);
+
+	if ((BitFlags&(1 << DARKBIT)) && Items[LIGHT_SOURCE].Location != CARRIED
+		&& Items[LIGHT_SOURCE].Location != MyLoc) {
+		if (Options&YOUARE)
+			Display(Top, "You can't see. It is too dark!\n");
+		else
+			Display(Top, "I can't see. It is too dark!\n");
+		if (Options & TRS80_STYLE)
+			Display(Top, TRS80_LINE);
+		return;
+	}
+	r = &Rooms[MyLoc];
+	if (*r->Text == '*')
+		Display(Top, "%s\n", r->Text + 1);
+	else {
+		if (Options&YOUARE)
+			Display(Top, "You are in a %s\n", r->Text);
+		else
+			Display(Top, "I'm in a %s\n", r->Text);
+	}
+
+	ct = 0;
+	f = 0;
+	Display(Top, "\nObvious exits: ");
+	while (ct<6) {
+		if (r->Exits[ct] != 0)
+		{
+			if (f == 0)
+				f = 1;
+			else
+				Display(Top, ", ");
+			Display(Top, "%s", ExitNames[ct]);
+		}
+		ct++;
+	}
+
+	if (f == 0)
+		Display(Top, "none");
+	Display(Top, ".\n");
+	ct = 0;
+	f = 0;
+	pos = 0;
+	while (ct <= GameHeader.NumItems) {
+		if (Items[ct].Location == MyLoc) {
+			if (f == 0) {
+				if (Options & YOUARE) {
+					Display(Top, "\nYou can also see: ");
+					pos = 18;
+				} else {
+					Display(Top, "\nI can also see: ");
+					pos = 16;
+				}
+				f++;
+			} else if (!(Options & TRS80_STYLE)) {
+				Display(Top, " - ");
+				pos += 3;
+			}
+			if (pos + (int)strlen(Items[ct].Text) > (Width - 10)) {
+				pos = 0;
+				Display(Top, "\n");
+			}
+			Display(Top, "%s", Items[ct].Text);
+			pos += strlen(Items[ct].Text);
+			if (Options & TRS80_STYLE) {
+				Display(Top, ". ");
+				pos += 2;
+			}
+		}
+		ct++;
+	}
+
+	Display(Top, "\n");
+	if (Options & TRS80_STYLE)
+		Display(Top, TRS80_LINE);
+}
+
+int Scott::WhichWord(const char *word, const char **list) {
+	int n = 1;
+	int ne = 1;
+	const char *tp;
+	while (ne <= GameHeader.NumWords) {
+		tp = list[ne];
+		if (*tp == '*')
+			tp++;
+		else
+			n = ne;
+		if (xstrncasecmp(word, tp, GameHeader.WordLength) == 0)
+			return n;
+		ne++;
+	}
+	return -1;
+}
+
+void Scott::LineInput(char *buf, size_t n) {
+	event_t ev;
+
+	glk_request_line_event(Bottom, buf, n - 1, 0);
+
 	do {
 		glk_select(&ev);
-		switch (ev.type) {
-		default:
-			/* do nothing */
+
+		if (ev.type == evtype_LineInput)
 			break;
-		}
+		else if (ev.type == evtype_Arrange && split_screen)
+			Look();
 	} while (ev.type != evtype_Quit);
+
+	buf[ev.val1] = 0;
+}
+
+void Scott::SaveGame(void) {
+	strid_t file;
+	frefid_t ref;
+	int ct;
+	Common::String msg;
+
+	ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame, filemode_Write, 0);
+	if (ref == nullptr) return;
+
+	file = glk_stream_open_file(ref, filemode_Write, 0);
+	glk_fileref_destroy(ref);
+	if (file == nullptr) return;
+
+	for (ct = 0; ct < 16; ct++) {
+		msg = Common::String::format("%d %d\n", Counters[ct], RoomSaved[ct]);
+		glk_put_string_stream(file, msg.c_str());
+	}
+
+	msg = Common::String::format("%lu %d %hd %d %d %hd\n",
+		BitFlags, (BitFlags&(1 << DARKBIT)) ? 1 : 0,
+		MyLoc, CurrentCounter, SavedRoom, GameHeader.LightTime);
+	glk_put_string_stream(file, msg.c_str());
+
+	for (ct = 0; ct <= GameHeader.NumItems; ct++) {
+		msg = Common::String::format("%hd\n", (short)Items[ct].Location);
+		glk_put_string_stream(file, msg.c_str());
+	}
+
+	glk_stream_close(file, nullptr);
+	Output("Saved.\n");
+}
+
+void Scott::LoadGame(void) {
+	strid_t file;
+	frefid_t ref;
+	char buf[128];
+	int ct = 0;
+	short lo;
+	short DarkFlag;
+
+	ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame, filemode_Read, 0);
+	if (ref == nullptr) return;
+
+	file = glk_stream_open_file(ref, filemode_Read, 0);
+	glk_fileref_destroy(ref);
+	if (file == nullptr) return;
+
+	for (ct = 0; ct<16; ct++) {
+		glk_get_line_stream(file, buf, sizeof buf);
+		sscanf(buf, "%d %d", &Counters[ct], &RoomSaved[ct]);
+	}
+
+	glk_get_line_stream(file, buf, sizeof buf);
+	sscanf(buf, "%ld %hd %d %d %d %d\n",
+		&BitFlags, &DarkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
+		&GameHeader.LightTime);
+
+	/* Backward compatibility */
+	if (DarkFlag)
+		BitFlags |= (1 << 15);
+	for (ct = 0; ct <= GameHeader.NumItems; ct++) {
+		glk_get_line_stream(file, buf, sizeof buf);
+		sscanf(buf, "%hd\n", &lo);
+		Items[ct].Location = (unsigned char)lo;
+	}
+}
+
+int Scott::GetInput(int *vb, int *no) {
+	char buf[256];
+	char verb[10], noun[10];
+	int vc, nc;
+	int num;
+
+	do {
+		do {
+			Output("\nTell me what to do ? ");
+			LineInput(buf, sizeof buf);
+			num = sscanf(buf, "%9s %9s", verb, noun);
+		} while (num == 0 || *buf == '\n');
+		
+		if (xstrcasecmp(verb, "restore") == 0) {
+			LoadGame();
+			return -1;
+		}
+		if (num == 1)
+			*noun = 0;
+		if (*noun == 0 && strlen(verb) == 1) {
+			switch (Common::isUpper((unsigned char)*verb) ? tolower((unsigned char)*verb) : *verb) {
+			case 'n':strcpy(verb, "NORTH"); break;
+			case 'e':strcpy(verb, "EAST"); break;
+			case 's':strcpy(verb, "SOUTH"); break;
+			case 'w':strcpy(verb, "WEST"); break;
+			case 'u':strcpy(verb, "UP"); break;
+			case 'd':strcpy(verb, "DOWN"); break;
+				/* Brian Howarth interpreter also supports this */
+			case 'i':strcpy(verb, "INVENTORY"); break;
+			}
+		}
+		nc = WhichWord(verb, Nouns);
+		/* The Scott Adams system has a hack to avoid typing 'go' */
+		if (nc >= 1 && nc <= 6) {
+			vc = 1;
+		} else {
+			vc = WhichWord(verb, Verbs);
+			nc = WhichWord(noun, Nouns);
+		}
+		*vb = vc;
+		*no = nc;
+		if (vc == -1) {
+			Output("You use word(s) I don't know! ");
+		}
+	} while (vc == -1);
+
+	strcpy(NounText, noun);	/* Needed by GET/DROP hack */
+	return 0;
+}
+
+int Scott::PerformLine(int ct) {
+	int continuation = 0;
+	int param[5], pptr = 0;
+	int act[4];
+	int cc = 0;
+
+	while (cc<5) {
+		int cv, dv;
+		cv = Actions[ct].Condition[cc];
+		dv = cv / 20;
+		cv %= 20;
+		switch (cv) {
+		case 0:
+			param[pptr++] = dv;
+			break;
+		case 1:
+			if (Items[dv].Location != CARRIED)
+				return 0;
+			break;
+		case 2:
+			if (Items[dv].Location != MyLoc)
+				return 0;
+			break;
+		case 3:
+			if (Items[dv].Location != CARRIED&&
+				Items[dv].Location != MyLoc)
+				return 0;
+			break;
+		case 4:
+			if (MyLoc != dv)
+				return 0;
+			break;
+		case 5:
+			if (Items[dv].Location == MyLoc)
+				return 0;
+			break;
+		case 6:
+			if (Items[dv].Location == CARRIED)
+				return 0;
+			break;
+		case 7:
+			if (MyLoc == dv)
+				return 0;
+			break;
+		case 8:
+			if ((BitFlags&(1 << dv)) == 0)
+				return 0;
+			break;
+		case 9:
+			if (BitFlags&(1 << dv))
+				return 0;
+			break;
+		case 10:
+			if (CountCarried() == 0)
+				return 0;
+			break;
+		case 11:
+			if (CountCarried())
+				return 0;
+			break;
+		case 12:
+			if (Items[dv].Location == CARRIED || Items[dv].Location == MyLoc)
+				return 0;
+			break;
+		case 13:
+			if (Items[dv].Location == 0)
+				return 0;
+			break;
+		case 14:
+			if (Items[dv].Location)
+				return 0;
+			break;
+		case 15:
+			if (CurrentCounter>dv)
+				return 0;
+			break;
+		case 16:
+			if (CurrentCounter <= dv)
+				return 0;
+			break;
+		case 17:
+			if (Items[dv].Location != Items[dv].InitialLoc)
+				return 0;
+			break;
+		case 18:
+			if (Items[dv].Location == Items[dv].InitialLoc)
+				return 0;
+			break;
+		case 19:/* Only seen in Brian Howarth games so far */
+			if (CurrentCounter != dv)
+				return 0;
+			break;
+		}
+		cc++;
+	}
+	/* Actions */
+	act[0] = Actions[ct].action[0];
+	act[2] = Actions[ct].action[1];
+	act[1] = act[0] % 150;
+	act[3] = act[2] % 150;
+	act[0] /= 150;
+	act[2] /= 150;
+	cc = 0;
+	pptr = 0;
+	while (cc<4)
+	{
+		if (act[cc] >= 1 && act[cc] < 52) {
+			Output(Messages[act[cc]]);
+			Output("\n");
+		} else if (act[cc] > 101) {
+			Output(Messages[act[cc] - 50]);
+			Output("\n");
+		}
+		else {
+			switch (act[cc]) {
+			case 0:/* NOP */
+				break;
+			case 52:
+				if (CountCarried() == GameHeader.MaxCarry)
+				{
+					if (Options&YOUARE)
+						Output("You are carrying too much. ");
+					else
+						Output("I've too much to carry! ");
+					break;
+				}
+				Items[param[pptr++]].Location = CARRIED;
+				break;
+			case 53:
+				Items[param[pptr++]].Location = MyLoc;
+				break;
+			case 54:
+				MyLoc = param[pptr++];
+				break;
+			case 55:
+				Items[param[pptr++]].Location = 0;
+				break;
+			case 56:
+				BitFlags |= 1 << DARKBIT;
+				break;
+			case 57:
+				BitFlags &= ~(1 << DARKBIT);
+				break;
+			case 58:
+				BitFlags |= (1 << param[pptr++]);
+				break;
+			case 59:
+				Items[param[pptr++]].Location = 0;
+				break;
+			case 60:
+				BitFlags &= ~(1 << param[pptr++]);
+				break;
+			case 61:
+				if (Options&YOUARE)
+					Output("You are dead.\n");
+				else
+					Output("I am dead.\n");
+				BitFlags &= ~(1 << DARKBIT);
+				MyLoc = GameHeader.NumRooms;/* It seems to be what the code says! */
+				break;
+			case 62:
+			{
+				/* Bug fix for some systems - before it could get parameters wrong */
+				int i = param[pptr++];
+				Items[i].Location = param[pptr++];
+				break;
+			}
+			case 63:
+				doneit:				Output("The game is now over.\n");
+									glk_exit();
+			case 64:
+				break;
+			case 65:
+			{
+				int i = 0;
+				int n = 0;
+				while (i <= GameHeader.NumItems)
+				{
+					if (Items[i].Location == GameHeader.TreasureRoom &&
+						*Items[i].Text == '*')
+						n++;
+					i++;
+				}
+				if (Options&YOUARE)
+					Output("You have stored ");
+				else
+					Output("I've stored ");
+				OutputNumber(n);
+				Output(" treasures.  On a scale of 0 to 100, that rates ");
+				OutputNumber((n * 100) / GameHeader.Treasures);
+				Output(".\n");
+				if (n == GameHeader.Treasures)
+				{
+					Output("Well done.\n");
+					goto doneit;
+				}
+				break;
+			}
+			case 66:
+			{
+				int i = 0;
+				int f = 0;
+				if (Options&YOUARE)
+					Output("You are carrying:\n");
+				else
+					Output("I'm carrying:\n");
+				while (i <= GameHeader.NumItems)
+				{
+					if (Items[i].Location == CARRIED)
+					{
+						if (f == 1)
+						{
+							if (Options & TRS80_STYLE)
+								Output(". ");
+							else
+								Output(" - ");
+						}
+						f = 1;
+						Output(Items[i].Text);
+					}
+					i++;
+				}
+				if (f == 0)
+					Output("Nothing");
+				Output(".\n");
+				break;
+			}
+			case 67:
+				BitFlags |= (1 << 0);
+				break;
+			case 68:
+				BitFlags &= ~(1 << 0);
+				break;
+			case 69:
+				GameHeader.LightTime = LightRefill;
+				Items[LIGHT_SOURCE].Location = CARRIED;
+				BitFlags &= ~(1 << LIGHTOUTBIT);
+				break;
+			case 70:
+				ClearScreen(); /* pdd. */
+				break;
+			case 71:
+				SaveGame();
+				break;
+			case 72:
+			{
+				int i1 = param[pptr++];
+				int i2 = param[pptr++];
+				int t = Items[i1].Location;
+				Items[i1].Location = Items[i2].Location;
+				Items[i2].Location = t;
+				break;
+			}
+			case 73:
+				continuation = 1;
+				break;
+			case 74:
+				Items[param[pptr++]].Location = CARRIED;
+				break;
+			case 75:
+			{
+				int i1, i2;
+				i1 = param[pptr++];
+				i2 = param[pptr++];
+				Items[i1].Location = Items[i2].Location;
+				break;
+			}
+			case 76:	/* Looking at adventure .. */
+				break;
+			case 77:
+				if (CurrentCounter >= 0)
+					CurrentCounter--;
+				break;
+			case 78:
+				OutputNumber(CurrentCounter);
+				break;
+			case 79:
+				CurrentCounter = param[pptr++];
+				break;
+			case 80:
+			{
+				int t = MyLoc;
+				MyLoc = SavedRoom;
+				SavedRoom = t;
+				break;
+			}
+			case 81:
+			{
+				/* This is somewhat guessed. Claymorgue always
+				seems to do select counter n, thing, select counter n,
+				but uses one value that always seems to exist. Trying
+				a few options I found this gave sane results on ageing */
+				int t = param[pptr++];
+				int c1 = CurrentCounter;
+				CurrentCounter = Counters[t];
+				Counters[t] = c1;
+				break;
+			}
+			case 82:
+				CurrentCounter += param[pptr++];
+				break;
+			case 83:
+				CurrentCounter -= param[pptr++];
+				if (CurrentCounter < -1)
+					CurrentCounter = -1;
+				/* Note: This seems to be needed. I don't yet
+				know if there is a maximum value to limit too */
+				break;
+			case 84:
+				Output(NounText);
+				break;
+			case 85:
+				Output(NounText);
+				Output("\n");
+				break;
+			case 86:
+				Output("\n");
+				break;
+			case 87:
+			{
+				/* Changed this to swap location<->roomflag[x]
+				not roomflag 0 and x */
+				int p = param[pptr++];
+				int sr = MyLoc;
+				MyLoc = RoomSaved[p];
+				RoomSaved[p] = sr;
+				break;
+			}
+			case 88:
+				Delay(2);
+				break;
+			case 89:
+				pptr++;
+				/* SAGA draw picture n */
+				/* Spectrum Seas of Blood - start combat ? */
+				/* Poking this into older spectrum games causes a crash */
+				break;
+			default:
+				error("Unknown action %d [Param begins %d %d]\n",
+					act[cc], param[pptr], param[pptr + 1]);
+				break;
+			}
+		}
+
+		cc++;
+	}
+
+	return 1 + continuation;
+}
+
+int Scott::PerformActions(int vb, int no) {
+	static int disable_sysfunc = 0;	/* Recursion lock */
+	int d = BitFlags&(1 << DARKBIT);
+
+	int ct = 0;
+	int fl;
+	int doagain = 0;
+	if (vb == 1 && no == -1) {
+		Output("Give me a direction too.");
+		return 0;
+	}
+	if (vb == 1 && no >= 1 && no <= 6) {
+		int nl;
+		if (Items[LIGHT_SOURCE].Location == MyLoc ||
+			Items[LIGHT_SOURCE].Location == CARRIED)
+			d = 0;
+		if (d)
+			Output("Dangerous to move in the dark! ");
+		nl = Rooms[MyLoc].Exits[no - 1];
+		if (nl != 0) {
+			MyLoc = nl;
+			return 0;
+		}
+		if (d) {
+			if (Options&YOUARE)
+				Output("You fell down and broke your neck. ");
+			else
+				Output("I fell down and broke my neck. ");
+			glk_exit();
+		}
+		if (Options&YOUARE)
+			Output("You can't go in that direction. ");
+		else
+			Output("I can't go in that direction. ");
+		return 0;
+	}
+
+	fl = -1;
+	while (ct <= GameHeader.NumActions) {
+		int vv, nv;
+		vv = Actions[ct].Vocab;
+		/* Think this is now right. If a line we run has an action73
+		run all following lines with vocab of 0,0 */
+		if (vb != 0 && (doagain&&vv != 0))
+			break;
+		/* Oops.. added this minor cockup fix 1.11 */
+		if (vb != 0 && !doagain && fl == 0)
+			break;
+		nv = vv % 150;
+		vv /= 150;
+		if ((vv == vb) || (doagain&&Actions[ct].Vocab == 0)) {
+			if ((vv == 0 && RandomPercent(nv)) || doagain ||
+					(vv != 0 && (nv == no || nv == 0))) {
+				int f2;
+				if (fl == -1)
+					fl = -2;
+				if ((f2 = PerformLine(ct)) > 0) {
+					/* ahah finally figured it out ! */
+					fl = 0;
+					if (f2 == 2)
+						doagain = 1;
+					if (vb != 0 && doagain == 0)
+						return 0;
+				}
+			}
+		}
+		ct++;
+
+		/* Previously this did not check ct against
+		* GameHeader.NumActions and would read past the end of
+		* Actions.  I don't know what should happen on the last
+		* action, but doing nothing is better than reading one
+		* past the end.
+		* --Chris
+		*/
+		if (ct <= GameHeader.NumActions && Actions[ct].Vocab != 0)
+			doagain = 0;
+	}
+	if (fl != 0 && disable_sysfunc == 0) {
+		int item;
+		if (Items[LIGHT_SOURCE].Location == MyLoc ||
+			Items[LIGHT_SOURCE].Location == CARRIED)
+			d = 0;
+		if (vb == 10 || vb == 18) {
+			/* Yes they really _are_ hardcoded values */
+			if (vb == 10) {
+				if (xstrcasecmp(NounText, "ALL") == 0) {
+					int i = 0;
+					int f = 0;
+
+					if (d) {
+						Output("It is dark.\n");
+						return 0;
+					}
+					while (i <= GameHeader.NumItems) {
+						if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
+							no = WhichWord(Items[i].AutoGet, Nouns);
+							disable_sysfunc = 1;	/* Don't recurse into auto get ! */
+							PerformActions(vb, no);	/* Recursively check each items table code */
+							disable_sysfunc = 0;
+							if (CountCarried() == GameHeader.MaxCarry) {
+								if (Options&YOUARE)
+									Output("You are carrying too much. ");
+								else
+									Output("I've too much to carry. ");
+								return 0;
+							}
+							Items[i].Location = CARRIED;
+							Output(Items[i].Text);
+							Output(": O.K.\n");
+							f = 1;
+						}
+						i++;
+					}
+					if (f == 0)
+						Output("Nothing taken.");
+					return 0;
+				}
+				if (no == -1)
+				{
+					Output("What ? ");
+					return 0;
+				}
+				if (CountCarried() == GameHeader.MaxCarry)
+				{
+					if (Options&YOUARE)
+						Output("You are carrying too much. ");
+					else
+						Output("I've too much to carry. ");
+					return 0;
+				}
+				item = MatchUpItem(NounText, MyLoc);
+				if (item == -1)
+				{
+					if (Options&YOUARE)
+						Output("It is beyond your power to do that. ");
+					else
+						Output("It's beyond my power to do that. ");
+					return 0;
+				}
+				Items[item].Location = CARRIED;
+				Output("O.K. ");
+				return 0;
+			}
+			if (vb == 18) {
+				if (xstrcasecmp(NounText, "ALL") == 0) {
+					int i = 0;
+					int f = 0;
+					while (i <= GameHeader.NumItems) {
+						if (Items[i].Location == CARRIED && Items[i].AutoGet && Items[i].AutoGet[0] != '*') {
+							no = WhichWord(Items[i].AutoGet, Nouns);
+							disable_sysfunc = 1;
+							PerformActions(vb, no);
+							disable_sysfunc = 0;
+							Items[i].Location = MyLoc;
+							Output(Items[i].Text);
+							Output(": O.K.\n");
+							f = 1;
+						}
+						i++;
+					}
+					if (f == 0)
+						Output("Nothing dropped.\n");
+					return 0;
+				}
+				if (no == -1) {
+					Output("What ? ");
+					return 0;
+				}
+				item = MatchUpItem(NounText, CARRIED);
+				if (item == -1) {
+					if (Options&YOUARE)
+						Output("It's beyond your power to do that.\n");
+					else
+						Output("It's beyond my power to do that.\n");
+					return 0;
+				}
+				Items[item].Location = MyLoc;
+				Output("O.K. ");
+				return 0;
+			}
+		}
+	}
+
+	return fl;
+}
+
+int Scott::xstrcasecmp(const char *s1, const char *s2) {
+	const unsigned char
+		*us1 = (const unsigned char *)s1,
+		*us2 = (const unsigned char *)s2;
+
+	while (tolower(*us1) == tolower(*us2++))
+		if (*us1++ == '\0')
+			return (0);
+	return (tolower(*us1) - tolower(*--us2));
+}
+
+int Scott::xstrncasecmp(const char *s1, const char *s2, size_t n) {
+	if (n != 0) {
+		const unsigned char
+			*us1 = (const unsigned char *)s1,
+			*us2 = (const unsigned char *)s2;
+
+		do {
+			if (tolower(*us1) != tolower(*us2++))
+				return (tolower(*us1) - tolower(*--us2));
+			if (*us1++ == '\0')
+				break;
+		} while (--n != 0);
+	}
+
+	return 0;
+}
+
+void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
+	va_list va;
+	va_start(va, count);
+	unsigned char c = '\0';
+
+	for (size_t idx = 0; idx < count; ++idx) {
+		if (idx > 0) {
+			while (f->pos() < f->size() && Common::isSpace(c))
+				c = f->readByte();
+		} else {
+			c = f->readByte();
+		}
+
+		// Get the next value
+		int *val = (int *)va_arg(va, int);
+		*val = 0;
+		while (Common::isDigit(c)) {
+			*val = (*val * 10) + (c - '0');
+			c = f->readByte();
+		}
+	}
+
+	va_end(va);
 }
 
 } // End of namespace Scott
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
index 0e7d85c..52ad560 100644
--- a/engines/gargoyle/scott/scott.h
+++ b/engines/gargoyle/scott/scott.h
@@ -45,6 +45,9 @@ namespace Scott {
 #define TRS80_STYLE	8		// Display in style used on TRS-80
 #define PREHISTORIC_LAMP 16	// Destroy the lamp (very old databases)
 
+#define TRS80_LINE	"\n<------------------------------------------------------------>\n"
+#define MyLoc	(GameHeader.PlayerRoom)
+
 struct Header {
  	int Unknown;
 	int NumItems;
@@ -58,17 +61,30 @@ struct Header {
 	int LightTime;
 	int NumMessages;
 	int TreasureRoom;
+
+	Header() : Unknown(0), NumItems(0), NumActions(0), NumWords(0), NumRooms(0),
+		MaxCarry(0), PlayerRoom(0), Treasures(0), WordLength(0), LightTime(0),
+		NumMessages(0), TreasureRoom(0) {}
 };
 
 struct Action {
 	uint Vocab;
 	uint Condition[5];
 	uint action[2];
+
+	Action() : Vocab(0) {
+		Common::fill(&Condition[0], &Condition[5], 0);
+		Common::fill(&action[0], &action[2], 0);
+	}
 };
 
 struct Room {
 	char *Text;
 	short Exits[6];
+
+	Room() : Text(0) {
+		Common::fill(&Exits[0], &Exits[6], 0);
+	}
 };
 
 struct Item {
@@ -76,28 +92,84 @@ struct Item {
 	byte Location;
 	byte InitialLoc;
 	char *AutoGet;
+
+	Item() : Text(nullptr), Location(0), InitialLoc(0), AutoGet(nullptr) {}
 };
 
 struct Tail {
 	int Version;
 	int AdventureNumber;
 	int Unknown;
+
+	Tail() : Version(0), AdventureNumber(0), Unknown(0) {}
 };
 
 /**
  * Scott Adams game interpreter
  */
 class Scott : public Glk {
+private:
+	Header GameHeader;
+	Item *Items;
+	Room *Rooms;
+	const char **Verbs;
+	const char **Nouns;
+	const char **Messages;
+	Action *Actions;
+	int LightRefill;
+	char NounText[16];
+	int Counters[16];   ///< Range unknown
+	int CurrentCounter;
+	int SavedRoom;
+	int RoomSaved[16];  ///< Range unknown
+	int Options;        ///< Option flags set
+	int Width;		    ///< Terminal width
+	int TopHeight;      ///< Height of top window
+
+	bool split_screen;
+	winid_t Bottom, Top;
+	uint32 BitFlags;    ///< Might be >32 flags - I haven't seen >32 yet
+private:
+	/**
+	 * Initialization code
+	 */
+	void initialize();
+
+	void Display(winid_t w, const char *fmt, ...);
+	void Delay(int seconds);
+	void Fatal(const char *x);
+	void ClearScreen(void);
+	void *MemAlloc(int size);
+	bool RandomPercent(uint n);
+	int CountCarried(void);
+	const char *MapSynonym(const char *word);
+	int MatchUpItem(const char *text, int loc);
+	char *ReadString(Common::SeekableReadStream *f);
+	void LoadDatabase(Common::SeekableReadStream *f, int loud);
+	void Output(const char *a);
+	void OutputNumber(int a);
+	void Look(void);
+	int WhichWord(const char *word, const char **list);
+	void LineInput(char *buf, size_t n);
+	void SaveGame(void);
+	void LoadGame(void);
+	int GetInput(int *vb, int *no);
+	int PerformLine(int ct);
+	int PerformActions(int vb, int no);
+
+	int xstrcasecmp(const char *, const char *);
+	int xstrncasecmp(const char *, const char *, size_t);
+	void readInts(Common::SeekableReadStream *f, size_t count, ...);
 public:
 	/**
 	 * Constructor
 	 */
-	Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc) {}
+	Scott(OSystem *syst, const GargoyleGameDescription *gameDesc);
 
 	/**
 	 * Execute the game
 	 */
-	virtual void main();
+	virtual void runGame(Common::SeekableReadStream *gameFile) override;
 };
 
 } // End of namespace Scott


Commit: 9c6aca1d5790e0c09dca8313e138f4744b4f9130
    https://github.com/scummvm/scummvm/commit/9c6aca1d5790e0c09dca8313e138f4744b4f9130
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Minor stream and event loop fixes

Changed paths:
    engines/gargoyle/scott/scott.cpp


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 02096c6..42a9088 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -211,10 +211,9 @@ void Scott::Delay(int seconds) {
 
 	glk_request_timer_events(1000 * seconds);
 
-	do
-	{
+	do {
 		glk_select(&ev);
-	} while (ev.type != evtype_Timer);
+	} while (ev.type != evtype_Timer && ev.type != evtype_Quit);
 
 	glk_request_timer_events(0);
 }
@@ -283,8 +282,7 @@ int Scott::MatchUpItem(const char *text, int loc) {
 	return -1;
 }
 
-char *Scott::ReadString(Common::SeekableReadStream *f)
-{
+char *Scott::ReadString(Common::SeekableReadStream *f) {
 	char tmp[1024];
 	char *t;
 	int c, nc;
@@ -296,12 +294,12 @@ char *Scott::ReadString(Common::SeekableReadStream *f)
 		Fatal("Initial quote expected");
 	}
 
-	do {
-		c = f->readByte();
-		if (c == EOF)
+	for (;;) {
+		if (f->pos() >= f->size())
 			Fatal("EOF in string");
-		if (c == '"')
-		{
+
+		c = f->readByte();
+		if (c == '"') {
 			nc = f->readByte();
 			if (nc != '"') {
 				f->seek(-1, SEEK_CUR);


Commit: db35a9cdc8832ce7768202126842d64a84afc444
    https://github.com/scummvm/scummvm/commit/db35a9cdc8832ce7768202126842d64a84afc444
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Adding game detection code

Changed paths:
  A engines/gargoyle/scott/detection.cpp
  A engines/gargoyle/scott/detection.h
    engines/gargoyle/detection.cpp
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 93859e6..f86e34c 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -64,6 +64,7 @@ static const PlainGameDescriptor gargoyleGames[] = {
 };
 
 #include "gargoyle/detection_tables.h"
+#include "gargoyle/scott/detection.h"
 #include "gargoyle/scott/scott.h"
 
 class GargoyleMetaEngine : public AdvancedMetaEngine {
@@ -80,12 +81,14 @@ public:
 		return "Gargoyle Engine (c)";
 	}
 
-	virtual bool hasFeature(MetaEngineFeature f) const;
-	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+	virtual bool hasFeature(MetaEngineFeature f) const override;
+	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
 	virtual SaveStateList listSaves(const char *target) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
+
+	virtual DetectedGames detectGames(const Common::FSList &fslist) const override;
 };
 
 bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -133,6 +136,12 @@ SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, i
 	return SaveStateDescriptor();
 }
 
+DetectedGames GargoyleMetaEngine::detectGames(const Common::FSList &fslist) const {
+	DetectedGames detectedGames;
+	Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
+
+	return detectedGames;
+}
 
 #if PLUGIN_ENABLED_DYNAMIC(GARGOYLE)
 	REGISTER_PLUGIN_DYNAMIC(Gargoyle, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index c1a2867..7462f7a 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	events.o \
 	gargoyle.o \
 	glk.o \
+	scott/detection.o \
 	scott/scott.o
 
 # This module can be built as a plugin
diff --git a/engines/gargoyle/scott/detection.cpp b/engines/gargoyle/scott/detection.cpp
new file mode 100644
index 0000000..fdf659e
--- /dev/null
+++ b/engines/gargoyle/scott/detection.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/scott/detection.h"
+#include "common/file.h"
+#include "common/md5.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+struct ScottGame {
+	const char *_md5;
+	int32 _filesize;
+	const char *_desc;
+};
+
+const ScottGame SCOTT_GAMES[] = {
+	{ "ae541fc1085da2f7d561b72ed20a6bc1", 18003, "Adventureland" },
+	{ nullptr, 0, nullptr }
+};
+
+void ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+	Common::File gameFile;
+	Common::String md5;
+
+	// Loop through the files of the folder
+	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+		if (file->isDirectory() || !file->getName().hasSuffix(".saga"))
+			continue;
+
+		if (gameFile.open(*file)) {
+			md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+
+			// Scan through the Scott game list for a match
+			const ScottGame *p = SCOTT_GAMES;
+			while (p->_md5 && p->_filesize != gameFile.size() && md5 != p->_md5)
+				++p;
+
+			if (p->_filesize) {
+				// Found a match
+				DetectedGame gd("scott", p->_desc, Common::EN_ANY, Common::kPlatformUnknown, "Scott");
+				gameList.push_back(gd);
+			}
+
+			gameFile.close();
+		}
+	}
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/scott/detection.h b/engines/gargoyle/scott/detection.h
new file mode 100644
index 0000000..07f1476
--- /dev/null
+++ b/engines/gargoyle/scott/detection.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_SCOTT_DETECTION
+#define GARGOYLE_SCOTT_DETECTION
+
+#include "common/fs.h"
+#include "engines/game.h"
+
+namespace Gargoyle {
+namespace Scott {
+
+class ScottMetaEngine {
+public:
+	static void detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+};
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
+
+#endif


Commit: ef161922d85a23ea99d9e86417c6ac8b675dc43d
    https://github.com/scummvm/scummvm/commit/ef161922d85a23ea99d9e86417c6ac8b675dc43d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
ENGINES: Add optional extra configuration entries when creating new targets

Changed paths:
    base/plugins.cpp
    engines/game.h


diff --git a/base/plugins.cpp b/base/plugins.cpp
index f6bbeac..f4d18ef 100644
--- a/base/plugins.cpp
+++ b/base/plugins.cpp
@@ -580,6 +580,11 @@ Common::String EngineManager::createTargetForGame(const DetectedGame &game) {
 	addStringToConf("extra", game.extra, domain);
 	addStringToConf("guioptions", game.getGUIOptions(), domain);
 
+	// Add any extra configuration keys
+	for (Common::StringMap::iterator i = game._extraConfigEntries.begin();
+			i != game._extraConfigEntries.end(); ++i)
+		addStringToConf((*i)._key, (*i)._value, domain);
+
 	// TODO: Setting the description field here has the drawback
 	// that the user does never notice when we upgrade our descriptions.
 	// It might be nice to leave this field empty, and only set it to
diff --git a/engines/game.h b/engines/game.h
index 14f9962..304166d 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -148,6 +148,21 @@ struct DetectedGame {
 	 */
 	GameSupportLevel gameSupportLevel;
 
+	/**
+	 * A list of extra keys to write to the configuration file
+	 */
+	Common::StringMap _extraConfigEntries;
+
+	/**
+	 * Allows adding of extra entries to be saved as part of the detection entry
+	 * in the configuration file.
+	 * @remarks		Any entry added using this should not be relied on being present
+	 *				in the configuration file, since starting games directly from the
+	 *				command line bypasses the game detection code
+	 */
+	void addExtraEntry(const Common::String &key, const Common::String &value) {
+		_extraConfigEntries[key] = value;
+	}
 private:
 	/**
 	 * Update the description string by appending (EXTRA/PLATFORM/LANG) to it.


Commit: 5f626f7a6abbcc2ec18cc50df1ce5ba705f967cd
    https://github.com/scummvm/scummvm/commit/5f626f7a6abbcc2ec18cc50df1ce5ba705f967cd
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add detection logic needed for engine startup

Changed paths:
    engines/gargoyle/detection.cpp
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/scott/detection.cpp


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index f86e34c..b7a32a7 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -37,9 +37,13 @@ namespace Gargoyle {
 
 struct GargoyleGameDescription {
 	ADGameDescription desc;
+	Common::String filename;
 	InterpreterType interpType;
 };
 
+const Common::String &GargoyleEngine::getFilename() const {
+	return _gameDescription->filename;
+}
 uint32 GargoyleEngine::getFeatures() const {
 	return _gameDescription->desc.flags;
 }
@@ -63,6 +67,7 @@ static const PlainGameDescriptor gargoyleGames[] = {
 	{0, 0}
 };
 
+#include "common/config-manager.h"
 #include "gargoyle/detection_tables.h"
 #include "gargoyle/scott/detection.h"
 #include "gargoyle/scott/scott.h"
@@ -89,6 +94,8 @@ public:
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 
 	virtual DetectedGames detectGames(const Common::FSList &fslist) const override;
+
+	virtual ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const override;
 };
 
 bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -108,7 +115,9 @@ bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
 }
 
 bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
-	const Gargoyle::GargoyleGameDescription *gd = (const Gargoyle::GargoyleGameDescription *)desc;
+	Gargoyle::GargoyleGameDescription *gd = (Gargoyle::GargoyleGameDescription *)desc;
+	gd->filename = ConfMan.get("filename");
+
 	switch (gd->interpType) {
 	case Gargoyle::INTERPRETER_SCOTT:
 		*engine = new Gargoyle::Scott::Scott(syst, gd);
@@ -143,6 +152,24 @@ DetectedGames GargoyleMetaEngine::detectGames(const Common::FSList &fslist) cons
 	return detectedGames;
 }
 
+static Gargoyle::GargoyleGameDescription gameDescription;
+
+ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
+	ADDetectedGames detectedGames;
+	static char gameId[100];
+	strcpy(gameId, ConfMan.get("gameid").c_str());
+
+	gameDescription.desc.gameId = gameId;
+	gameDescription.desc.language = language;
+	gameDescription.desc.platform = platform;
+	gameDescription.desc.extra = extra.c_str();
+	gameDescription.filename = ConfMan.get("filename");
+
+	ADDetectedGame dg((ADGameDescription *)&gameDescription);
+	detectedGames.push_back(dg);
+	return detectedGames;
+}
+
 #if PLUGIN_ENABLED_DYNAMIC(GARGOYLE)
 	REGISTER_PLUGIN_DYNAMIC(Gargoyle, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
 #else
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 7953c68..561f14a 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -24,6 +24,7 @@
 #include "common/config-manager.h"
 #include "common/debug-channels.h"
 #include "common/events.h"
+#include "common/file.h"
 #include "engines/util.h"
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
@@ -54,8 +55,10 @@ void GargoyleEngine::initialize() {
 Common::Error GargoyleEngine::run() {
 	initialize();
 
-	// TODO: Pass proper gamefile
-	runGame(nullptr);
+	Common::File f;
+	if (!f.open(getFilename()))
+		error("Could not open game file");
+	runGame(&f);
 
 	return Common::kNoError;
 }
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 37c1a22..3cfee29 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -75,6 +75,10 @@ protected:
 
 	// Engine APIs
 	virtual Common::Error run();
+
+	/**
+	  * Returns true whether a given feature is supported by the engine
+	  */
 	virtual bool hasFeature(EngineFeature f) const;
 
 	/**
@@ -85,10 +89,30 @@ public:
 	GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
 	virtual ~GargoyleEngine();
 
+	/**
+	 * Returns the bitset of game features
+	 */
 	uint32 getFeatures() const;
+
+	/**
+	 * Returns whether the game is a demo
+	 */
 	bool isDemo() const;
+
+	/**
+	 * Returns the language
+	 */
 	Common::Language getLanguage() const;
+
+	/**
+	 * Returns the running interpreter type
+	 */
 	InterpreterType getInterpreterType() const;
+
+	/**
+	 * Returns the primary filename for the game
+	 */
+	const Common::String &GargoyleEngine::getFilename() const;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/scott/detection.cpp b/engines/gargoyle/scott/detection.cpp
index fdf659e..d2ed439 100644
--- a/engines/gargoyle/scott/detection.cpp
+++ b/engines/gargoyle/scott/detection.cpp
@@ -58,6 +58,8 @@ void ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 			if (p->_filesize) {
 				// Found a match
 				DetectedGame gd("scott", p->_desc, Common::EN_ANY, Common::kPlatformUnknown, "Scott");
+				gd.addExtraEntry("filename", file->getName());
+
 				gameList.push_back(gd);
 			}
 


Commit: 74080143da579b2db0521ec204a172719c096bc5
    https://github.com/scummvm/scummvm/commit/74080143da579b2db0521ec204a172719c096bc5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Detect presence of game file in detectGames

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


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index b7a32a7..63ada3e 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -68,6 +68,7 @@ static const PlainGameDescriptor gargoyleGames[] = {
 };
 
 #include "common/config-manager.h"
+#include "common/file.h"
 #include "gargoyle/detection_tables.h"
 #include "gargoyle/scott/detection.h"
 #include "gargoyle/scott/scott.h"
@@ -158,15 +159,19 @@ ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, con
 	ADDetectedGames detectedGames;
 	static char gameId[100];
 	strcpy(gameId, ConfMan.get("gameid").c_str());
+	Common::String filename = ConfMan.get("filename");
 
-	gameDescription.desc.gameId = gameId;
-	gameDescription.desc.language = language;
-	gameDescription.desc.platform = platform;
-	gameDescription.desc.extra = extra.c_str();
-	gameDescription.filename = ConfMan.get("filename");
+	if (parent.getChild(filename).exists()) {
+		gameDescription.desc.gameId = gameId;
+		gameDescription.desc.language = language;
+		gameDescription.desc.platform = platform;
+		gameDescription.desc.extra = extra.c_str();
+		gameDescription.filename = filename;
+
+		ADDetectedGame dg((ADGameDescription *)&gameDescription);
+		detectedGames.push_back(dg);
+	}
 
-	ADDetectedGame dg((ADGameDescription *)&gameDescription);
-	detectedGames.push_back(dg);
 	return detectedGames;
 }
 
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 561f14a..5d3d53c 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -56,9 +56,8 @@ Common::Error GargoyleEngine::run() {
 	initialize();
 
 	Common::File f;
-	if (!f.open(getFilename()))
-		error("Could not open game file");
-	runGame(&f);
+	if (f.open(getFilename()))
+		runGame(&f);
 
 	return Common::kNoError;
 }


Commit: 77f357e72f1f1bfad93b3b1c54f6f57b67131ba7
    https://github.com/scummvm/scummvm/commit/77f357e72f1f1bfad93b3b1c54f6f57b67131ba7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add window creation

Changed paths:
  A engines/gargoyle/windows.cpp
  A engines/gargoyle/windows.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/module.mk
    engines/gargoyle/scott/scott.cpp


diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 5d3d53c..5401b2c 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -29,16 +29,20 @@
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/events.h"
+#include "gargoyle/windows.h"
 
 namespace Gargoyle {
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-	_gameDescription(gameDesc), Engine(syst), _random("Gargoyle") {
-	_screen = nullptr;
+		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"),
+		_events(nullptr), _screen(nullptr), _windows(nullptr) {
 }
 
 GargoyleEngine::~GargoyleEngine() {
+	delete _events;
 	delete _screen;
+	delete _windows;
 }
 
 void GargoyleEngine::initialize() {
@@ -50,6 +54,8 @@ void GargoyleEngine::initialize() {
 
 	initGraphics(640, 480, false);
 	_screen = new Graphics::Screen();
+	_events = new Events();
+	_windows = new Windows(_screen);
 }
 
 Common::Error GargoyleEngine::run() {
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 3cfee29..c7a7b86 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -33,6 +33,9 @@
 
 namespace Gargoyle {
 
+class Events;
+class Windows;
+
 enum InterpreterType {
 	INTERPRETER_SCOTT
 };
@@ -69,7 +72,9 @@ private:
 	void initialize();
 protected:
 	const GargoyleGameDescription *_gameDescription;
+	Events *_events;
 	Graphics::Screen *_screen;
+	Windows *_windows;
 	Common::RandomSource _random;
 	int _loadSaveSlot;
 
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 82943bd..278faf5 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -21,6 +21,8 @@
  */
 
 #include "gargoyle/glk.h"
+#include "gargoyle/events.h"
+#include "gargoyle/windows.h"
 
 namespace Gargoyle {
 
@@ -60,15 +62,12 @@ unsigned char Glk::glk_char_to_upper(unsigned char ch) {
 	return '\0';
 }
 
-winid_t Glk::glk_window_get_root(void) {
-	// TODO
-	return nullptr;
+winid_t Glk::glk_window_get_root(void) const {
+	return _windows->getRoot();
 }
 
-winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size,
-	glui32 wintype, glui32 rock) {
-	// TODO
-	return nullptr;
+winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, glui32 rock) const {
+	return _windows->windowOpen(split, method, size, wintype, rock);
 }
 
 void Glk::glk_window_close(winid_t win, stream_result_t *result) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 55cd563..ce21910 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -25,9 +25,11 @@
 
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/glk_types.h"
+#include "gargoyle/windows.h"
 
 namespace Gargoyle {
 
+
 /**
  * Implements the GLK interface
  */
@@ -57,9 +59,17 @@ public:
 	unsigned char glk_char_to_lower(unsigned char ch);
 	unsigned char glk_char_to_upper(unsigned char ch);
 
-	winid_t glk_window_get_root(void);
+	/**
+	 * Get the root window of the window hierarchy
+	 */
+	winid_t glk_window_get_root(void) const;
+
+	/**
+	 * Open a new window
+	 */
 	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
-		glui32 wintype, glui32 rock);
+		glui32 wintype, glui32 rock = 0) const;
+
 	void glk_window_close(winid_t win, stream_result_t *result);
 	void glk_window_get_size(winid_t win, glui32 *widthptr,
 		glui32 *heightptr);
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index a822741..040d666 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -29,6 +29,7 @@ namespace Gargoyle {
 
 typedef uint32 glui32;
 typedef int32 glsi32;
+class Window;
 
 /**
  * These are the compile-time conditionals that reveal various Glk optional modules.
@@ -48,7 +49,6 @@ typedef int32 glsi32;
  * These types are opaque object identifiers. They're pointers to opaque
  * C structures, which are defined differently by each library.
  */
-typedef struct glk_window_struct  *winid_t;
 typedef struct glk_stream_struct  *strid_t;
 typedef struct glk_fileref_struct *frefid_t;
 typedef struct glk_schannel_struct *schanid_t;
@@ -233,7 +233,7 @@ enum ImageAlign {
 
 struct event_struct {
 	glui32 type;
-	winid_t win;
+	Window *win;
 	glui32 val1, val2;
 };
 typedef event_struct event_t;
@@ -265,6 +265,11 @@ struct glkdate_struct {
 };
 typedef glkdate_struct glkdate_t;
 
+union gidispatch_rock_t {
+	glui32 num;
+	void *ptr;
+};
+
 #endif /* GLK_MODULE_DATETIME */
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 7462f7a..4a535af 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	events.o \
 	gargoyle.o \
 	glk.o \
+	windows.o \
 	scott/detection.o \
 	scott/scott.o
 
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 42a9088..9632408 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -864,6 +864,7 @@ int Scott::PerformLine(int ct) {
 			case 63:
 				doneit:				Output("The game is now over.\n");
 									glk_exit();
+									break;
 			case 64:
 				break;
 			case 65:
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
new file mode 100644
index 0000000..ad03ab8
--- /dev/null
+++ b/engines/gargoyle/windows.cpp
@@ -0,0 +1,244 @@
+/* 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 "gargoyle/windows.h"
+#include "common/algorithm.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+
+#define MAGIC_WINDOW_NUM (9876)
+
+Windows::Windows(Graphics::Screen *screen) : _screen(screen), _forceRedraw(true), _moreFocus(false),
+		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr) {
+}
+
+Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
+		glui32 wintype, glui32 rock) {
+	Window *newwin, *oldparent;
+	PairWindow *pairwin;
+	glui32 val;
+
+	_forceRedraw = true;
+
+	if (!_rootWin) {
+		if (splitwin) {
+			warning("window_open: ref must be NULL");
+			return nullptr;
+		}
+
+		/* ignore method and size now */
+		oldparent = NULL;
+	} else {
+		if (!splitwin) {
+			warning("window_open: ref must not be NULL");
+			return nullptr;
+		}
+
+		val = (method & winmethod_DivisionMask);
+		if (val != winmethod_Fixed && val != winmethod_Proportional)
+		{
+			warning("window_open: invalid method (not fixed or proportional)");
+			return nullptr;
+		}
+
+		val = (method & winmethod_DirMask);
+		if (val != winmethod_Above && val != winmethod_Below
+			&& val != winmethod_Left && val != winmethod_Right)
+		{
+			warning("window_open: invalid method (bad direction)");
+			return nullptr;
+		}
+
+		oldparent = splitwin->parent;
+		if (oldparent && oldparent->_type != wintype_Pair)
+		{
+			warning("window_open: parent window is not Pair");
+			return nullptr;
+		}
+	}
+
+	assert(wintype != wintype_Pair);
+	newwin = newWindow(wintype, rock);
+	if (!newwin) {
+		warning("window_open: unable to create window");
+		return nullptr;
+	}
+
+	if (!splitwin) {
+		_rootWin = newwin;
+	} else {
+		// create pairwin, with newwin as the key
+		pairwin = new PairWindow(method, newwin, size);
+		pairwin->child1 = splitwin;
+		pairwin->child2 = newwin;
+
+		splitwin->parent = pairwin;
+		newwin->parent = pairwin;
+		pairwin->parent = oldparent;
+
+		if (oldparent) {
+			PairWindow *parentWin = dynamic_cast<PairWindow *>(oldparent);
+			assert(parentWin);
+			if (parentWin->child1 == splitwin)
+				parentWin->child1 = pairwin;
+			else
+				parentWin->child2 = pairwin;
+		} else {
+			_rootWin = pairwin;
+		}
+	}
+
+	rearrange();
+
+	return newwin;
+}
+
+Window *Windows::newWindow(glui32 type, glui32 rock) {
+	Window *win;
+
+	switch (type) {
+	case wintype_Blank:
+		win = new BlankWindow(rock);
+		break;
+	case wintype_TextGrid:
+		win = new TextGridWindow(rock);
+		break;
+	case wintype_TextBuffer:
+		win = new TextBufferWindow(rock);
+		break;
+	case wintype_Graphics:
+		win = new GraphicsWindow(rock);
+		break;
+	case wintype_Pair:
+		error("Pair windows cannot be created directly");
+	default:
+		error("Unknown window type");
+	}
+
+	win->next = _windowList;
+	_windowList = win;
+	if (win->next)
+		win->next->prev = win;
+
+	return win;
+}
+
+PairWindow *Windows::newPairWindow(glui32 method, Window *key, glui32 size) {
+	PairWindow *pwin = new PairWindow(method, key, size);
+	pwin->next = _windowList;
+	_windowList = pwin;
+	if (pwin->next)
+		pwin->next->prev = pwin;
+
+	return pwin;
+}
+
+void Windows::rearrange() {
+	// TODO
+	/*
+	if (_rootWin) {
+		rect_t box;
+
+		if (gli_conf_lockcols) {
+			int desired_width = gli_wmarginx_save * 2 + gli_cellw * gli_cols;
+			if (desired_width > gli_image_w)
+				gli_wmarginx = gli_wmarginx_save;
+			else
+				gli_wmarginx = (gli_image_w - gli_cellw * gli_cols) / 2;
+		}
+
+		if (gli_conf_lockrows)
+		{
+			int desired_height = gli_wmarginy_save * 2 + gli_cellh * gli_rows;
+			if (desired_height > gli_image_h)
+				gli_wmarginy = gli_wmarginy_save;
+			else
+				gli_wmarginy = (gli_image_h - gli_cellh * gli_rows) / 2;
+		}
+
+		box.x0 = gli_wmarginx;
+		box.y0 = gli_wmarginy;
+		box.x1 = gli_image_w - gli_wmarginx;
+		box.y1 = gli_image_h - gli_wmarginy;
+		gli_window_rearrange(_rootWin, &box);
+	}
+	*/
+}
+
+/*--------------------------------------------------------------------------*/
+
+Window::Window(glui32 rock) : _magicnum(MAGIC_WINDOW_NUM), _rock(rock), _type(0),
+		parent(nullptr), next(nullptr), prev(nullptr),
+		yadj(0), line_request(0), line_request_uni(0), char_request(0), char_request_uni(0),
+		mouse_request(0), hyper_request(0), more_request(0), scroll_request(0), image_loaded(0),
+		echo_line_input(true),  line_terminators(nullptr), termct(0), str(nullptr), echostr(nullptr) {
+	attr.fgset = 0;
+	attr.bgset = 0;
+	attr.reverse = 0;
+	attr.style = 0;
+	attr.fgcolor = 0;
+	attr.bgcolor = 0;
+	attr.hyper = 0;
+
+	Common::fill(&bgcolor[0], &bgcolor[3], 3);
+	Common::fill(&fgcolor[0], &fgcolor[3], 3);
+	disprock.num = 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+BlankWindow::BlankWindow(uint32 rock) : Window(rock) {
+	_type = wintype_Blank;
+}
+
+/*--------------------------------------------------------------------------*/
+
+TextGridWindow::TextGridWindow(uint32 rock) : Window(rock) {
+	_type = wintype_TextGrid;
+}
+
+/*--------------------------------------------------------------------------*/
+
+TextBufferWindow::TextBufferWindow(uint32 rock) : Window(rock) {
+	_type = wintype_TextBuffer;
+}
+
+/*--------------------------------------------------------------------------*/
+
+GraphicsWindow::GraphicsWindow(uint32 rock) : Window(rock) {
+	_type = wintype_Graphics;
+}
+
+/*--------------------------------------------------------------------------*/
+
+PairWindow::PairWindow(glui32 method, Window *_key, glui32 _size) : Window(0),
+		dir(method & winmethod_DirMask),
+		division(method & winmethod_DivisionMask),
+		wborder((method & winmethod_BorderMask) == winmethod_Border),
+		vertical(dir == winmethod_Left || dir == winmethod_Right),
+		backward(dir == winmethod_Left || dir == winmethod_Above),
+		key(key), size(size), keydamage(0), child1(nullptr), child2(nullptr) {
+	_type = wintype_Pair;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
new file mode 100644
index 0000000..91cbc91
--- /dev/null
+++ b/engines/gargoyle/windows.h
@@ -0,0 +1,223 @@
+/* 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 GARGOYLE_WINDOWS_H
+#define GARGOYLE_WINDOWS_H
+
+#include "common/list.h"
+#include "common/rect.h"
+#include "common/stream.h"
+#include "graphics/screen.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+class Window;
+class PairWindow;
+
+class Windows {
+private:
+	Graphics::Screen *_screen;
+	Window * _windowList;      ///< List of all windows
+	Window *_rootWin;          ///< The topmost window
+	Window *_focusWin;         ///< The window selected by the player
+	bool _forceRedraw;
+	bool _moreFocus;
+private:
+	/**
+	 * Create a new window
+	 */
+	Window *newWindow(glui32 type, glui32 rock);
+
+	/**
+	 * Create a new pair window
+	 */
+	PairWindow *newPairWindow(glui32 method, Window *key, glui32 size);
+
+	/**
+	 * Rearrange windows
+	 */
+	void rearrange();
+public:
+	/**
+	 * Constructor
+	 */
+	Windows(Graphics::Screen *screen);
+
+	/**
+	 * Open a new window
+	 */
+	Window *windowOpen(Window *splitwin, glui32 method, glui32 size,
+		glui32 wintype, glui32 rock);
+
+	/**
+	 * Return the root window
+	 */
+	Window *getRoot() const { return _rootWin; }
+};
+
+/**
+ * Window attributes
+ */
+struct attr_t {
+    unsigned fgset   : 1;
+    unsigned bgset   : 1;
+    unsigned reverse : 1;
+    unsigned         : 1;
+    unsigned style   : 4;
+    unsigned fgcolor : 24;
+    unsigned bgcolor : 24;
+    unsigned hyper   : 32;
+};
+
+struct WindowPair {
+	Window *owner;
+	Window *child1, *child2;
+
+	// split info...
+	glui32 dir;             ///< winmethod_Left, Right, Above, or Below
+	int vertical, backward; ///< flags
+	glui32 division;        ///< winmethod_Fixed or winmethod_Proportional
+	Window *key;            ///< NULL or a leaf-descendant (not a Pair)
+	int keydamage;          ///< used as scratch space in window closing
+	glui32 size;            ///< size value
+	glui32 wborder;         ///< winMethod_Border, NoBorder
+};
+
+/**
+ * Window definition
+ */
+class Window {
+public:
+	glui32 _magicnum;
+	glui32 _rock;
+	glui32 _type;
+
+	Window *parent;               ///< pair window which contains this one
+	Common::Rect bbox;
+	int yadj;
+
+	Common::WriteStream *str;     ///< the window stream.
+	Common::WriteStream *echostr; ///< the window's echo stream, if any.
+
+	int line_request;
+	int line_request_uni;
+	int char_request;
+	int char_request_uni;
+	int mouse_request;
+	int hyper_request;
+	int more_request;
+	int scroll_request;
+	int image_loaded;
+
+	glui32 echo_line_input;
+	glui32 *line_terminators;
+	glui32 termct;
+
+	attr_t attr;
+	byte bgcolor[3];
+	byte fgcolor[3];
+
+	gidispatch_rock_t disprock;
+	Window *next, *prev;          ///< in the big linked list of windows
+public:
+	/**
+	 * Constructor
+	 */
+	Window(uint32 rock);
+
+	/**
+	 * Destructor
+	 */
+	virtual ~Window() {}
+};
+typedef Window *winid_t;
+
+/**
+ * Blank window
+ */
+class BlankWindow : public Window {
+public:
+	/**
+	 * Constructor
+	 */
+	BlankWindow(uint32 rock);
+};
+
+/**
+ * Text Grid window
+ */
+class TextGridWindow : public Window {
+public:
+	/**
+	 * Constructor
+	 */
+	TextGridWindow(uint32 rock);
+};
+
+/**
+ * Text Buffer window
+ */
+class TextBufferWindow : public Window {
+public:
+	/**
+	 * Constructor
+	 */
+	TextBufferWindow(uint32 rock);
+};
+
+/**
+ * Graphics window
+ */
+class GraphicsWindow : public Window {
+public:
+	/**
+	 * Constructor
+	 */
+	GraphicsWindow(uint32 rock);
+};
+
+/**
+ * Pair window
+ */
+class PairWindow : public Window {
+public:
+	Window *child1, *child2;
+
+	/* split info... */
+	glui32 dir;             ///< winmethod_Left, Right, Above, or Below
+	int vertical, backward; ///< flags
+	glui32 division;        ///< winmethod_Fixed or winmethod_Proportional
+	Window *key;            ///< NULL or a leaf-descendant (not a Pair)
+	int keydamage;          ///< used as scratch space in window closing
+	glui32 size;            ///< size value
+	glui32 wborder;         ///< winMethod_Border, NoBorder
+public:
+	/**
+	 * Constructor
+	 */
+	PairWindow(glui32 method, Window *_key, glui32 _size);
+};
+
+} // End of namespace Gargoyle
+
+#endif


Commit: 47a7a1d4e2640b3b34e2091be606b26723990efb
    https://github.com/scummvm/scummvm/commit/47a7a1d4e2640b3b34e2091be606b26723990efb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added setup of text grid windows

Changed paths:
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index ad03ab8..25af90b 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -28,8 +28,47 @@ namespace Gargoyle {
 
 #define MAGIC_WINDOW_NUM (9876)
 
+bool Windows::_confLockCols;
+bool Windows::_confLockRows;
+int Windows::_wMarginx;
+int Windows::_wMarginy;
+int Windows::_wPaddingx;
+int Windows::_wPaddingy;
+int Windows::_wBorderx;
+int Windows::_wBordery;
+int Windows::_tMarginx;
+int Windows::_tMarginy;
+int Windows::_wMarginXsave;
+int Windows::_wMarginYsave;
+int Windows::_cols;
+int Windows::_rows;
+int Windows::_imageW;
+int Windows::_imageH;
+int Windows::_cellW;
+int Windows::_cellH;
+int Windows::_baseLine;
+int Windows::_leading;
+
 Windows::Windows(Graphics::Screen *screen) : _screen(screen), _forceRedraw(true), _moreFocus(false),
 		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr) {
+	_confLockCols = false;
+	_confLockRows = false;
+	_wMarginx = 15;
+	_wMarginy = 15;
+	_wPaddingx = 0;
+	_wPaddingy = 0;
+	_wBorderx = 1;
+	_wBordery = 1;
+	_tMarginx = 7;
+	_tMarginy = 7;
+	_wMarginXsave = 15;
+	_wMarginYsave = 15;
+	_cols = 60;
+	_rows = 25;
+	_imageW = _imageH = 0;
+	_cellW = _cellH = 0;
+	_baseLine = 15;
+	_leading = 20;
 }
 
 Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
@@ -154,35 +193,32 @@ PairWindow *Windows::newPairWindow(glui32 method, Window *key, glui32 size) {
 }
 
 void Windows::rearrange() {
-	// TODO
-	/*
 	if (_rootWin) {
-		rect_t box;
+		Common::Rect box;
 
-		if (gli_conf_lockcols) {
-			int desired_width = gli_wmarginx_save * 2 + gli_cellw * gli_cols;
-			if (desired_width > gli_image_w)
-				gli_wmarginx = gli_wmarginx_save;
+		if (_confLockCols) {
+			int desired_width = _wMarginXsave * 2 + _cellW * _cols;
+			if (desired_width > _imageW)
+				_wMarginx = _wMarginXsave;
 			else
-				gli_wmarginx = (gli_image_w - gli_cellw * gli_cols) / 2;
+				_wMarginx = (_imageW - _cellW * _cols) / 2;
 		}
 
-		if (gli_conf_lockrows)
-		{
-			int desired_height = gli_wmarginy_save * 2 + gli_cellh * gli_rows;
-			if (desired_height > gli_image_h)
-				gli_wmarginy = gli_wmarginy_save;
+		if (_confLockRows) {
+			int desired_height = _wMarginYsave * 2 + _cellH * _rows;
+			if (desired_height > _imageH)
+				_wMarginy = _wMarginYsave;
 			else
-				gli_wmarginy = (gli_image_h - gli_cellh * gli_rows) / 2;
+				_wMarginy = (_imageH - _cellH * _rows) / 2;
 		}
 
-		box.x0 = gli_wmarginx;
-		box.y0 = gli_wmarginy;
-		box.x1 = gli_image_w - gli_wmarginx;
-		box.y1 = gli_image_h - gli_wmarginy;
-		gli_window_rearrange(_rootWin, &box);
+		box.left = _wMarginx;
+		box.top = _wMarginy;
+		box.right = _imageW - _wMarginx;
+		box.bottom = _imageH - _wMarginy;
+
+		_rootWin->rearrange(box);
 	}
-	*/
 }
 
 /*--------------------------------------------------------------------------*/
@@ -215,6 +251,50 @@ BlankWindow::BlankWindow(uint32 rock) : Window(rock) {
 
 TextGridWindow::TextGridWindow(uint32 rock) : Window(rock) {
 	_type = wintype_TextGrid;
+	width = height = 0;
+	curx = cury = 0;
+	inbuf = nullptr;
+	inorgx = inorgy = 0;
+	inmax = 0;
+	incurs = inlen = 0;
+	inarrayrock.num = 0;
+	line_terminators = nullptr;
+}
+
+void TextGridWindow::rearrange(const Common::Rect &box) {
+	Window::rearrange(box);
+	int newwid, newhgt;
+
+	newwid = box.width() / Windows::_cellW;
+	newhgt = box.height() / Windows::_cellH;
+
+	if (newwid == width && newhgt == height)
+		return;
+
+	lines.resize(newhgt);
+	for (int y = 0; y < newhgt; ++y) {
+		lines[y].resize(newwid);
+		touch(y);
+	}
+
+	attr.clear();
+	width = newwid;
+	height = newhgt;
+}
+
+void TextGridWindow::touch(int line) {
+//	int y = bbox.top + line * Windows::_leading;
+	lines[line].dirty = true;
+	// TODO
+//	winrepaint(bbox.left, y, bbox.right, y + Windows::_leading);
+}
+
+void TextGridWindow::TextGridRow::resize(size_t newSize) {
+	chars.clear();
+	attr.clear();
+	chars.resize(newSize);
+	attr.resize(newSize);
+	Common::fill(&chars[0], &chars[0] + newSize, ' ');
 }
 
 /*--------------------------------------------------------------------------*/
@@ -241,4 +321,23 @@ PairWindow::PairWindow(glui32 method, Window *_key, glui32 _size) : Window(0),
 	_type = wintype_Pair;
 }
 
+/*--------------------------------------------------------------------------*/
+
+WindowStyle::WindowStyle() : font(0), reverse(0) {
+	Common::fill(&bg[0], &bg[3], 0);
+	Common::fill(&fg[0], &fg[3], 0);
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Attributes::clear() {
+	fgset = 0;
+	bgset = 0;
+	fgcolor = 0;
+	bgcolor = 0;
+	reverse = false;
+	hyper = 0;
+	style = 0;
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 91cbc91..45907a8 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -23,6 +23,7 @@
 #ifndef GARGOYLE_WINDOWS_H
 #define GARGOYLE_WINDOWS_H
 
+#include "common/array.h"
 #include "common/list.h"
 #include "common/rect.h"
 #include "common/stream.h"
@@ -58,6 +59,24 @@ private:
 	 */
 	void rearrange();
 public:
+	static bool _confLockCols, _confLockRows;
+	static int _wMarginx;
+	static int _wMarginy;
+	static int _wPaddingx;
+	static int _wPaddingy;
+	static int _wBorderx;
+	static int _wBordery;
+	static int _tMarginx;
+	static int _tMarginy;
+	static int _wMarginXsave;
+	static int _wMarginYsave;
+	static int _cols;
+	static int _rows;
+	static int _imageW, _imageH;
+	static int _cellW, _cellH;
+	static int _baseLine;
+	static int _leading;
+public:
 	/**
 	 * Constructor
 	 */
@@ -76,9 +95,21 @@ public:
 };
 
 /**
+ * Window styles
+ */
+struct WindowStyle {
+	int font;
+	byte bg[3];
+	byte fg[3];
+	int reverse;
+
+	WindowStyle();
+};
+
+/**
  * Window attributes
  */
-struct attr_t {
+struct Attributes {
     unsigned fgset   : 1;
     unsigned bgset   : 1;
     unsigned reverse : 1;
@@ -87,20 +118,18 @@ struct attr_t {
     unsigned fgcolor : 24;
     unsigned bgcolor : 24;
     unsigned hyper   : 32;
-};
 
-struct WindowPair {
-	Window *owner;
-	Window *child1, *child2;
+	/**
+	 * Constructor
+	 */
+	Attributes() {
+		clear();
+	}
 
-	// split info...
-	glui32 dir;             ///< winmethod_Left, Right, Above, or Below
-	int vertical, backward; ///< flags
-	glui32 division;        ///< winmethod_Fixed or winmethod_Proportional
-	Window *key;            ///< NULL or a leaf-descendant (not a Pair)
-	int keydamage;          ///< used as scratch space in window closing
-	glui32 size;            ///< size value
-	glui32 wborder;         ///< winMethod_Border, NoBorder
+	/**
+	 * Clear
+	 */
+	void clear();
 };
 
 /**
@@ -133,7 +162,7 @@ public:
 	glui32 *line_terminators;
 	glui32 termct;
 
-	attr_t attr;
+	Attributes attr;
 	byte bgcolor[3];
 	byte fgcolor[3];
 
@@ -149,6 +178,11 @@ public:
 	 * Destructor
 	 */
 	virtual ~Window() {}
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box) { bbox = box; }
 };
 typedef Window *winid_t;
 
@@ -167,11 +201,56 @@ public:
  * Text Grid window
  */
 class TextGridWindow : public Window {
+	/**
+	 * Structure for a row within the grid window
+	 */
+	struct TextGridRow {
+		Common::Array<uint32> chars;
+		Common::Array<Attributes> attr;
+		bool dirty;
+
+		/**
+		 * Constructor
+		 */
+		TextGridRow() : dirty(false) {}
+
+		/**
+		 * Resize the row
+		 */
+		void resize(size_t newSize);
+	};
+	typedef Common::Array<TextGridRow> TextGridRows;
+private:
+	/**
+	 * Mark a given text row as modified
+	 */
+	void touch(int line);
+public:
+	int width, height;
+	TextGridRows lines;
+
+	int curx, cury;     ///< the window cursor position
+
+                        ///< for line input
+	void *inbuf;        ///< unsigned char* for latin1, glui32* for unicode
+	int inorgx, inorgy;
+	int inmax;
+	int incurs, inlen;
+	Attributes origattr;
+	gidispatch_rock_t inarrayrock;
+	glui32 *line_terminators;
+
+	WindowStyle styles[style_NUMSTYLES]; ///< style hints and settings
 public:
 	/**
 	 * Constructor
 	 */
 	TextGridWindow(uint32 rock);
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box);
 };
 
 /**


Commit: 89102c42cac1bd2367a6322f07286452bab21e11
    https://github.com/scummvm/scummvm/commit/89102c42cac1bd2367a6322f07286452bab21e11
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Window setup for text buffer windows

Changed paths:
    engines/gargoyle/glk_types.h
    engines/gargoyle/module.mk
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 040d666..82f70e3 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -219,6 +219,10 @@ enum StyleHint {
 	stylehint_just_RightFlush = 3,
 };
 
+enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
+enum TYPES { MONOF, PROPF };
+enum STYLES { FONTR, FONTB, FONTI, FONTZ };
+
 #ifdef GLK_MODULE_IMAGE
 
 enum ImageAlign {
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 4a535af..e5542a7 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	events.o \
 	gargoyle.o \
 	glk.o \
+	picture.o \
 	windows.o \
 	scott/detection.o \
 	scott/scott.o
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 25af90b..194002b 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -27,6 +27,7 @@
 namespace Gargoyle {
 
 #define MAGIC_WINDOW_NUM (9876)
+#define GLI_SUBPIX 8
 
 bool Windows::_confLockCols;
 bool Windows::_confLockRows;
@@ -48,9 +49,47 @@ int Windows::_cellW;
 int Windows::_cellH;
 int Windows::_baseLine;
 int Windows::_leading;
+int Windows::_scrollWidth;
+bool Windows::_overrideReverse;
+bool Windows::_overrideFgSet;
+bool Windows::_overrideBgSet;
+int Windows::_overrideFgVal;
+int Windows::_overrideBgVal;
+
+
+WindowStyle T_STYLES[style_NUMSTYLES] = {
+	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Normal
+	{ PROPI,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Emphasized
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Preformatted
+	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Header
+	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Subheader
+	{ PROPZ,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
+	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
+	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
+	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Input
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User1
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User2
+};
+
+WindowStyle G_STYLES[style_NUMSTYLES] = {
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Normal
+	{ MONOI,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Emphasized
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Preformatted
+	{ MONOB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Header
+	{ MONOB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Subheader
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Input
+	{ MONOR,{ 0x60,0x60,0x60 },{ 0xff,0xff,0xff }, 0 }, ///< User1
+	{ MONOR,{ 0x60,0x60,0x60 },{ 0xff,0xff,0xff }, 0 }, ///< User2
+};
+
+/*--------------------------------------------------------------------------*/
 
 Windows::Windows(Graphics::Screen *screen) : _screen(screen), _forceRedraw(true), _moreFocus(false),
-		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr) {
+		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr), _mask(nullptr),
+		_claimSelect(0) {
 	_confLockCols = false;
 	_confLockRows = false;
 	_wMarginx = 15;
@@ -69,6 +108,12 @@ Windows::Windows(Graphics::Screen *screen) : _screen(screen), _forceRedraw(true)
 	_cellW = _cellH = 0;
 	_baseLine = 15;
 	_leading = 20;
+	_scrollWidth = 0;
+	_overrideReverse = false;
+	_overrideFgSet = false;
+	_overrideBgSet = false;
+	_overrideFgVal = 0;
+	_overrideBgVal = 0;
 }
 
 Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
@@ -127,7 +172,7 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 		_rootWin = newwin;
 	} else {
 		// create pairwin, with newwin as the key
-		pairwin = new PairWindow(method, newwin, size);
+		pairwin = newPairWindow(method, newwin, size);
 		pairwin->child1 = splitwin;
 		pairwin->child2 = newwin;
 
@@ -157,16 +202,16 @@ Window *Windows::newWindow(glui32 type, glui32 rock) {
 
 	switch (type) {
 	case wintype_Blank:
-		win = new BlankWindow(rock);
+		win = new BlankWindow(this, rock);
 		break;
 	case wintype_TextGrid:
-		win = new TextGridWindow(rock);
+		win = new TextGridWindow(this, rock);
 		break;
 	case wintype_TextBuffer:
-		win = new TextBufferWindow(rock);
+		win = new TextBufferWindow(this, rock);
 		break;
 	case wintype_Graphics:
-		win = new GraphicsWindow(rock);
+		win = new GraphicsWindow(this, rock);
 		break;
 	case wintype_Pair:
 		error("Pair windows cannot be created directly");
@@ -183,7 +228,7 @@ Window *Windows::newWindow(glui32 type, glui32 rock) {
 }
 
 PairWindow *Windows::newPairWindow(glui32 method, Window *key, glui32 size) {
-	PairWindow *pwin = new PairWindow(method, key, size);
+	PairWindow *pwin = new PairWindow(this, method, key, size);
 	pwin->next = _windowList;
 	_windowList = pwin;
 	if (pwin->next)
@@ -221,10 +266,24 @@ void Windows::rearrange() {
 	}
 }
 
+void Windows::clearSelection() {
+	if (!_mask) {
+		warning("clear_selection: mask not initialized");
+		return;
+	}
+
+	if (_mask->select.left || _mask->select.right
+		|| _mask->select.top || _mask->select.bottom)
+		_forceRedraw = true;
+
+	_mask->select = Common::Rect();
+	_claimSelect = false;
+}
+
 /*--------------------------------------------------------------------------*/
 
-Window::Window(glui32 rock) : _magicnum(MAGIC_WINDOW_NUM), _rock(rock), _type(0),
-		parent(nullptr), next(nullptr), prev(nullptr),
+Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
+		_windows(windows), _rock(rock), _type(0), parent(nullptr), next(nullptr), prev(nullptr),
 		yadj(0), line_request(0), line_request_uni(0), char_request(0), char_request_uni(0),
 		mouse_request(0), hyper_request(0), more_request(0), scroll_request(0), image_loaded(0),
 		echo_line_input(true),  line_terminators(nullptr), termct(0), str(nullptr), echostr(nullptr) {
@@ -243,13 +302,13 @@ Window::Window(glui32 rock) : _magicnum(MAGIC_WINDOW_NUM), _rock(rock), _type(0)
 
 /*--------------------------------------------------------------------------*/
 
-BlankWindow::BlankWindow(uint32 rock) : Window(rock) {
+BlankWindow::BlankWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
 	_type = wintype_Blank;
 }
 
 /*--------------------------------------------------------------------------*/
 
-TextGridWindow::TextGridWindow(uint32 rock) : Window(rock) {
+TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
 	_type = wintype_TextGrid;
 	width = height = 0;
 	curx = cury = 0;
@@ -259,6 +318,8 @@ TextGridWindow::TextGridWindow(uint32 rock) : Window(rock) {
 	incurs = inlen = 0;
 	inarrayrock.num = 0;
 	line_terminators = nullptr;
+
+	Common::copy(&G_STYLES[0], &G_STYLES[style_NUMSTYLES], styles);
 }
 
 void TextGridWindow::rearrange(const Common::Rect &box) {
@@ -289,6 +350,8 @@ void TextGridWindow::touch(int line) {
 //	winrepaint(bbox.left, y, bbox.right, y + Windows::_leading);
 }
 
+/*--------------------------------------------------------------------------*/
+
 void TextGridWindow::TextGridRow::resize(size_t newSize) {
 	chars.clear();
 	attr.clear();
@@ -299,19 +362,452 @@ void TextGridWindow::TextGridRow::resize(size_t newSize) {
 
 /*--------------------------------------------------------------------------*/
 
-TextBufferWindow::TextBufferWindow(uint32 rock) : Window(rock) {
+TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windows, rock),
+		historypos(0), historyfirst(0), historypresent(0), lastseen(0), scrollpos(0),
+		scrollmax(0), scrollback(SCROLLBACK), width(-1), height(-1), inbuf(nullptr),
+		line_terminators(nullptr), echo_line_input(true), ladjw(0), radjw(0), ladjn(0),
+		radjn(0), numchars(0), chars(nullptr), attrs(nullptr),
+		spaced(0), dashed(0), copybuf(0), copypos(0) {
 	_type = wintype_TextBuffer;
+	Common::fill(&history[0], &history[HISTORYLEN], nullptr);
+
+	Common::copy(&T_STYLES[0], &T_STYLES[style_NUMSTYLES], styles);
+}
+
+void TextBufferWindow::rearrange(const Common::Rect &box) {
+	Window::rearrange(box);
+	int newwid, newhgt;
+	int rnd;
+
+	newwid = (box.width() - Windows::_tMarginx * 2 - Windows::_scrollWidth) / Windows::_cellW;
+	newhgt = (box.height() - Windows::_tMarginy * 2) / Windows::_cellH;
+
+	/* align text with bottom */
+	rnd = newhgt * Windows::_cellH + Windows::_tMarginy * 2;
+	yadj = (box.height() - rnd);
+	bbox.top += (box.height() - rnd);
+
+	if (newwid != width) {
+		width = newwid;
+		reflow();
+	}
+
+	if (newhgt != height) {
+		/* scroll up if we obscure new lines */
+		if (lastseen >= newhgt - 1)
+			scrollpos += (height - newhgt);
+
+		height = newhgt;
+
+		/* keep window within 'valid' lines */
+		if (scrollpos > scrollmax - height + 1)
+			scrollpos = scrollmax - height + 1;
+		if (scrollpos < 0)
+			scrollpos = 0;
+		touchScroll();
+
+		/* allocate copy buffer */
+		if (copybuf)
+			delete[] copybuf;
+		copybuf = new glui32[height * TBLINELEN];
+
+		for (int i = 0; i < (height * TBLINELEN); i++)
+			copybuf[i] = 0;
+
+		copypos = 0;
+	}
+}
+
+void TextBufferWindow::reflow() {
+	int inputbyte = -1;
+	Attributes curattr, oldattr;
+	int i, k, p, s;
+	int x;
+
+	if (height < 4 || width < 20)
+		return;
+
+	lines[0].len = numchars;
+
+	/* allocate temp buffers */
+	Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN];
+	glui32 *charbuf = new glui32[SCROLLBACK * TBLINELEN];
+	int *alignbuf = new int[SCROLLBACK];
+	Picture **pictbuf = new Picture *[SCROLLBACK];
+	glui32 *hyperbuf = new glui32[SCROLLBACK];
+	int *offsetbuf = new int[SCROLLBACK];
+
+	if (!attrbuf || !charbuf || !alignbuf || !pictbuf || !hyperbuf || !offsetbuf) {
+		delete[] attrbuf;
+		delete[] charbuf;
+		delete[] alignbuf;
+		delete[] pictbuf;
+		delete[] hyperbuf;
+		delete[] offsetbuf;
+		return;
+	}
+
+	/* copy text to temp buffers */
+
+	oldattr = attr;
+	curattr.clear();
+
+	x = 0;
+	p = 0;
+	s = scrollmax < SCROLLBACK ? scrollmax : SCROLLBACK - 1;
+
+	for (k = s; k >= 0; k--) {
+		if (k == 0 && line_request)
+			inputbyte = p + infence;
+
+		if (lines[k].lpic) {
+			offsetbuf[x] = p;
+			alignbuf[x] = imagealign_MarginLeft;
+			pictbuf[x] = lines[k].lpic;
+
+			if (pictbuf[x]) pictbuf[x]->increment();
+			hyperbuf[x] = lines[k].lhyper;
+			x++;
+		}
+
+		if (lines[k].rpic) {
+			offsetbuf[x] = p;
+			alignbuf[x] = imagealign_MarginRight;
+			pictbuf[x] = lines[k].rpic;
+			if (pictbuf[x]) pictbuf[x]->increment();
+			hyperbuf[x] = lines[k].rhyper;
+			x++;
+		}
+
+		for (i = 0; i < lines[k].len; i++) {
+			attrbuf[p] = curattr = lines[k].attr[i];
+			charbuf[p] = lines[k].chars[i];
+			p++;
+		}
+
+		if (lines[k].newline) {
+			attrbuf[p] = curattr;
+			charbuf[p] = '\n';
+			p++;
+		}
+	}
+
+	offsetbuf[x] = -1;
+
+	/* clear window */
+
+	clear();
+
+	/* and dump text back */
+
+	x = 0;
+	for (i = 0; i < p; i++) {
+		if (i == inputbyte)
+			break;
+		attr = attrbuf[i];
+
+		if (offsetbuf[x] == i) {
+			putPicture(pictbuf[x], alignbuf[x], hyperbuf[x]);
+			x++;
+		}
+
+		putCharUni(charbuf[i]);
+	}
+
+	/* terribly sorry about this... */
+	lastseen = 0;
+	scrollpos = 0;
+
+	if (inputbyte != -1) {
+		infence = numchars;
+		putTextUnit(charbuf + inputbyte, p - inputbyte, numchars, 0);
+		incurs = numchars;
+	}
+
+	// free temp buffers
+	delete[] attrbuf;
+	delete[] charbuf;
+	delete[] alignbuf;
+	delete[] pictbuf;
+	delete[] hyperbuf;
+	delete[] offsetbuf;
+
+	attr = oldattr;
+
+	touchScroll();
+}
+
+void TextBufferWindow::touchScroll() {
+	_windows->clearSelection();
+
+	// TODO
+	//winrepaint(win->bbox.left, win->bbox.top, win->bbox.right, win->bbox.bottom);
+	for (int i = 0; i < scrollmax; i++)
+		lines[i].dirty = true;
+}
+
+void TextBufferWindow::clear() {
+	int i;
+
+	attr.fgset = Windows::_overrideFgSet;
+	attr.bgset = Windows::_overrideBgSet;
+	attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
+	attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
+	attr.reverse = false;
+
+	ladjw = radjw = 0;
+	ladjn = radjn = 0;
+
+	spaced = 0;
+	dashed = 0;
+
+	numchars = 0;
+
+	for (i = 0; i < scrollback; i++) {
+		lines[i].len = 0;
+
+		if (lines[i].lpic) lines[i].lpic->decrement();
+		lines[i].lpic = nullptr;
+		if (lines[i].rpic) lines[i].rpic->decrement();
+		lines[i].rpic = nullptr;
+
+		lines[i].lhyper = 0;
+		lines[i].rhyper = 0;
+		lines[i].lm = 0;
+		lines[i].rm = 0;
+		lines[i].newline = 0;
+		lines[i].dirty = 1;
+		lines[i].repaint = 0;
+	}
+
+	lastseen = 0;
+	scrollpos = 0;
+	scrollmax = 0;
+
+	for (i = 0; i < height; i++)
+		touch(i);
+}
+
+bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
+	if (align == imagealign_MarginRight)
+	{
+		if (lines[0].rpic || numchars)
+			return false;
+
+		radjw = (pic->w + Windows::_tMarginx) * GLI_SUBPIX;
+		radjn = (pic->h + Windows::_cellH - 1) / Windows::_cellH;
+		lines[0].rpic = pic;
+		lines[0].rm = radjw;
+		lines[0].rhyper = linkval;
+	} else {
+		if (align != imagealign_MarginLeft && numchars)
+			putCharUni('\n');
+
+		if (lines[0].lpic || numchars)
+			return false;
+
+		ladjw = (pic->w + Windows::_tMarginx) * GLI_SUBPIX;
+		ladjn = (pic->h + Windows::_cellH - 1) / Windows::_cellH;
+		lines[0].lpic = pic;
+		lines[0].lm = ladjw;
+		lines[0].lhyper = linkval;
+
+		if (align != imagealign_MarginLeft)
+			flowBreak();
+	}
+
+	return true ;
+}
+
+void TextBufferWindow::putCharUni(glui32 ch) {
+	/*
+	glui32 bchars[TBLINELEN];
+	Attributes battrs[TBLINELEN];
+	int pw;
+	int bpoint;
+	int saved;
+	int i;
+	int linelen;
+	unsigned char *color;
+
+	gli_tts_speak(&ch, 1);
+
+	pw = (win->bbox.x1 - win->bbox.x0 - Windows::_tMarginx * 2 - gli_scroll_width) * GLI_SUBPIX;
+	pw = pw - 2 * SLOP - radjw - ladjw;
+
+	color = gli_override_bg_set ? gli_window_color : win->bgcolor;
+
+	// oops ... overflow
+	if (numchars + 1 >= TBLINELEN)
+		scrolloneline(dwin, 0);
+
+	if (ch == '\n') {
+		scrolloneline(dwin, 1);
+		return;
+	}
+
+	if (gli_conf_quotes) {
+		// fails for 'tis a wonderful day in the '80s
+		if (gli_conf_quotes > 1 && ch == '\'')
+		{
+			if (numchars == 0 || leftquote(chars[numchars - 1]))
+				ch = UNI_LSQUO;
+		}
+
+		if (ch == '`')
+			ch = UNI_LSQUO;
+
+		if (ch == '\'')
+			ch = UNI_RSQUO;
+
+		if (ch == '"')
+		{
+			if (numchars == 0 || leftquote(chars[numchars - 1]))
+				ch = UNI_LDQUO;
+			else
+				ch = UNI_RDQUO;
+		}
+	}
+
+	if (gli_conf_dashes && win->attr.style != style_Preformatted)
+	{
+		if (ch == '-')
+		{
+			dashed++;
+			if (dashed == 2)
+			{
+				numchars--;
+				if (gli_conf_dashes == 2)
+					ch = UNI_NDASH;
+				else
+					ch = UNI_MDASH;
+			}
+			if (dashed == 3)
+			{
+				numchars--;
+				ch = UNI_MDASH;
+				dashed = 0;
+			}
+		}
+		else
+			dashed = 0;
+	}
+
+	if (gli_conf_spaces && win->attr.style != style_Preformatted
+		&& styles[win->attr.style].bg == color
+		&& !styles[win->attr.style].reverse)
+	{
+		// turn (period space space) into (period space)
+		if (gli_conf_spaces == 1)
+		{
+			if (ch == '.')
+				spaced = 1;
+			else if (ch == ' ' && spaced == 1)
+				spaced = 2;
+			else if (ch == ' ' && spaced == 2)
+			{
+				spaced = 0;
+				return;
+			}
+			else
+				spaced = 0;
+		}
+
+		// Turn (per sp x) into (per sp sp x)
+		if (gli_conf_spaces == 2)
+		{
+			if (ch == '.')
+				spaced = 1;
+			else if (ch == ' ' && spaced == 1)
+				spaced = 2;
+			else if (ch != ' ' && spaced == 2)
+			{
+				spaced = 0;
+				win_textbuffer_putchar_uni(win, ' ');
+			}
+			else
+				spaced = 0;
+		}
+	}
+
+	chars[numchars] = ch;
+	attrs[numchars] = win->attr;
+	numchars++;
+
+	// kill spaces at the end for line width calculation
+	linelen = numchars;
+	while (linelen > 1 && chars[linelen - 1] == ' '
+		&& styles[attrs[linelen - 1].style].bg == color
+		&& !styles[attrs[linelen - 1].style].reverse)
+		linelen--;
+
+	if (calcwidth(dwin, chars, attrs, 0, linelen, -1) >= pw)
+	{
+		bpoint = numchars;
+
+		for (i = numchars - 1; i > 0; i--)
+			if (chars[i] == ' ')
+			{
+				bpoint = i + 1; // skip space
+				break;
+			}
+
+		saved = numchars - bpoint;
+
+		memcpy(bchars, chars + bpoint, saved * 4);
+		memcpy(battrs, attrs + bpoint, saved * sizeof(attr_t));
+		numchars = bpoint;
+
+		scrolloneline(dwin, 0);
+
+		memcpy(chars, bchars, saved * 4);
+		memcpy(attrs, battrs, saved * sizeof(attr_t));
+		numchars = saved;
+	}
+
+	touch(0);
+	*/
+}
+
+void TextBufferWindow::putTextUnit(const glui32 *buf, int len, int pos, int oldlen) {
+	// TODO
+}
+
+void TextBufferWindow::flowBreak() {
+	// TODO
+}
+
+void TextBufferWindow::touch(int line) {
+//	int y = bbox.top + Windows::_tMarginy + (height - line - 1) * Windows::_leading;
+	lines[line].dirty = 1;
+	_windows->clearSelection();
+	//winrepaint(bbox.left, y - 2, bbox.right, y + Windows::_leading + 2);
 }
 
 /*--------------------------------------------------------------------------*/
 
-GraphicsWindow::GraphicsWindow(uint32 rock) : Window(rock) {
+TextBufferWindow::TextBufferRow::TextBufferRow() : len(0), newline(0), dirty(false), repaint(false),
+		lpic(nullptr), rpic(nullptr), lhyper(0), rhyper(0), lm(0), rm(0) {
+}
+
+void TextBufferWindow::TextBufferRow::resize(size_t newSize) {
+	chars.clear();
+	attr.clear();
+	chars.resize(newSize);
+	attr.resize(newSize);
+	Common::fill(&chars[0], &chars[0] + newSize, ' ');
+}
+
+/*--------------------------------------------------------------------------*/
+
+GraphicsWindow::GraphicsWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
 	_type = wintype_Graphics;
 }
 
 /*--------------------------------------------------------------------------*/
 
-PairWindow::PairWindow(glui32 method, Window *_key, glui32 _size) : Window(0),
+PairWindow::PairWindow(Windows *windows, glui32 method, Window *_key, glui32 _size) :
+		Window(windows, 0),
 		dir(method & winmethod_DirMask),
 		division(method & winmethod_DivisionMask),
 		wborder((method & winmethod_BorderMask) == winmethod_Border),
@@ -323,13 +819,6 @@ PairWindow::PairWindow(glui32 method, Window *_key, glui32 _size) : Window(0),
 
 /*--------------------------------------------------------------------------*/
 
-WindowStyle::WindowStyle() : font(0), reverse(0) {
-	Common::fill(&bg[0], &bg[3], 0);
-	Common::fill(&fg[0], &fg[3], 0);
-}
-
-/*--------------------------------------------------------------------------*/
-
 void Attributes::clear() {
 	fgset = 0;
 	bgset = 0;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 45907a8..aad4969 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -29,12 +29,21 @@
 #include "common/stream.h"
 #include "graphics/screen.h"
 #include "gargoyle/glk_types.h"
+#include "gargoyle/picture.h"
 
 namespace Gargoyle {
 
 class Window;
 class PairWindow;
+struct WindowMask;
 
+#define HISTORYLEN 100
+#define SCROLLBACK 512
+#define TBLINELEN 300
+
+/**
+ * Main windows manager
+ */
 class Windows {
 private:
 	Graphics::Screen *_screen;
@@ -43,6 +52,8 @@ private:
 	Window *_focusWin;         ///< The window selected by the player
 	bool _forceRedraw;
 	bool _moreFocus;
+	bool _claimSelect;
+	WindowMask *_mask;
 private:
 	/**
 	 * Create a new window
@@ -76,6 +87,12 @@ public:
 	static int _cellW, _cellH;
 	static int _baseLine;
 	static int _leading;
+	static int _scrollWidth;
+	static bool _overrideReverse;
+	static bool _overrideFgSet;
+	static bool _overrideBgSet;
+	static int _overrideFgVal;
+	static int _overrideBgVal;
 public:
 	/**
 	 * Constructor
@@ -92,6 +109,8 @@ public:
 	 * Return the root window
 	 */
 	Window *getRoot() const { return _rootWin; }
+
+	void clearSelection();
 };
 
 /**
@@ -102,8 +121,6 @@ struct WindowStyle {
 	byte bg[3];
 	byte fg[3];
 	int reverse;
-
-	WindowStyle();
 };
 
 /**
@@ -132,11 +149,21 @@ struct Attributes {
 	void clear();
 };
 
+struct WindowMask {
+	int hor;
+	int ver;
+	glui32 **links;
+	Common::Rect select;
+
+	WindowMask() : hor(0), ver(0), links(nullptr) {}
+};
+
 /**
  * Window definition
  */
 class Window {
 public:
+	Windows *_windows;
 	glui32 _magicnum;
 	glui32 _rock;
 	glui32 _type;
@@ -172,7 +199,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Window(uint32 rock);
+	Window(Windows *windows, uint32 rock);
 
 	/**
 	 * Destructor
@@ -194,7 +221,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	BlankWindow(uint32 rock);
+	BlankWindow(Windows *windows, uint32 rock);
 };
 
 /**
@@ -245,7 +272,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	TextGridWindow(uint32 rock);
+	TextGridWindow(Windows *windows, uint32 rock);
 
 	/**
 	 * Rearranges the window
@@ -257,11 +284,101 @@ public:
  * Text Buffer window
  */
 class TextBufferWindow : public Window {
+	/**
+	 * Structure for a row within the window
+	 */
+	struct TextBufferRow {
+		Common::Array<uint32> chars;
+		Common::Array<Attributes> attr;
+		int len, newline;
+		bool dirty, repaint;
+		Picture *lpic, *rpic;
+		glui32 lhyper, rhyper;
+		int lm, rm;
+
+		/**
+		 * Constructor
+		 */
+		TextBufferRow();
+
+		/**
+		 * Resize the row
+		 */
+		void resize(size_t newSize);
+	};
+	typedef Common::Array<TextBufferRow> TextBufferRows;
+private:
+	void reflow();
+	void touchScroll();
+	bool putPicture(Picture *pic, glui32 align, glui32 linkval);
+	void putCharUni(glui32 ch);
+	void putTextUnit(const glui32 *buf, int len, int pos, int oldlen);
+	void flowBreak();
+
+	/**
+	 * Mark a given text row as modified
+	 */
+	void touch(int line);
+public:
+	int width, height;
+	int spaced;
+	int dashed;
+
+	TextBufferRows lines;
+	int scrollback;
+
+	int numchars;       ///< number of chars in last line: lines[0]
+	glui32 *chars;      ///< alias to lines[0].chars
+	Attributes *attrs;  ///< alias to lines[0].attrs
+
+						///< adjust margins temporarily for images
+	int ladjw;
+	int ladjn;
+	int radjw;
+	int radjn;
+
+	/* Command history. */
+	glui32 *history[HISTORYLEN];
+	int historypos;
+	int historyfirst, historypresent;
+
+	/* for paging */
+	int lastseen;
+	int scrollpos;
+	int scrollmax;
+
+	/* for line input */
+	void *inbuf;        ///< unsigned char* for latin1, glui32* for unicode
+	int inmax;
+	long infence;
+	long incurs;
+	Attributes origattr;
+	gidispatch_rock_t inarrayrock;
+
+	glui32 echo_line_input;
+	glui32 *line_terminators;
+
+	/* style hints and settings */
+	WindowStyle styles[style_NUMSTYLES];
+
+	/* for copy selection */
+	glui32 *copybuf;
+	int copypos;
 public:
 	/**
 	 * Constructor
 	 */
-	TextBufferWindow(uint32 rock);
+	TextBufferWindow(Windows *windows, uint32 rock);
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box);
+
+	/**
+	 * Clear the window
+	 */
+	void clear();
 };
 
 /**
@@ -272,7 +389,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	GraphicsWindow(uint32 rock);
+	GraphicsWindow(Windows *windows, uint32 rock);
 };
 
 /**
@@ -294,7 +411,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	PairWindow(glui32 method, Window *_key, glui32 _size);
+	PairWindow(Windows *windows, glui32 method, Window *_key, glui32 _size);
 };
 
 } // End of namespace Gargoyle


Commit: b9bafba382a9756cbedbb2008086208e24834139
    https://github.com/scummvm/scummvm/commit/b9bafba382a9756cbedbb2008086208e24834139
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Remaining window rearrange code

Changed paths:
  A engines/gargoyle/picture.cpp
  A engines/gargoyle/picture.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/picture.cpp b/engines/gargoyle/picture.cpp
new file mode 100644
index 0000000..4b0b892
--- /dev/null
+++ b/engines/gargoyle/picture.cpp
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 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 "gargoyle/picture.h"
+
+namespace Gargoyle {
+
+void Picture::increment() {
+	++_refCount;
+}
+
+void Picture::decrement() {
+	if (_refCount > 0 && --_refCount == 0) {
+		free();
+		delete this;
+	}
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/picture.h b/engines/gargoyle/picture.h
new file mode 100644
index 0000000..38f776c
--- /dev/null
+++ b/engines/gargoyle/picture.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_PICTURE_H
+#define GARGOYLE_PICTURE_H
+
+#include "graphics/surface.h"
+
+namespace Gargoyle {
+
+struct Picture : Graphics::Surface {
+	int _refCount;
+	uint32 _id;
+	bool _scaled;
+
+	/**
+	 * Constructor
+	 */
+	Picture() : Graphics::Surface(), _refCount(0), _id(0), _scaled(0) {}
+
+	/**
+	 * Increment reference counter
+	 */
+	void increment();
+
+	/**
+	 * Decrement reference counter
+	 */
+	void decrement();
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 194002b..8dc546a 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -541,7 +541,7 @@ void TextBufferWindow::touchScroll() {
 	_windows->clearSelection();
 
 	// TODO
-	//winrepaint(win->bbox.left, win->bbox.top, win->bbox.right, win->bbox.bottom);
+	//winrepaint(bbox.left, bbox.top, bbox.right, bbox.bottom);
 	for (int i = 0; i < scrollmax; i++)
 		lines[i].dirty = true;
 }
@@ -632,10 +632,10 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 
 	gli_tts_speak(&ch, 1);
 
-	pw = (win->bbox.x1 - win->bbox.x0 - Windows::_tMarginx * 2 - gli_scroll_width) * GLI_SUBPIX;
+	pw = (bbox.right - bbox.left - Windows::_tMarginx * 2 - gli_scroll_width) * GLI_SUBPIX;
 	pw = pw - 2 * SLOP - radjw - ladjw;
 
-	color = gli_override_bg_set ? gli_window_color : win->bgcolor;
+	color = gli_override_bg_set ? gli_window_color : bgcolor;
 
 	// oops ... overflow
 	if (numchars + 1 >= TBLINELEN)
@@ -669,7 +669,7 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 		}
 	}
 
-	if (gli_conf_dashes && win->attr.style != style_Preformatted)
+	if (gli_conf_dashes && attr.style != style_Preformatted)
 	{
 		if (ch == '-')
 		{
@@ -693,9 +693,9 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 			dashed = 0;
 	}
 
-	if (gli_conf_spaces && win->attr.style != style_Preformatted
-		&& styles[win->attr.style].bg == color
-		&& !styles[win->attr.style].reverse)
+	if (gli_conf_spaces && attr.style != style_Preformatted
+		&& styles[attr.style].bg == color
+		&& !styles[attr.style].reverse)
 	{
 		// turn (period space space) into (period space)
 		if (gli_conf_spaces == 1)
@@ -731,7 +731,7 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 	}
 
 	chars[numchars] = ch;
-	attrs[numchars] = win->attr;
+	attrs[numchars] = attr;
 	numchars++;
 
 	// kill spaces at the end for line width calculation
@@ -800,8 +800,58 @@ void TextBufferWindow::TextBufferRow::resize(size_t newSize) {
 
 /*--------------------------------------------------------------------------*/
 
-GraphicsWindow::GraphicsWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
+GraphicsWindow::GraphicsWindow(Windows *windows, uint32 rock) : Window(windows, rock),
+		w(0), h(0), dirty(false), _surface(nullptr) {
 	_type = wintype_Graphics;
+	Common::copy(&bgcolor[0], &bgcolor[3], bgnd);
+}
+
+void GraphicsWindow::rearrange(const Common::Rect &box) {
+	int newwid, newhgt;
+	int bothwid, bothhgt;
+	int oldw, oldh;
+	Graphics::ManagedSurface *newSurface;
+
+	bbox = box;
+
+	newwid = box.width();
+	newhgt = box.height();
+	oldw = w;
+	oldh = h;
+
+	if (newwid <= 0 || newhgt <= 0) {
+		w = 0;
+		h = 0;
+		delete _surface;
+		_surface = NULL;
+		return;
+	}
+
+	bothwid = w;
+	if (newwid < bothwid)
+		bothwid = newwid;
+	bothhgt = h;
+	if (newhgt < bothhgt)
+		bothhgt = newhgt;
+
+	newSurface = new Graphics::ManagedSurface(newwid, newhgt,
+		Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0));
+
+	// If the new surface is equal or bigger than the old one, copy it over
+	if (_surface && bothwid && bothhgt)
+		newSurface->blitFrom(*_surface);
+
+	delete _surface;
+	_surface = newSurface;
+	w = newwid;
+	h = newhgt;
+
+	touch();
+}
+
+void GraphicsWindow::touch() {
+	dirty = true;
+//	winrepaint(bbox.left, bbox.top, bbox.right, bbox.bottom);
 }
 
 /*--------------------------------------------------------------------------*/
@@ -817,6 +867,90 @@ PairWindow::PairWindow(Windows *windows, glui32 method, Window *_key, glui32 _si
 	_type = wintype_Pair;
 }
 
+void PairWindow::rearrange(const Common::Rect &box) {
+	Common::Rect box1, box2;
+	int min, diff, split, splitwid, max;
+	Window *keyWin;
+	Window *ch1, *ch2;
+
+	bbox = box;
+
+	if (vertical) {
+		min = bbox.left;
+		max = bbox.right;
+	} else {
+		min = bbox.top;
+		max = bbox.bottom;
+	}
+	diff = max - min;
+
+	// We now figure split.
+	if (vertical)
+		splitwid = Windows::_wPaddingx; // want border?
+	else
+		splitwid = Windows::_wPaddingy; // want border?
+
+	switch (division) {
+	case winmethod_Proportional:
+		split = (diff * size) / 100;
+		break;
+
+	case winmethod_Fixed:
+		keyWin = key;
+		split = !keyWin ? 0 : keyWin->getSplit(size, vertical);
+		break;
+
+	default:
+		split = diff / 2;
+		break;
+	}
+
+	if (!backward)
+		split = max - split - splitwid;
+	else
+		split = min + split;
+
+	if (min >= max) {
+		split = min;
+	} else {
+		if (split < min)
+			split = min;
+		else if (split > max - splitwid)
+			split = max - splitwid;
+	}
+
+	if (vertical) {
+		box1.left = bbox.left;
+		box1.right = split;
+		box2.left = split + splitwid;
+		box2.right = bbox.right;
+		box1.top = bbox.top;
+		box1.bottom = bbox.bottom;
+		box2.top = bbox.top;
+		box2.bottom = bbox.bottom;
+	} else {
+		box1.top = bbox.top;
+		box1.bottom = split;
+		box2.top = split + splitwid;
+		box2.bottom = bbox.bottom;
+		box1.left = bbox.left;
+		box1.right = bbox.right;
+		box2.left = bbox.left;
+		box2.right = bbox.right;
+	}
+
+	if (!backward) {
+		ch1 = child1;
+		ch2 = child2;
+	} else {
+		ch1 = child2;
+		ch2 = child1;
+	}
+
+	ch1->rearrange(box1);
+	ch2->rearrange(box2);
+}
+
 /*--------------------------------------------------------------------------*/
 
 void Attributes::clear() {
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index aad4969..64adf81 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -210,6 +210,11 @@ public:
 	 * Rearranges the window
 	 */
 	virtual void rearrange(const Common::Rect &box) { bbox = box; }
+
+	/**
+	 * Get window split size within parent pair window
+	 */
+	virtual glui32 getSplit(glui32 size, bool vertical) const { return 0; }
 };
 typedef Window *winid_t;
 
@@ -277,7 +282,15 @@ public:
 	/**
 	 * Rearranges the window
 	 */
-	virtual void rearrange(const Common::Rect &box);
+	virtual void rearrange(const Common::Rect &box) override;
+
+	/**
+	 * Get window split size within parent pair window
+	 */
+	virtual glui32 getSplit(glui32 size, bool vertical) const override {
+		return vertical ? size * Windows::_cellW + Windows::_tMarginx * 2 :
+			size * Windows::_cellH + Windows::_tMarginy * 2;
+	}
 };
 
 /**
@@ -373,7 +386,14 @@ public:
 	/**
 	 * Rearranges the window
 	 */
-	virtual void rearrange(const Common::Rect &box);
+	virtual void rearrange(const Common::Rect &box) override;
+
+	/**
+	 * Get window split size within parent pair window
+	 */
+	virtual glui32 getSplit(glui32 size, bool vertical) const override {
+		return (vertical) ? size * Windows::_cellW : size * Windows::_cellH;
+	}
 
 	/**
 	 * Clear the window
@@ -385,11 +405,30 @@ public:
  * Graphics window
  */
 class GraphicsWindow : public Window {
+private:
+	void touch();
+public:
+	unsigned char bgnd[3];
+	bool dirty;
+	int w, h;
+	Graphics::ManagedSurface *_surface;
 public:
 	/**
 	 * Constructor
 	 */
 	GraphicsWindow(Windows *windows, uint32 rock);
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box) override;
+
+	/**
+	 * Get window split size within parent pair window
+	 */
+	virtual glui32 getSplit(glui32 size, bool vertical) const override {
+		return size;
+	}
 };
 
 /**
@@ -412,6 +451,11 @@ public:
 	 * Constructor
 	 */
 	PairWindow(Windows *windows, glui32 method, Window *_key, glui32 _size);
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box) override;
 };
 
 } // End of namespace Gargoyle


Commit: 9e804bf4848d926c32bc3f1fbb5f0ee663fecaa1
    https://github.com/scummvm/scummvm/commit/9e804bf4848d926c32bc3f1fbb5f0ee663fecaa1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added glk_set_window

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 278faf5..7274ff2 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -136,7 +136,7 @@ strid_t Glk::glk_window_get_echo_stream(winid_t win) {
 }
 
 void Glk::glk_set_window(winid_t win) {
-	// TODO
+	_windows->setCurrent(win ? win->str : nullptr);
 }
 
 strid_t Glk::glk_stream_open_file(frefid_t fileref, glui32 fmode,
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 8dc546a..0bc16b2 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -89,7 +89,7 @@ WindowStyle G_STYLES[style_NUMSTYLES] = {
 
 Windows::Windows(Graphics::Screen *screen) : _screen(screen), _forceRedraw(true), _moreFocus(false),
 		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr), _mask(nullptr),
-		_claimSelect(0) {
+		_claimSelect(0), _currentStr(nullptr) {
 	_confLockCols = false;
 	_confLockRows = false;
 	_wMarginx = 15;
@@ -280,6 +280,10 @@ void Windows::clearSelection() {
 	_claimSelect = false;
 }
 
+void Windows::setCurrent(Common::WriteStream *stream) {
+	_currentStr = stream;
+}
+
 /*--------------------------------------------------------------------------*/
 
 Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 64adf81..02fa7aa 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -54,6 +54,7 @@ private:
 	bool _moreFocus;
 	bool _claimSelect;
 	WindowMask *_mask;
+	Common::WriteStream *_currentStr;
 private:
 	/**
 	 * Create a new window
@@ -111,6 +112,11 @@ public:
 	Window *getRoot() const { return _rootWin; }
 
 	void clearSelection();
+
+	/**
+	 * Set the current output stream
+	 */
+	void setCurrent(Common::WriteStream *stream);
 };
 
 /**


Commit: 3c9987c0a7566f99da637a83c9a3fe64b160e6e4
    https://github.com/scummvm/scummvm/commit/3c9987c0a7566f99da637a83c9a3fe64b160e6e4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fixes for window initialization

Changed paths:
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 0bc16b2..42c5b1a 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -90,6 +90,9 @@ WindowStyle G_STYLES[style_NUMSTYLES] = {
 Windows::Windows(Graphics::Screen *screen) : _screen(screen), _forceRedraw(true), _moreFocus(false),
 		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr), _mask(nullptr),
 		_claimSelect(0), _currentStr(nullptr) {
+	_imageW = _screen->w;
+	_imageH = _screen->h;
+	_cellW = _cellH = 8;
 	_confLockCols = false;
 	_confLockRows = false;
 	_wMarginx = 15;
@@ -104,8 +107,6 @@ Windows::Windows(Graphics::Screen *screen) : _screen(screen), _forceRedraw(true)
 	_wMarginYsave = 15;
 	_cols = 60;
 	_rows = 25;
-	_imageW = _imageH = 0;
-	_cellW = _cellH = 0;
 	_baseLine = 15;
 	_leading = 20;
 	_scrollWidth = 0;
@@ -173,8 +174,8 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 	} else {
 		// create pairwin, with newwin as the key
 		pairwin = newPairWindow(method, newwin, size);
-		pairwin->child1 = splitwin;
-		pairwin->child2 = newwin;
+		pairwin->_child1 = splitwin;
+		pairwin->_child2 = newwin;
 
 		splitwin->parent = pairwin;
 		newwin->parent = pairwin;
@@ -183,10 +184,10 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 		if (oldparent) {
 			PairWindow *parentWin = dynamic_cast<PairWindow *>(oldparent);
 			assert(parentWin);
-			if (parentWin->child1 == splitwin)
-				parentWin->child1 = pairwin;
+			if (parentWin->_child1 == splitwin)
+				parentWin->_child1 = pairwin;
 			else
-				parentWin->child2 = pairwin;
+				parentWin->_child2 = pairwin;
 		} else {
 			_rootWin = pairwin;
 		}
@@ -284,6 +285,10 @@ void Windows::setCurrent(Common::WriteStream *stream) {
 	_currentStr = stream;
 }
 
+void Windows::repaint(const Common::Rect &box) {
+	// TODO
+}
+
 /*--------------------------------------------------------------------------*/
 
 Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
@@ -348,10 +353,9 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 }
 
 void TextGridWindow::touch(int line) {
-//	int y = bbox.top + line * Windows::_leading;
+	int y = bbox.top + line * Windows::_leading;
 	lines[line].dirty = true;
-	// TODO
-//	winrepaint(bbox.left, y, bbox.right, y + Windows::_leading);
+	_windows->repaint(Common::Rect(bbox.left, y, bbox.right, y + Windows::_leading));
 }
 
 /*--------------------------------------------------------------------------*/
@@ -543,9 +547,8 @@ void TextBufferWindow::reflow() {
 
 void TextBufferWindow::touchScroll() {
 	_windows->clearSelection();
+	_windows->repaint(bbox);
 
-	// TODO
-	//winrepaint(bbox.left, bbox.top, bbox.right, bbox.bottom);
 	for (int i = 0; i < scrollmax; i++)
 		lines[i].dirty = true;
 }
@@ -580,8 +583,8 @@ void TextBufferWindow::clear() {
 		lines[i].lm = 0;
 		lines[i].rm = 0;
 		lines[i].newline = 0;
-		lines[i].dirty = 1;
-		lines[i].repaint = 0;
+		lines[i].dirty = true;
+		lines[i].repaint = false;
 	}
 
 	lastseen = 0;
@@ -782,10 +785,10 @@ void TextBufferWindow::flowBreak() {
 }
 
 void TextBufferWindow::touch(int line) {
-//	int y = bbox.top + Windows::_tMarginy + (height - line - 1) * Windows::_leading;
+	int y = bbox.top + Windows::_tMarginy + (height - line - 1) * Windows::_leading;
 	lines[line].dirty = 1;
 	_windows->clearSelection();
-	//winrepaint(bbox.left, y - 2, bbox.right, y + Windows::_leading + 2);
+	_windows->repaint(Common::Rect(bbox.left, y - 2, bbox.right, y + Windows::_leading + 2));
 }
 
 /*--------------------------------------------------------------------------*/
@@ -855,31 +858,30 @@ void GraphicsWindow::rearrange(const Common::Rect &box) {
 
 void GraphicsWindow::touch() {
 	dirty = true;
-//	winrepaint(bbox.left, bbox.top, bbox.right, bbox.bottom);
+	_windows->repaint(bbox);
 }
 
 /*--------------------------------------------------------------------------*/
 
-PairWindow::PairWindow(Windows *windows, glui32 method, Window *_key, glui32 _size) :
+PairWindow::PairWindow(Windows *windows, glui32 method, Window *key, glui32 size) :
 		Window(windows, 0),
-		dir(method & winmethod_DirMask),
-		division(method & winmethod_DivisionMask),
-		wborder((method & winmethod_BorderMask) == winmethod_Border),
-		vertical(dir == winmethod_Left || dir == winmethod_Right),
-		backward(dir == winmethod_Left || dir == winmethod_Above),
-		key(key), size(size), keydamage(0), child1(nullptr), child2(nullptr) {
+		_dir(method & winmethod_DirMask),
+		_division(method & winmethod_DivisionMask),
+		_wborder((method & winmethod_BorderMask) == winmethod_Border),
+		_vertical(_dir == winmethod_Left || _dir == winmethod_Right),
+		_backward(_dir == winmethod_Left || _dir == winmethod_Above),
+		_key(key), _size(size), _keydamage(0), _child1(nullptr), _child2(nullptr) {
 	_type = wintype_Pair;
 }
 
 void PairWindow::rearrange(const Common::Rect &box) {
 	Common::Rect box1, box2;
 	int min, diff, split, splitwid, max;
-	Window *keyWin;
 	Window *ch1, *ch2;
 
 	bbox = box;
 
-	if (vertical) {
+	if (_vertical) {
 		min = bbox.left;
 		max = bbox.right;
 	} else {
@@ -889,19 +891,18 @@ void PairWindow::rearrange(const Common::Rect &box) {
 	diff = max - min;
 
 	// We now figure split.
-	if (vertical)
+	if (_vertical)
 		splitwid = Windows::_wPaddingx; // want border?
 	else
 		splitwid = Windows::_wPaddingy; // want border?
 
-	switch (division) {
+	switch (_division) {
 	case winmethod_Proportional:
-		split = (diff * size) / 100;
+		split = (diff * _size) / 100;
 		break;
 
 	case winmethod_Fixed:
-		keyWin = key;
-		split = !keyWin ? 0 : keyWin->getSplit(size, vertical);
+		split = !_key ? 0 : _key->getSplit(_size, _vertical);
 		break;
 
 	default:
@@ -909,7 +910,7 @@ void PairWindow::rearrange(const Common::Rect &box) {
 		break;
 	}
 
-	if (!backward)
+	if (!_backward)
 		split = max - split - splitwid;
 	else
 		split = min + split;
@@ -923,7 +924,7 @@ void PairWindow::rearrange(const Common::Rect &box) {
 			split = max - splitwid;
 	}
 
-	if (vertical) {
+	if (_vertical) {
 		box1.left = bbox.left;
 		box1.right = split;
 		box2.left = split + splitwid;
@@ -943,12 +944,12 @@ void PairWindow::rearrange(const Common::Rect &box) {
 		box2.right = bbox.right;
 	}
 
-	if (!backward) {
-		ch1 = child1;
-		ch2 = child2;
+	if (!_backward) {
+		ch1 = _child1;
+		ch2 = _child2;
 	} else {
-		ch1 = child2;
-		ch2 = child1;
+		ch1 = _child2;
+		ch2 = _child1;
 	}
 
 	ch1->rearrange(box1);
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 02fa7aa..112e903 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -117,6 +117,11 @@ public:
 	 * Set the current output stream
 	 */
 	void setCurrent(Common::WriteStream *stream);
+
+	/**
+	 * Repaint an area of the windows
+	 */
+	void repaint(const Common::Rect &box);
 };
 
 /**
@@ -442,21 +447,21 @@ public:
  */
 class PairWindow : public Window {
 public:
-	Window *child1, *child2;
+	Window *_child1, *_child2;
 
 	/* split info... */
-	glui32 dir;             ///< winmethod_Left, Right, Above, or Below
-	int vertical, backward; ///< flags
-	glui32 division;        ///< winmethod_Fixed or winmethod_Proportional
-	Window *key;            ///< NULL or a leaf-descendant (not a Pair)
-	int keydamage;          ///< used as scratch space in window closing
-	glui32 size;            ///< size value
-	glui32 wborder;         ///< winMethod_Border, NoBorder
+	glui32 _dir;               ///< winmethod_Left, Right, Above, or Below
+	bool _vertical, _backward; ///< flags
+	glui32 _division;          ///< winmethod_Fixed or winmethod_Proportional
+	Window *_key;              ///< NULL or a leaf-descendant (not a Pair)
+	int _keydamage;            ///< used as scratch space in window closing
+	glui32 _size;              ///< size value
+	glui32 _wborder;           ///< winMethod_Border, NoBorder
 public:
 	/**
 	 * Constructor
 	 */
-	PairWindow(Windows *windows, glui32 method, Window *_key, glui32 _size);
+	PairWindow(Windows *windows, glui32 method, Window *key, glui32 size);
 
 	/**
 	 * Rearranges the window


Commit: 256f7ff31264efcd23b86d1fe79fce8a3597e64a
    https://github.com/scummvm/scummvm/commit/256f7ff31264efcd23b86d1fe79fce8a3597e64a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Skeleton implementation of window text stream

Changed paths:
  A engines/gargoyle/stream.cpp
  A engines/gargoyle/stream.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/module.mk
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 7274ff2..3e308d55 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -22,6 +22,7 @@
 
 #include "gargoyle/glk.h"
 #include "gargoyle/events.h"
+#include "gargoyle/stream.h"
 #include "gargoyle/windows.h"
 
 namespace Gargoyle {
@@ -122,8 +123,7 @@ void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
 }
 
 strid_t Glk::glk_window_get_stream(winid_t win) {
-	// TODO
-	return nullptr;
+	return win->_stream;
 }
 
 void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
@@ -136,7 +136,7 @@ strid_t Glk::glk_window_get_echo_stream(winid_t win) {
 }
 
 void Glk::glk_set_window(winid_t win) {
-	_windows->setCurrent(win ? win->str : nullptr);
+	_windows->setCurrent(win ? win->_stream : nullptr);
 }
 
 strid_t Glk::glk_stream_open_file(frefid_t fileref, glui32 fmode,
@@ -371,27 +371,28 @@ glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
 }
 
 void Glk::glk_put_char_uni(glui32 ch) {
-	// TODO
+	glk_put_char_stream_uni(_windows->getCurrent(), ch);
 }
 
 void Glk::glk_put_string_uni(glui32 *s) {
-	// TODO
+	glk_put_buffer_stream_uni(_windows->getCurrent(), s, strlen_uni(s));
 }
 
 void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
-	// TODO
+	glk_put_buffer_stream_uni(_windows->getCurrent(), buf, len);
 }
 
 void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
-	// TODO
+	str->writeUint32LE(ch);
 }
 
-void Glk::glk_put_string_stream_uni(strid_t str, glui32 *s) {
-	// TODO
+void Glk::glk_put_string_stream_uni(strid_t str, const glui32 *s) {
+	glk_put_buffer_stream_uni(str, s, strlen_uni(s));
 }
 
-void Glk::glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	// TODO
+void Glk::glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len) {
+	while (len-- > 0)
+		str->writeUint32LE(*buf++);
 }
 
 glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index ce21910..b0ef171 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -171,8 +171,8 @@ public:
 	void glk_put_string_uni(glui32 *s);
 	void glk_put_buffer_uni(glui32 *buf, glui32 len);
 	void glk_put_char_stream_uni(strid_t str, glui32 ch);
-	void glk_put_string_stream_uni(strid_t str, glui32 *s);
-	void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+	void glk_put_string_stream_uni(strid_t str, const glui32 *s);
+	void glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len);
 
 	glsi32 glk_get_char_stream_uni(strid_t str);
 	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 82f70e3..86b37fc 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -24,6 +24,7 @@
 #define GARGOYLE_GLK_TYPES_H
 
 #include "common/scummsys.h"
+#include "common/stream.h"
 
 namespace Gargoyle {
 
@@ -49,7 +50,7 @@ class Window;
  * These types are opaque object identifiers. They're pointers to opaque
  * C structures, which are defined differently by each library.
  */
-typedef struct glk_stream_struct  *strid_t;
+typedef Common::WriteStream  *strid_t;
 typedef struct glk_fileref_struct *frefid_t;
 typedef struct glk_schannel_struct *schanid_t;
 
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index e5542a7..5acba6a 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS := \
 	gargoyle.o \
 	glk.o \
 	picture.o \
+	stream.o \
 	windows.o \
 	scott/detection.o \
 	scott/scott.o
diff --git a/engines/gargoyle/stream.cpp b/engines/gargoyle/stream.cpp
new file mode 100644
index 0000000..a08579a
--- /dev/null
+++ b/engines/gargoyle/stream.cpp
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY{} without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program{} if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/stream.h"
+
+namespace Gargoyle {
+
+uint32 WindowStream::write(const void *dataPtr, uint32 dataSize) {
+	// TODO
+	return dataSize;
+}
+
+bool WindowStream::flush() {
+	// TODO
+	return true;
+}
+
+size_t strlen_uni(const uint32 *s) {
+	size_t len = 0;
+	while (*s++)
+		++len;
+	return len;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/stream.h b/engines/gargoyle/stream.h
new file mode 100644
index 0000000..5a55fbc
--- /dev/null
+++ b/engines/gargoyle/stream.h
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_STREAM_H
+#define GARGOYLE_STREAM_H
+
+#include "common/stream.h"
+
+namespace Gargoyle {
+
+class Window;
+
+/**
+ * Implements the stream for writing text to a window
+ */
+class WindowStream : public Common::WriteStream {
+private:
+	uint32 _rock;
+	Window *_window;
+public:
+	/**
+	 * Constructor
+	 */
+	WindowStream(Window *window, uint32 rock = 0) : Common::WriteStream(),
+		_window(window), _rock(rock) {}
+
+	/**
+	 * Write to the stream
+	 */
+	virtual uint32 write(const void *dataPtr, uint32 dataSize);
+	
+	/**
+	 * Flush the stream
+	 */
+	virtual bool flush();
+
+	/**
+	 * Finalize and close this stream
+	 */
+	virtual void finalize() { flush(); }
+
+	/**
+	 * Returns the stream position
+	 */
+	virtual int32 pos() const { return 0; }
+};
+
+/*
+ * Get the length of a unicode string
+ */
+size_t strlen_uni(const uint32 *s);
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 42c5b1a..0abc8a8 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "gargoyle/windows.h"
+#include "gargoyle/stream.h"
 #include "common/algorithm.h"
 #include "common/textconsole.h"
 
@@ -281,10 +282,6 @@ void Windows::clearSelection() {
 	_claimSelect = false;
 }
 
-void Windows::setCurrent(Common::WriteStream *stream) {
-	_currentStr = stream;
-}
-
 void Windows::repaint(const Common::Rect &box) {
 	// TODO
 }
@@ -295,7 +292,7 @@ Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
 		_windows(windows), _rock(rock), _type(0), parent(nullptr), next(nullptr), prev(nullptr),
 		yadj(0), line_request(0), line_request_uni(0), char_request(0), char_request_uni(0),
 		mouse_request(0), hyper_request(0), more_request(0), scroll_request(0), image_loaded(0),
-		echo_line_input(true),  line_terminators(nullptr), termct(0), str(nullptr), echostr(nullptr) {
+		echo_line_input(true),  line_terminators(nullptr), termct(0), _echoStream(nullptr) {
 	attr.fgset = 0;
 	attr.bgset = 0;
 	attr.reverse = 0;
@@ -307,6 +304,8 @@ Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
 	Common::fill(&bgcolor[0], &bgcolor[3], 3);
 	Common::fill(&fgcolor[0], &fgcolor[3], 3);
 	disprock.num = 0;
+
+	_stream = new WindowStream(this, rock);
 }
 
 /*--------------------------------------------------------------------------*/
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 112e903..17e7d16 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -116,7 +116,12 @@ public:
 	/**
 	 * Set the current output stream
 	 */
-	void setCurrent(Common::WriteStream *stream);
+	void setCurrent(Common::WriteStream *stream) { _currentStr = stream; }
+
+	/**
+	 * Gets the current output stream
+	 */
+	Common::WriteStream *getCurrent() const { return _currentStr; }
 
 	/**
 	 * Repaint an area of the windows
@@ -179,12 +184,12 @@ public:
 	glui32 _rock;
 	glui32 _type;
 
-	Window *parent;               ///< pair window which contains this one
+	Window *parent;                   ///< pair window which contains this one
 	Common::Rect bbox;
 	int yadj;
 
-	Common::WriteStream *str;     ///< the window stream.
-	Common::WriteStream *echostr; ///< the window's echo stream, if any.
+	Common::WriteStream *_stream;     ///< the window stream.
+	Common::WriteStream *_echoStream; ///< the window's echo stream, if any.
 
 	int line_request;
 	int line_request_uni;


Commit: 65091b25c13be1c5cb319d91fe4ad773ae6fcad0
    https://github.com/scummvm/scummvm/commit/65091b25c13be1c5cb319d91fe4ad773ae6fcad0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Starting to flesh out stream classes

Changed paths:
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/stream.cpp
    engines/gargoyle/stream.h
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 5401b2c..986a40a 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -30,6 +30,7 @@
 #include "graphics/thumbnail.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/events.h"
+#include "gargoyle/stream.h"
 #include "gargoyle/windows.h"
 
 namespace Gargoyle {
@@ -42,6 +43,7 @@ GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gam
 GargoyleEngine::~GargoyleEngine() {
 	delete _events;
 	delete _screen;
+	delete _streams;
 	delete _windows;
 }
 
@@ -55,6 +57,7 @@ void GargoyleEngine::initialize() {
 	initGraphics(640, 480, false);
 	_screen = new Graphics::Screen();
 	_events = new Events();
+	_streams = new Streams();
 	_windows = new Windows(_screen);
 }
 
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index c7a7b86..bd97713 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -35,6 +35,7 @@ namespace Gargoyle {
 
 class Events;
 class Windows;
+class Streams;
 
 enum InterpreterType {
 	INTERPRETER_SCOTT
@@ -74,6 +75,7 @@ protected:
 	const GargoyleGameDescription *_gameDescription;
 	Events *_events;
 	Graphics::Screen *_screen;
+	Streams *_streams;
 	Windows *_windows;
 	Common::RandomSource _random;
 	int _loadSaveSlot;
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 3e308d55..22d02f5 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -139,13 +139,13 @@ void Glk::glk_set_window(winid_t win) {
 	_windows->setCurrent(win ? win->_stream : nullptr);
 }
 
-strid_t Glk::glk_stream_open_file(frefid_t fileref, glui32 fmode,
+strid_t Glk::glk_stream_open_file(frefid_t fileref, FileMode fmode,
 	glui32 rock) {
 	// TODO
 	return nullptr;
 }
 
-strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) {
+strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock) {
 	// TODO
 	return nullptr;
 }
@@ -154,14 +154,17 @@ void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
 	// TODO
 }
 
-strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) {
-	// TODO
-	return nullptr;
+strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) const {
+	return str ? str->getNext(rockptr) : _streams->getFirst(rockptr);
 }
 
-glui32 Glk::glk_stream_get_rock(strid_t str) {
-	// TODO
-	return 0;
+glui32 Glk::glk_stream_get_rock(strid_t str) const {
+	if (!str) {
+		warning("stream_get_rock: invalid ref");
+		return 0;
+	}
+
+	return str->getRock();
 }
 
 void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) {
@@ -257,7 +260,7 @@ frefid_t Glk::glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock)
 	return nullptr;
 }
 
-frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, glui32 fmode, glui32 rock) {
+frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock) {
 	// TODO
 	return nullptr;
 }
@@ -383,7 +386,7 @@ void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
 }
 
 void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
-	str->writeUint32LE(ch);
+//	str->writeUint32LE(ch);
 }
 
 void Glk::glk_put_string_stream_uni(strid_t str, const glui32 *s) {
@@ -391,8 +394,7 @@ void Glk::glk_put_string_stream_uni(strid_t str, const glui32 *s) {
 }
 
 void Glk::glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len) {
-	while (len-- > 0)
-		str->writeUint32LE(*buf++);
+//	while (len-- > 0) str->writeUint32LE(*buf++);
 }
 
 glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
@@ -410,15 +412,13 @@ glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
 	return 0;
 }
 
-strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock) {
+strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock) {
 	// TODO
 	return nullptr;
 }
 
-strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
-	glui32 fmode, glui32 rock) {
-	// TODO
-	return nullptr;
+strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock) {
+	return _streams->addMemoryStream(buf, buflen, fmode, rock, false);
 }
 
 void Glk::glk_request_char_event_uni(winid_t win) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index b0ef171..0029ecf 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -90,13 +90,11 @@ public:
 	strid_t glk_window_get_echo_stream(winid_t win);
 	void glk_set_window(winid_t win);
 
-	strid_t glk_stream_open_file(frefid_t fileref, glui32 fmode,
-		glui32 rock);
-	strid_t glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode,
-		glui32 rock);
+	strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock);
+	strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock);
 	void glk_stream_close(strid_t str, stream_result_t *result);
-	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr);
-	glui32 glk_stream_get_rock(strid_t str);
+	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const;
+	glui32 glk_stream_get_rock(strid_t str) const;
 	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode);
 	glui32 glk_stream_get_position(strid_t str);
 	void glk_stream_set_current(strid_t str);
@@ -125,7 +123,7 @@ public:
 	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
 	frefid_t glk_fileref_create_by_name(glui32 usage, char *name,
 		glui32 rock);
-	frefid_t glk_fileref_create_by_prompt(glui32 usage, glui32 fmode,
+	frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode,
 		glui32 rock);
 	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref,
 		glui32 rock);
@@ -178,10 +176,8 @@ public:
 	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
 	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
 
-	strid_t glk_stream_open_file_uni(frefid_t fileref, glui32 fmode,
-		glui32 rock);
-	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen,
-		glui32 fmode, glui32 rock);
+	strid_t glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock);
+	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock);
 
 	void glk_request_char_event_uni(winid_t win);
 	void glk_request_line_event_uni(winid_t win, glui32 *buf,
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 86b37fc..c7b505e 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -50,7 +50,6 @@ class Window;
  * These types are opaque object identifiers. They're pointers to opaque
  * C structures, which are defined differently by each library.
  */
-typedef Common::WriteStream  *strid_t;
 typedef struct glk_fileref_struct *frefid_t;
 typedef struct glk_schannel_struct *schanid_t;
 
diff --git a/engines/gargoyle/stream.cpp b/engines/gargoyle/stream.cpp
index a08579a..c447646 100644
--- a/engines/gargoyle/stream.cpp
+++ b/engines/gargoyle/stream.cpp
@@ -21,19 +21,121 @@
  */
 
 #include "gargoyle/stream.h"
+#include "gargoyle/windows.h"
 
 namespace Gargoyle {
 
-uint32 WindowStream::write(const void *dataPtr, uint32 dataSize) {
-	// TODO
-	return dataSize;
+Stream::Stream(bool readable, bool writable, uint32 rock, bool unicode) :
+		_readable(readable), _writable(writable), _readCount(0), _writeCount(0),
+		_prev(nullptr), _next(nullptr), _rock(0) {
 }
 
-bool WindowStream::flush() {
-	// TODO
-	return true;
+Stream *Stream::getNext(uint32 *rock) const {
+	Stream *stream = _next;
+	if (rock)
+		*rock = stream ? stream->_rock : 0;
+	return stream;
 }
 
+void Stream::fillResult(StreamResult *result) {
+	if (result) {
+		result->_readCount = _readCount;
+		result->_writeCount = _writeCount;
+	}
+}
+
+void Stream::close(StreamResult *result) {
+	fillResult(result);
+
+}
+
+/*--------------------------------------------------------------------------*/
+
+void WindowStream::writeChar(unsigned char ch) {
+
+}
+
+void WindowStream::writeCharUni(uint32 ch) {
+
+}
+
+/*--------------------------------------------------------------------------*/
+
+MemoryStream::MemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
+		Stream(mode != filemode_Write, mode != filemode_Read, rock, unicode),
+		_buf(buf), _buflen(buflen), _bufptr(buf) {
+	assert(_buf && _buflen);
+	assert(mode == filemode_Read || mode == filemode_Write || mode == filemode_ReadWrite);
+
+	if (unicode)
+		_bufend = (uint32 *)buf + buflen;
+	else
+		_bufend = (byte *)buf + buflen;
+	_bufeof = mode == filemode_Write ? _buf : _bufend;
+}
+
+void MemoryStream::writeChar(unsigned char ch) {
+
+}
+
+void MemoryStream::writeCharUni(uint32 ch) {
+
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+
+Streams::Streams() : _streamList(nullptr) {}
+
+Streams::~Streams() {
+	while (_streamList)
+		deleteStream(_streamList);
+}
+
+WindowStream *Streams::addWindowStream(Window *window) {
+	WindowStream *stream = new WindowStream(window);
+	addStream(stream);
+	return stream;
+}
+
+MemoryStream *Streams::addMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) {
+	MemoryStream *stream = new MemoryStream(buf, buflen, mode, rock, unicode);
+	addStream(stream);
+	return stream;
+}
+
+void Streams::addStream(Stream *stream) {
+	stream->_next = _streamList;
+	_streamList = stream;
+	if (stream->_next)
+		stream->_next->_prev = stream;
+}
+
+void Streams::deleteStream(Stream *stream) {
+	Stream *prev = stream->_prev;
+	Stream *next = stream->_next;
+
+	if (prev)
+		prev->_next = next;
+	else
+		_streamList = next;
+	if (next)
+		next->_prev = prev;
+
+	delete stream;
+}
+
+Stream *Streams::getFirst(uint32 *rock) {
+	if (rock)
+		*rock = _streamList ? _streamList->_rock : 0;
+	return _streamList;
+}
+
+/*--------------------------------------------------------------------------*/
+
 size_t strlen_uni(const uint32 *s) {
 	size_t len = 0;
 	while (*s++)
diff --git a/engines/gargoyle/stream.h b/engines/gargoyle/stream.h
index 5a55fbc..4effee3 100644
--- a/engines/gargoyle/stream.h
+++ b/engines/gargoyle/stream.h
@@ -23,47 +23,169 @@
 #ifndef GARGOYLE_STREAM_H
 #define GARGOYLE_STREAM_H
 
-#include "common/stream.h"
+#include "common/scummsys.h"
+#include "gargoyle/glk_types.h"
 
 namespace Gargoyle {
 
 class Window;
 
+struct StreamResult {
+	uint32 _readCount;
+	uint32 _writeCount;
+};
+
+/**
+ * Base class for streams
+ */
+class Stream {
+public:
+	Stream *_prev;
+	Stream *_next;
+	uint32 _rock;
+	bool _unicode;
+	uint32 _readCount;
+	uint32 _writeCount;
+	bool _readable, _writable;
+public:
+	/**
+	 * Constructor
+	 */
+	Stream(bool readable, bool writable, uint32 rock, bool unicode);
+
+	/**
+	 * Destructor
+	 */
+	virtual ~Stream() {}
+
+	/**
+	 * Get the next stream
+	 */
+	Stream *getNext(uint32 *rock) const;
+
+	/**
+	 * Get the rock value for the stream
+	 */
+	uint32 getRock() const { return _rock; }
+
+	/**
+	 * Fill out the total amount read and/or written
+	 */
+	void fillResult(StreamResult *result);
+
+	/**
+	 * Close the stream
+	 */
+	virtual void close(StreamResult *result = nullptr);
+
+	/**
+	 * Write a character
+	 */
+	virtual void writeChar(unsigned char ch) = 0;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void writeCharUni(uint32 ch) = 0;
+};
+typedef Stream *strid_t;
+
 /**
  * Implements the stream for writing text to a window
  */
-class WindowStream : public Common::WriteStream {
+class WindowStream : public Stream {
 private:
-	uint32 _rock;
 	Window *_window;
 public:
 	/**
 	 * Constructor
 	 */
-	WindowStream(Window *window, uint32 rock = 0) : Common::WriteStream(),
-		_window(window), _rock(rock) {}
+	WindowStream(Window *window, uint32 rock = 0, bool unicode = true) :
+		Stream(true, false, rock, unicode), _window(window) {}
 
 	/**
-	 * Write to the stream
+	 * Write a character
 	 */
-	virtual uint32 write(const void *dataPtr, uint32 dataSize);
-	
+	virtual void writeChar(unsigned char ch) override;
+
 	/**
-	 * Flush the stream
+	 * Write a unicode character
 	 */
-	virtual bool flush();
+	virtual void writeCharUni(uint32 ch) override;
+};
 
+/**
+ * Implements an in-memory stream
+ */
+class MemoryStream : public Stream {
+private:
+	void *_buf;		///< unsigned char* for latin1, glui32* for unicode
+	void *_bufptr;
+	void *_bufend;
+	void *_bufeof;
+	size_t _buflen;	///< # of bytes for latin1, # of 4-byte words for unicode
+public:
+	/**
+	 * Constructor
+	 */
+	MemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
+
+	/**
+	 * Write a character
+	 */
+	virtual void writeChar(unsigned char ch);
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void writeCharUni(uint32 ch);
+};
+
+/**
+ * Streams manager
+ */
+class Streams {
+private:
+	Stream *_streamList;
+private:
 	/**
-	 * Finalize and close this stream
+	 * Adds a created stream to the list
 	 */
-	virtual void finalize() { flush(); }
+	void addStream(Stream *stream);
+public:
+	/**
+	 * Constructor
+	 */
+	Streams();
+
+	/**
+	 * Destructor
+	 */
+	~Streams();
+
+	/**
+	 * Add a window stream
+	 */
+	WindowStream *addWindowStream(Window *window);
 
 	/**
-	 * Returns the stream position
+	 * Add a memory stream
 	 */
-	virtual int32 pos() const { return 0; }
+	MemoryStream *addMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
+
+	/**
+	 * Delete a stream
+	 */
+	void deleteStream(Stream *stream);
+
+	/**
+	 * Start an Iteration through streams
+	 */
+	Stream *getFirst(uint32 *rock);
 };
 
+
+
 /*
  * Get the length of a unicode string
  */
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 17e7d16..fdb29bb 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -26,10 +26,10 @@
 #include "common/array.h"
 #include "common/list.h"
 #include "common/rect.h"
-#include "common/stream.h"
 #include "graphics/screen.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/picture.h"
+#include "gargoyle/stream.h"
 
 namespace Gargoyle {
 
@@ -54,7 +54,7 @@ private:
 	bool _moreFocus;
 	bool _claimSelect;
 	WindowMask *_mask;
-	Common::WriteStream *_currentStr;
+	Stream *_currentStr;
 private:
 	/**
 	 * Create a new window
@@ -116,12 +116,12 @@ public:
 	/**
 	 * Set the current output stream
 	 */
-	void setCurrent(Common::WriteStream *stream) { _currentStr = stream; }
+	void setCurrent(Stream *stream) { _currentStr = stream; }
 
 	/**
 	 * Gets the current output stream
 	 */
-	Common::WriteStream *getCurrent() const { return _currentStr; }
+	Stream *getCurrent() const { return _currentStr; }
 
 	/**
 	 * Repaint an area of the windows
@@ -184,12 +184,12 @@ public:
 	glui32 _rock;
 	glui32 _type;
 
-	Window *parent;                   ///< pair window which contains this one
+	Window *parent;            ///< pair window which contains this one
 	Common::Rect bbox;
 	int yadj;
 
-	Common::WriteStream *_stream;     ///< the window stream.
-	Common::WriteStream *_echoStream; ///< the window's echo stream, if any.
+	Stream *_stream;      ///< the window stream.
+	Stream *_echoStream;  ///< the window's echo stream, if any.
 
 	int line_request;
 	int line_request_uni;
@@ -210,7 +210,7 @@ public:
 	byte fgcolor[3];
 
 	gidispatch_rock_t disprock;
-	Window *next, *prev;          ///< in the big linked list of windows
+	Window *next, *prev;       ///< in the big linked list of windows
 public:
 	/**
 	 * Constructor


Commit: cf5259d3bc279fd73b4ae8bad2d7b04b8ff7265e
    https://github.com/scummvm/scummvm/commit/cf5259d3bc279fd73b4ae8bad2d7b04b8ff7265e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Adding stream open/closing

Changed paths:
  A engines/gargoyle/streams.cpp
  A engines/gargoyle/streams.h
  R engines/gargoyle/stream.cpp
  R engines/gargoyle/stream.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/module.mk
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 986a40a..a481ae9 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -30,7 +30,7 @@
 #include "graphics/thumbnail.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/events.h"
-#include "gargoyle/stream.h"
+#include "gargoyle/streams.h"
 #include "gargoyle/windows.h"
 
 namespace Gargoyle {
@@ -57,8 +57,8 @@ void GargoyleEngine::initialize() {
 	initGraphics(640, 480, false);
 	_screen = new Graphics::Screen();
 	_events = new Events();
-	_streams = new Streams();
-	_windows = new Windows(_screen);
+	_streams = new Streams(this);
+	_windows = new Windows(this, _screen);
 }
 
 Common::Error GargoyleEngine::run() {
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index bd97713..e7d5df2 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -73,10 +73,7 @@ private:
 	void initialize();
 protected:
 	const GargoyleGameDescription *_gameDescription;
-	Events *_events;
 	Graphics::Screen *_screen;
-	Streams *_streams;
-	Windows *_windows;
 	Common::RandomSource _random;
 	int _loadSaveSlot;
 
@@ -93,6 +90,10 @@ protected:
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
 public:
+	Events *_events;
+	Streams *_streams;
+	Windows *_windows;
+public:
 	GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
 	virtual ~GargoyleEngine();
 
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 22d02f5..b49fb63 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -22,7 +22,7 @@
 
 #include "gargoyle/glk.h"
 #include "gargoyle/events.h"
-#include "gargoyle/stream.h"
+#include "gargoyle/streams.h"
 #include "gargoyle/windows.h"
 
 namespace Gargoyle {
@@ -136,7 +136,7 @@ strid_t Glk::glk_window_get_echo_stream(winid_t win) {
 }
 
 void Glk::glk_set_window(winid_t win) {
-	_windows->setCurrent(win ? win->_stream : nullptr);
+	_streams->setCurrent(win ? win->_stream : nullptr);
 }
 
 strid_t Glk::glk_stream_open_file(frefid_t fileref, FileMode fmode,
@@ -374,15 +374,15 @@ glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
 }
 
 void Glk::glk_put_char_uni(glui32 ch) {
-	glk_put_char_stream_uni(_windows->getCurrent(), ch);
+	glk_put_char_stream_uni(_streams->getCurrent(), ch);
 }
 
 void Glk::glk_put_string_uni(glui32 *s) {
-	glk_put_buffer_stream_uni(_windows->getCurrent(), s, strlen_uni(s));
+	glk_put_buffer_stream_uni(_streams->getCurrent(), s, strlen_uni(s));
 }
 
 void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
-	glk_put_buffer_stream_uni(_windows->getCurrent(), buf, len);
+	glk_put_buffer_stream_uni(_streams->getCurrent(), buf, len);
 }
 
 void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 5acba6a..05b7f5f 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -6,7 +6,7 @@ MODULE_OBJS := \
 	gargoyle.o \
 	glk.o \
 	picture.o \
-	stream.o \
+	streams.o \
 	windows.o \
 	scott/detection.o \
 	scott/scott.o
diff --git a/engines/gargoyle/stream.cpp b/engines/gargoyle/stream.cpp
deleted file mode 100644
index c447646..0000000
--- a/engines/gargoyle/stream.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/stream.h"
-#include "gargoyle/windows.h"
-
-namespace Gargoyle {
-
-Stream::Stream(bool readable, bool writable, uint32 rock, bool unicode) :
-		_readable(readable), _writable(writable), _readCount(0), _writeCount(0),
-		_prev(nullptr), _next(nullptr), _rock(0) {
-}
-
-Stream *Stream::getNext(uint32 *rock) const {
-	Stream *stream = _next;
-	if (rock)
-		*rock = stream ? stream->_rock : 0;
-	return stream;
-}
-
-void Stream::fillResult(StreamResult *result) {
-	if (result) {
-		result->_readCount = _readCount;
-		result->_writeCount = _writeCount;
-	}
-}
-
-void Stream::close(StreamResult *result) {
-	fillResult(result);
-
-}
-
-/*--------------------------------------------------------------------------*/
-
-void WindowStream::writeChar(unsigned char ch) {
-
-}
-
-void WindowStream::writeCharUni(uint32 ch) {
-
-}
-
-/*--------------------------------------------------------------------------*/
-
-MemoryStream::MemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
-		Stream(mode != filemode_Write, mode != filemode_Read, rock, unicode),
-		_buf(buf), _buflen(buflen), _bufptr(buf) {
-	assert(_buf && _buflen);
-	assert(mode == filemode_Read || mode == filemode_Write || mode == filemode_ReadWrite);
-
-	if (unicode)
-		_bufend = (uint32 *)buf + buflen;
-	else
-		_bufend = (byte *)buf + buflen;
-	_bufeof = mode == filemode_Write ? _buf : _bufend;
-}
-
-void MemoryStream::writeChar(unsigned char ch) {
-
-}
-
-void MemoryStream::writeCharUni(uint32 ch) {
-
-}
-
-/*--------------------------------------------------------------------------*/
-
-/*--------------------------------------------------------------------------*/
-
-/*--------------------------------------------------------------------------*/
-
-Streams::Streams() : _streamList(nullptr) {}
-
-Streams::~Streams() {
-	while (_streamList)
-		deleteStream(_streamList);
-}
-
-WindowStream *Streams::addWindowStream(Window *window) {
-	WindowStream *stream = new WindowStream(window);
-	addStream(stream);
-	return stream;
-}
-
-MemoryStream *Streams::addMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) {
-	MemoryStream *stream = new MemoryStream(buf, buflen, mode, rock, unicode);
-	addStream(stream);
-	return stream;
-}
-
-void Streams::addStream(Stream *stream) {
-	stream->_next = _streamList;
-	_streamList = stream;
-	if (stream->_next)
-		stream->_next->_prev = stream;
-}
-
-void Streams::deleteStream(Stream *stream) {
-	Stream *prev = stream->_prev;
-	Stream *next = stream->_next;
-
-	if (prev)
-		prev->_next = next;
-	else
-		_streamList = next;
-	if (next)
-		next->_prev = prev;
-
-	delete stream;
-}
-
-Stream *Streams::getFirst(uint32 *rock) {
-	if (rock)
-		*rock = _streamList ? _streamList->_rock : 0;
-	return _streamList;
-}
-
-/*--------------------------------------------------------------------------*/
-
-size_t strlen_uni(const uint32 *s) {
-	size_t len = 0;
-	while (*s++)
-		++len;
-	return len;
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/stream.h b/engines/gargoyle/stream.h
deleted file mode 100644
index 4effee3..0000000
--- a/engines/gargoyle/stream.h
+++ /dev/null
@@ -1,196 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_STREAM_H
-#define GARGOYLE_STREAM_H
-
-#include "common/scummsys.h"
-#include "gargoyle/glk_types.h"
-
-namespace Gargoyle {
-
-class Window;
-
-struct StreamResult {
-	uint32 _readCount;
-	uint32 _writeCount;
-};
-
-/**
- * Base class for streams
- */
-class Stream {
-public:
-	Stream *_prev;
-	Stream *_next;
-	uint32 _rock;
-	bool _unicode;
-	uint32 _readCount;
-	uint32 _writeCount;
-	bool _readable, _writable;
-public:
-	/**
-	 * Constructor
-	 */
-	Stream(bool readable, bool writable, uint32 rock, bool unicode);
-
-	/**
-	 * Destructor
-	 */
-	virtual ~Stream() {}
-
-	/**
-	 * Get the next stream
-	 */
-	Stream *getNext(uint32 *rock) const;
-
-	/**
-	 * Get the rock value for the stream
-	 */
-	uint32 getRock() const { return _rock; }
-
-	/**
-	 * Fill out the total amount read and/or written
-	 */
-	void fillResult(StreamResult *result);
-
-	/**
-	 * Close the stream
-	 */
-	virtual void close(StreamResult *result = nullptr);
-
-	/**
-	 * Write a character
-	 */
-	virtual void writeChar(unsigned char ch) = 0;
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void writeCharUni(uint32 ch) = 0;
-};
-typedef Stream *strid_t;
-
-/**
- * Implements the stream for writing text to a window
- */
-class WindowStream : public Stream {
-private:
-	Window *_window;
-public:
-	/**
-	 * Constructor
-	 */
-	WindowStream(Window *window, uint32 rock = 0, bool unicode = true) :
-		Stream(true, false, rock, unicode), _window(window) {}
-
-	/**
-	 * Write a character
-	 */
-	virtual void writeChar(unsigned char ch) override;
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void writeCharUni(uint32 ch) override;
-};
-
-/**
- * Implements an in-memory stream
- */
-class MemoryStream : public Stream {
-private:
-	void *_buf;		///< unsigned char* for latin1, glui32* for unicode
-	void *_bufptr;
-	void *_bufend;
-	void *_bufeof;
-	size_t _buflen;	///< # of bytes for latin1, # of 4-byte words for unicode
-public:
-	/**
-	 * Constructor
-	 */
-	MemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
-
-	/**
-	 * Write a character
-	 */
-	virtual void writeChar(unsigned char ch);
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void writeCharUni(uint32 ch);
-};
-
-/**
- * Streams manager
- */
-class Streams {
-private:
-	Stream *_streamList;
-private:
-	/**
-	 * Adds a created stream to the list
-	 */
-	void addStream(Stream *stream);
-public:
-	/**
-	 * Constructor
-	 */
-	Streams();
-
-	/**
-	 * Destructor
-	 */
-	~Streams();
-
-	/**
-	 * Add a window stream
-	 */
-	WindowStream *addWindowStream(Window *window);
-
-	/**
-	 * Add a memory stream
-	 */
-	MemoryStream *addMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
-
-	/**
-	 * Delete a stream
-	 */
-	void deleteStream(Stream *stream);
-
-	/**
-	 * Start an Iteration through streams
-	 */
-	Stream *getFirst(uint32 *rock);
-};
-
-
-
-/*
- * Get the length of a unicode string
- */
-size_t strlen_uni(const uint32 *s);
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
new file mode 100644
index 0000000..62e43e3
--- /dev/null
+++ b/engines/gargoyle/streams.cpp
@@ -0,0 +1,156 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 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 "gargoyle/streams.h"
+#include "gargoyle/windows.h"
+
+namespace Gargoyle {
+
+Stream::Stream(Streams *streams, bool readable, bool writable, uint32 rock, bool unicode) :
+		_streams(streams), _readable(readable), _writable(writable), _readCount(0),
+		_writeCount(0), _prev(nullptr), _next(nullptr), _rock(0) {
+}
+
+Stream::~Stream() {
+	_streams->removeStream(this);
+}
+
+Stream *Stream::getNext(uint32 *rock) const {
+	Stream *stream = _next;
+	if (rock)
+		*rock = stream ? stream->_rock : 0;
+	return stream;
+}
+
+void Stream::fillResult(StreamResult *result) {
+	if (result) {
+		result->_readCount = _readCount;
+		result->_writeCount = _writeCount;
+	}
+}
+
+void Stream::close(StreamResult *result) {
+	// Get the read/write totals
+	fillResult(result);
+
+	// Remove the stream
+	delete this;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void WindowStream::close(StreamResult *result) {
+	warning("cannot close window stream");
+}
+
+void WindowStream::writeChar(unsigned char ch) {
+
+}
+
+void WindowStream::writeCharUni(uint32 ch) {
+
+}
+
+/*--------------------------------------------------------------------------*/
+
+MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
+		Stream(streams, mode != filemode_Write, mode != filemode_Read, rock, unicode),
+		_buf(buf), _buflen(buflen), _bufptr(buf) {
+	assert(_buf && _buflen);
+	assert(mode == filemode_Read || mode == filemode_Write || mode == filemode_ReadWrite);
+
+	if (unicode)
+		_bufend = (uint32 *)buf + buflen;
+	else
+		_bufend = (byte *)buf + buflen;
+	_bufeof = mode == filemode_Write ? _buf : _bufend;
+}
+
+void MemoryStream::writeChar(unsigned char ch) {
+
+}
+
+void MemoryStream::writeCharUni(uint32 ch) {
+
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+
+Streams::Streams(GargoyleEngine *engine) : _engine(engine), _streamList(nullptr), _currentStream(nullptr) {
+}
+
+Streams::~Streams() {
+	while (_streamList)
+		deleteStream(_streamList);
+}
+
+WindowStream *Streams::addWindowStream(Window *window) {
+	WindowStream *stream = new WindowStream(this, window);
+	addStream(stream);
+	return stream;
+}
+
+MemoryStream *Streams::addMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) {
+	MemoryStream *stream = new MemoryStream(this, buf, buflen, mode, rock, unicode);
+	addStream(stream);
+	return stream;
+}
+
+void Streams::addStream(Stream *stream) {
+	stream->_next = _streamList;
+	_streamList = stream;
+	if (stream->_next)
+		stream->_next->_prev = stream;
+}
+
+void Streams::removeStream(Stream *stream) {
+	Stream *prev = stream->_prev;
+	Stream *next = stream->_next;
+
+	if (prev)
+		prev->_next = next;
+	else
+		_streamList = next;
+	if (next)
+		next->_prev = prev;
+}
+
+Stream *Streams::getFirst(uint32 *rock) {
+	if (rock)
+		*rock = _streamList ? _streamList->_rock : 0;
+	return _streamList;
+}
+
+/*--------------------------------------------------------------------------*/
+
+size_t strlen_uni(const uint32 *s) {
+	size_t len = 0;
+	while (*s++)
+		++len;
+	return len;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
new file mode 100644
index 0000000..db21d58
--- /dev/null
+++ b/engines/gargoyle/streams.h
@@ -0,0 +1,227 @@
+/* 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 GARGOYLE_STREAMS_H
+#define GARGOYLE_STREAMS_H
+
+#include "common/scummsys.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+class GargoyleEngine;
+class Window;
+class Streams;
+
+struct StreamResult {
+	uint32 _readCount;
+	uint32 _writeCount;
+};
+
+/**
+ * Base class for streams
+ */
+class Stream {
+public:
+	Streams *_streams;
+	Stream *_prev;
+	Stream *_next;
+	uint32 _rock;
+	bool _unicode;
+	uint32 _readCount;
+	uint32 _writeCount;
+	bool _readable, _writable;
+public:
+	/**
+	 * Constructor
+	 */
+	Stream(Streams *streams, bool readable, bool writable, uint32 rock, bool unicode);
+
+	/**
+	 * Destructor
+	 */
+	virtual ~Stream();
+
+	/**
+	 * Get the next stream
+	 */
+	Stream *getNext(uint32 *rock) const;
+
+	/**
+	 * Get the rock value for the stream
+	 */
+	uint32 getRock() const { return _rock; }
+
+	/**
+	 * Fill out the total amount read and/or written
+	 */
+	void fillResult(StreamResult *result);
+
+	/**
+	 * Close and delete the stream
+	 */
+	void close(StreamResult *result = nullptr);
+
+	/**
+	 * Write a character
+	 */
+	virtual void writeChar(unsigned char ch) = 0;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void writeCharUni(uint32 ch) = 0;
+};
+typedef Stream *strid_t;
+
+/**
+ * Implements the stream for writing text to a window
+ */
+class WindowStream : public Stream {
+private:
+	Window *_window;
+public:
+	/**
+	 * Constructor
+	 */
+	WindowStream(Streams *streams, Window *window, uint32 rock = 0, bool unicode = true) :
+		Stream(streams, true, false, rock, unicode), _window(window) {}
+
+	/**
+	 * Close the stream
+	 */
+	virtual void close(StreamResult *result = nullptr);
+
+	/**
+	 * Write a character
+	 */
+	virtual void writeChar(unsigned char ch) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void writeCharUni(uint32 ch) override;
+};
+
+/**
+ * Implements an in-memory stream
+ */
+class MemoryStream : public Stream {
+private:
+	void *_buf;		///< unsigned char* for latin1, glui32* for unicode
+	void *_bufptr;
+	void *_bufend;
+	void *_bufeof;
+	size_t _buflen;	///< # of bytes for latin1, # of 4-byte words for unicode
+public:
+	/**
+	 * Constructor
+	 */
+	MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
+
+	/**
+	 * Write a character
+	 */
+	virtual void writeChar(unsigned char ch);
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void writeCharUni(uint32 ch);
+};
+
+/**
+ * Streams manager
+ */
+class Streams {
+	friend class Stream;
+private:
+	GargoyleEngine *_engine;
+	Stream *_streamList;
+	Stream *_currentStream;
+private:
+	/**
+	 * Adds a created stream to the list
+	 */
+	void addStream(Stream *stream);
+
+	/**
+	 * Remove a stream
+	 */
+	void removeStream(Stream *stream);
+public:
+	/**
+	 * Constructor
+	 */
+	Streams(GargoyleEngine *engine);
+
+	/**
+	 * Destructor
+	 */
+	~Streams();
+
+	/**
+	 * Add a window stream
+	 */
+	WindowStream *addWindowStream(Window *window);
+
+	/**
+	 * Add a memory stream
+	 */
+	MemoryStream *addMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
+
+	/**
+	 * Delete a stream
+	 */
+	void deleteStream(Stream *stream) {
+		delete stream;
+	}
+
+	/**
+	 * Start an Iteration through streams
+	 */
+	Stream *getFirst(uint32 *rock);
+
+	/**
+	 * Set the current output stream
+	 */
+	void setCurrent(Stream *stream) {
+		assert(stream->_writable);
+		_currentStream = stream;
+	}
+
+	/**
+	 * Gets the current output stream
+	 */
+	Stream *getCurrent() const { return _currentStream; }
+};
+
+
+
+/*
+ * Get the length of a unicode string
+ */
+size_t strlen_uni(const uint32 *s);
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 0abc8a8..2c8680b 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -21,7 +21,8 @@
  */
 
 #include "gargoyle/windows.h"
-#include "gargoyle/stream.h"
+#include "gargoyle/gargoyle.h"
+#include "gargoyle/streams.h"
 #include "common/algorithm.h"
 #include "common/textconsole.h"
 
@@ -88,9 +89,10 @@ WindowStyle G_STYLES[style_NUMSTYLES] = {
 
 /*--------------------------------------------------------------------------*/
 
-Windows::Windows(Graphics::Screen *screen) : _screen(screen), _forceRedraw(true), _moreFocus(false),
+Windows::Windows(GargoyleEngine *engine, Graphics::Screen *screen) :
+		_engine(engine), _screen(screen), _forceRedraw(true), _moreFocus(false),
 		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr), _mask(nullptr),
-		_claimSelect(0), _currentStr(nullptr) {
+		_claimSelect(0) {
 	_imageW = _screen->w;
 	_imageH = _screen->h;
 	_cellW = _cellH = 8;
@@ -305,7 +307,8 @@ Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
 	Common::fill(&fgcolor[0], &fgcolor[3], 3);
 	disprock.num = 0;
 
-	_stream = new WindowStream(this, rock);
+	Streams &streams = *windows->_engine->_streams;
+	_stream = streams.addWindowStream(this);
 }
 
 /*--------------------------------------------------------------------------*/
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index fdb29bb..95f1ed4 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -29,10 +29,11 @@
 #include "graphics/screen.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/picture.h"
-#include "gargoyle/stream.h"
+#include "gargoyle/streams.h"
 
 namespace Gargoyle {
 
+class GargoyleEngine;
 class Window;
 class PairWindow;
 struct WindowMask;
@@ -45,7 +46,9 @@ struct WindowMask;
  * Main windows manager
  */
 class Windows {
+	friend class Window;
 private:
+	GargoyleEngine *_engine;
 	Graphics::Screen *_screen;
 	Window * _windowList;      ///< List of all windows
 	Window *_rootWin;          ///< The topmost window
@@ -54,7 +57,6 @@ private:
 	bool _moreFocus;
 	bool _claimSelect;
 	WindowMask *_mask;
-	Stream *_currentStr;
 private:
 	/**
 	 * Create a new window
@@ -98,7 +100,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Windows(Graphics::Screen *screen);
+	Windows(GargoyleEngine *engine, Graphics::Screen *screen);
 
 	/**
 	 * Open a new window
@@ -114,16 +116,6 @@ public:
 	void clearSelection();
 
 	/**
-	 * Set the current output stream
-	 */
-	void setCurrent(Stream *stream) { _currentStr = stream; }
-
-	/**
-	 * Gets the current output stream
-	 */
-	Stream *getCurrent() const { return _currentStr; }
-
-	/**
 	 * Repaint an area of the windows
 	 */
 	void repaint(const Common::Rect &box);


Commit: ef15871fec18060420eddc92a9830f595b8cc970
    https://github.com/scummvm/scummvm/commit/ef15871fec18060420eddc92a9830f595b8cc970
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Setting up of configuration loading

Changed paths:
  A engines/gargoyle/conf.cpp
  A engines/gargoyle/conf.h
  A engines/gargoyle/fonts.cpp
  A engines/gargoyle/fonts.h
  A engines/gargoyle/string.cpp
  A engines/gargoyle/string.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk_types.h
    engines/gargoyle/module.mk
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
new file mode 100644
index 0000000..c296254
--- /dev/null
+++ b/engines/gargoyle/conf.cpp
@@ -0,0 +1,152 @@
+/* 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 "gargoyle/conf.h"
+#include "gargoyle/fonts.h"
+#include "gargoyle/string.h"
+#include "common/config-manager.h"
+
+namespace Gargoyle {
+
+const byte WHITE[3] = { 0xff, 0xff, 0xff };
+const byte BLUE[3] = { 0x00, 0x00, 0x60 };
+const byte SCROLL_BG[3] = { 0xb0, 0xb0, 0xb0 };
+const byte SCROLL_FG[3] = { 0x80, 0x80, 0x80 };
+
+Conf::Conf() {
+	g_conf = this;
+
+	get("moreprompt", _morePrompt, "\207 more \207");
+	get("morecolor", _moreColor);
+	get("morecolor", _moreSave);
+	get("morefont", _moreFont, PROPB);
+	get("morealign", _moreAlign);
+	get("monoaspect", _monoAspect, 1.0);
+	get("propaspect", _propAspect, 1.0);
+	get("monosize", _monoSize, 12.5);
+	get("monor", _monoR);
+	get("monob", _monoR);
+	get("monoi", _monoI);
+	get("monoz", _monoZ);
+	get("monofont", _monoFont, "Liberation Mono");
+	get("propsize", _propSize, 15.5);
+	get("propr", _propR);
+	get("propb", _propR);
+	get("propi", _propI);
+	get("propz", _propZ);
+	get("propfont", _propFont, "Linux Libertine O");
+	get("leading", _leading);
+	get("baseline", _baseLine);
+	get("rows", _rows, 25);
+	get("cols", _cols, 60);
+
+	if (ConfMan.hasKey("minrows"))
+		_rows = MAX(_rows, strToInt(ConfMan.get("minrows").c_str()));
+	if (ConfMan.hasKey("maxrows"))
+		_rows = MIN(_rows, strToInt(ConfMan.get("maxrows").c_str()));
+	if (ConfMan.hasKey("mincols"))
+		_cols = MAX(_cols, strToInt(ConfMan.get("mincols").c_str()));
+	if (ConfMan.hasKey("maxcols"))
+		_cols = MIN(_cols, strToInt(ConfMan.get("maxcols").c_str()));
+
+	get("lockrows", _lockRows);
+	get("lockcols", _lockCols);
+	get("wmarginx", _wMarginX, 15);
+	get("wmarginy", _wMarginY, 15);
+	_wMarginSaveX = _wMarginX;
+	_wMarginSaveY = _wMarginY;
+
+	get("wpaddingx", _wPaddingX);
+	get("wpaddingy", _wPaddingY);
+	get("wborderx", _wBorderX, 1);
+	get("wbordery", _wBorderY, 1);
+	get("tmarginx", _tMarginX, 7);
+	get("tmarginy", _tMarginY, 7);
+	get("gamma", _gamma, 1.0);
+	
+	get("caretcolor", _caretColor);
+	get("caretcolor", _caretSave);
+	get("linkcolor", _linkColor, BLUE);
+	get("linkcolor", _linkSave, BLUE);
+	get("bordercolor", _borderColor);
+	get("bordercolor", _borderSave);
+	get("windowcolor", _windowColor, WHITE);
+	get("windowcolor", _windowSave, WHITE);
+	get("lcd", _lcd, 1);
+	get("caretshape", _caretShape, 2);
+
+	_linkStyle = ConfMan.hasKey("linkstyle") && !strToInt(ConfMan.get("linkstyle").c_str()) ? 0 : 1;
+
+	get("scrollwidth", _scrollWidth);
+	get("scrollbg", _scrollBg, SCROLL_BG);
+	get("scrollfg", _scrollFg, SCROLL_FG);
+	get("justify", _justify);
+	get("quotes", _quotes, 1);
+	get("dashes", _dashes, 1);
+	get("spaces", _spaces);
+	get("caps", _caps);
+	get("graphics", _graphics, 1);
+	get("sound", _sound, 1);
+	get("speak", _speak);
+	get("speak_input", _speakInput);
+	get("speak_language", _speakLanguage);
+	get("stylehint", _styleHint, 1);
+
+}
+
+void Conf::get(const Common::String &key, Common::String &field, const char *defaultVal) {
+	field = ConfMan.hasKey(key) ? ConfMan.get(key) : defaultVal;
+	field.trim();
+}
+
+void Conf::get(const Common::String &key, byte *color, const byte *defaultColor) {
+	char r[3], g[3], b[3];
+	Common::String str;
+
+	if (ConfMan.hasKey(key) && (str = ConfMan.get(key)).size() == 6) {
+		r[0] = str[0]; r[1] = str[1]; r[2] = 0;
+		g[0] = str[2]; g[1] = str[3]; g[2] = 0;
+		b[0] = str[4]; b[1] = str[5]; b[2] = 0;
+
+		color[0] = strtol(r, NULL, 16);
+		color[1] = strtol(g, NULL, 16);
+		color[2] = strtol(b, NULL, 16);
+	} else if (defaultColor) {
+		Common::copy(defaultColor, defaultColor + 3, color);
+	} else {
+		Common::fill(color, color + 3, 0);
+	}
+}
+
+void Conf::get(const Common::String &key, int &field, int defaultVal) {
+	field = ConfMan.hasKey(key) ? strToInt(ConfMan.get(key).c_str()) : defaultVal;
+}
+
+void Conf::get(const Common::String &key, FACES &field, FACES defaultFont) {
+	field = ConfMan.hasKey(key) ? Fonts::getId(ConfMan.get(key)) : defaultFont;
+}
+
+void Conf::get(const Common::String &key, double &field, double defaultVal) {
+	field = ConfMan.hasKey(key) ?  atof(ConfMan.get(key).c_str()) : defaultVal;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/conf.h b/engines/gargoyle/conf.h
new file mode 100644
index 0000000..2dbb29f
--- /dev/null
+++ b/engines/gargoyle/conf.h
@@ -0,0 +1,118 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_CONF_H
+#define GARGOYLE_CONF_H
+
+#include "gargoyle/glk_types.h"
+#include "gargoyle/fonts.h"
+
+namespace Gargoyle {
+
+class Conf {
+private:
+	/**
+	 * Get a string
+	 */
+	void get(const Common::String &key, Common::String &field, const char *defaultVal = nullptr);
+
+	/**
+	 * Get a color
+	 */
+	void get(const Common::String &key, byte *color, const byte *defaultColor = nullptr);
+
+	/**
+	 * Get a font name into a font Id
+	 */
+	void get(const Common::String &key, FACES &field, FACES defaultFont);
+
+	/**
+	 * Get a numeric value
+	 */
+	void get(const Common::String &key, int &field, int defaultVal = 0);
+
+	/**
+	 * Get a double
+	 */
+	void get(const Common::String &key, double &field, double defaultVal = 0.0);
+
+public:
+	Common::String _morePrompt;
+	byte _moreColor[3], _moreSave[3];
+	FACES _moreFont;
+	int _moreAlign;
+	double _monoAspect;
+	double _propAspect;
+	double _monoSize;
+	Common::String _monoR;
+	Common::String _monoB;
+	Common::String _monoI;
+	Common::String _monoZ;
+	Common::String _monoFont;
+	double _propSize;
+	Common::String _propR;
+	Common::String _propB;
+	Common::String _propI;
+	Common::String _propZ;
+	Common::String _propFont;
+	int _leading;
+	int _baseLine;
+	int _cols, _rows;
+	int _lockCols, _lockRows;
+	int _wMarginX, _wMarginY;
+	int _wMarginSaveX, _wMarginSaveY;
+	int _wPaddingX, _wPaddingY;
+	int _wBorderX, _wBorderY;
+	int _tMarginX, _tMarginY;
+	double _gamma;
+	byte _caretColor[3], _caretSave[3];
+	byte _linkColor[3], _linkSave[3];
+	byte _borderColor[3], _borderSave[3];
+	byte _windowColor[3], _windowSave[3];
+	int _lcd;
+	int _caretShape;
+	int _linkStyle;
+	int _scrollWidth;
+	byte _scrollBg[3], _scrollFg[3];
+	int _justify;
+	int _quotes;
+	int _dashes;
+	int _spaces;
+	int _caps;
+	int _graphics;
+	int _sound;
+	int _speak;
+	int _speakInput;
+	Common::String _speakLanguage;
+	int _styleHint;
+public:
+	/**
+	 * Constructor
+	 */
+	Conf();
+};
+
+extern Conf *g_conf;
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
new file mode 100644
index 0000000..9c84a12
--- /dev/null
+++ b/engines/gargoyle/fonts.cpp
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 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 "gargoyle/fonts.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+FACES Fonts::getId(const Common::String &name) {
+	if (name == "monor") return MONOR;
+	if (name == "monob") return MONOB;
+	if (name == "monoi") return MONOI;
+	if (name == "monoz") return MONOZ;
+	if (name == "propr") return PROPR;
+	if (name == "propb") return PROPB;
+	if (name == "propi") return PROPI;
+	if (name == "propz") return PROPZ;
+	return MONOR;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
new file mode 100644
index 0000000..f2f701a
--- /dev/null
+++ b/engines/gargoyle/fonts.h
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_FONTS_H
+#define GARGOYLE_FONTS_H
+
+#include "gargoyle/glk_types.h"
+#include "common/str.h"
+
+namespace Gargoyle {
+
+enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
+enum TYPES { MONOF, PROPF };
+enum STYLES { FONTR, FONTB, FONTI, FONTZ };
+
+class Fonts {
+public:
+	/**
+	 * Get the index/id of a font by name
+	 */
+	static FACES getId(const Common::String &name);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index a481ae9..d3dd35b 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -29,6 +29,7 @@
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/conf.h"
 #include "gargoyle/events.h"
 #include "gargoyle/streams.h"
 #include "gargoyle/windows.h"
@@ -36,11 +37,12 @@
 namespace Gargoyle {
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"),
+		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _conf(nullptr),
 		_events(nullptr), _screen(nullptr), _windows(nullptr) {
 }
 
 GargoyleEngine::~GargoyleEngine() {
+	delete _conf;
 	delete _events;
 	delete _screen;
 	delete _streams;
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index e7d5df2..39799d7 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -33,6 +33,7 @@
 
 namespace Gargoyle {
 
+class Conf;
 class Events;
 class Windows;
 class Streams;
@@ -90,6 +91,7 @@ protected:
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
 public:
+	Conf *_conf;
 	Events *_events;
 	Streams *_streams;
 	Windows *_windows;
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index b49fb63..e81e806 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -23,6 +23,7 @@
 #include "gargoyle/glk.h"
 #include "gargoyle/events.h"
 #include "gargoyle/streams.h"
+#include "gargoyle/string.h"
 #include "gargoyle/windows.h"
 
 namespace Gargoyle {
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index c7b505e..b6f0d29 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -219,10 +219,6 @@ enum StyleHint {
 	stylehint_just_RightFlush = 3,
 };
 
-enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
-enum TYPES { MONOF, PROPF };
-enum STYLES { FONTR, FONTB, FONTI, FONTZ };
-
 #ifdef GLK_MODULE_IMAGE
 
 enum ImageAlign {
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 05b7f5f..925c66d 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -1,12 +1,15 @@
 MODULE := engines/gargoyle
 
 MODULE_OBJS := \
+	conf.o \
 	detection.o \
 	events.o \
+	fonts.o \
 	gargoyle.o \
 	glk.o \
 	picture.o \
 	streams.o \
+	string.o \
 	windows.o \
 	scott/detection.o \
 	scott/scott.o
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 62e43e3..382ce28 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -144,13 +144,4 @@ Stream *Streams::getFirst(uint32 *rock) {
 	return _streamList;
 }
 
-/*--------------------------------------------------------------------------*/
-
-size_t strlen_uni(const uint32 *s) {
-	size_t len = 0;
-	while (*s++)
-		++len;
-	return len;
-}
-
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index db21d58..3a80366 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -215,13 +215,6 @@ public:
 	Stream *getCurrent() const { return _currentStream; }
 };
 
-
-
-/*
- * Get the length of a unicode string
- */
-size_t strlen_uni(const uint32 *s);
-
 } // End of namespace Gargoyle
 
 #endif
diff --git a/engines/gargoyle/string.cpp b/engines/gargoyle/string.cpp
new file mode 100644
index 0000000..4b68978
--- /dev/null
+++ b/engines/gargoyle/string.cpp
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY{} without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program{} if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/conf.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+
+size_t strlen_uni(const uint32 *s) {
+	size_t len = 0;
+	while (*s++)
+		++len;
+	return len;
+}
+
+int strToInt(const char *s) {
+	if (!*s)
+		// No string at all
+		return 0;
+	else if (toupper(s[strlen(s) - 1]) != 'H')
+		// Standard decimal string
+		return atoi(s);
+
+	// Hexadecimal string
+	uint tmp = 0;
+	int read = sscanf(s, "%xh", &tmp);
+	if (read < 1)
+		error("strToInt failed on string \"%s\"", s);
+	return (int)tmp;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/string.h b/engines/gargoyle/string.h
new file mode 100644
index 0000000..2ab2925
--- /dev/null
+++ b/engines/gargoyle/string.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_STRING_H
+#define GARGOYLE_STRING_H
+
+#include "gargoyle/string.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+/*
+ * Get the length of a unicode string
+ */
+size_t strlen_uni(const uint32 *s);
+
+/**
+ * Converts a decimal or hexadecimal string into a number
+ */
+int strToInt(const char *s);
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 95f1ed4..0eb5088 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -28,6 +28,7 @@
 #include "common/rect.h"
 #include "graphics/screen.h"
 #include "gargoyle/glk_types.h"
+#include "gargoyle/fonts.h"
 #include "gargoyle/picture.h"
 #include "gargoyle/streams.h"
 
@@ -125,7 +126,7 @@ public:
  * Window styles
  */
 struct WindowStyle {
-	int font;
+	FACES font;
 	byte bg[3];
 	byte fg[3];
 	int reverse;


Commit: bbd744ad4731784e13c8e92048a75531300e3fe4
    https://github.com/scummvm/scummvm/commit/bbd744ad4731784e13c8e92048a75531300e3fe4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Further config loading

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/conf.h
    engines/gargoyle/windows.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index c296254..0b8eda8 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -23,6 +23,7 @@
 #include "gargoyle/conf.h"
 #include "gargoyle/fonts.h"
 #include "gargoyle/string.h"
+#include "gargoyle/windows.h"
 #include "common/config-manager.h"
 
 namespace Gargoyle {
@@ -32,6 +33,36 @@ const byte BLUE[3] = { 0x00, 0x00, 0x60 };
 const byte SCROLL_BG[3] = { 0xb0, 0xb0, 0xb0 };
 const byte SCROLL_FG[3] = { 0x80, 0x80, 0x80 };
 
+WindowStyle T_STYLES[style_NUMSTYLES] = {
+	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Normal
+	{ PROPI,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Emphasized
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Preformatted
+	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Header
+	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Subheader
+	{ PROPZ,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
+	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
+	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
+	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Input
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User1
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User2
+};
+
+WindowStyle G_STYLES[style_NUMSTYLES] = {
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Normal
+	{ MONOI,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Emphasized
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Preformatted
+	{ MONOB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Header
+	{ MONOB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Subheader
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Input
+	{ MONOR,{ 0x60,0x60,0x60 },{ 0xff,0xff,0xff }, 0 }, ///< User1
+	{ MONOR,{ 0x60,0x60,0x60 },{ 0xff,0xff,0xff }, 0 }, ///< User2
+};
+
+Conf *g_conf;
+
 Conf::Conf() {
 	g_conf = this;
 
@@ -111,6 +142,55 @@ Conf::Conf() {
 	get("speak_language", _speakLanguage);
 	get("stylehint", _styleHint, 1);
 
+	Common::copy(T_STYLES, T_STYLES + style_NUMSTYLES, _tStyles);
+	Common::copy(G_STYLES, G_STYLES + style_NUMSTYLES, _gStyles);
+
+	char buffer[255];
+	const char *const TG_COLOR[2] = { "tcolor", "gcolor" };
+	for (int idx = 0; idx < 2; ++idx) {
+		if (!ConfMan.hasKey(TG_COLOR[idx]))
+			continue;
+
+		strncpy(buffer, ConfMan.get(TG_COLOR[idx]).c_str(), 254);
+		buffer[255] = '\0';
+		char *style = strtok(buffer, "\r\n\t ");
+		char *fg = strtok(nullptr, "\r\n\t ");
+		char *bg = strtok(nullptr, "\r\n\t ");
+
+		int i = atoi(style);
+		if (i < 0 || i >= style_NUMSTYLES)
+			continue;
+
+		if (idx == 0) {
+			parseColor(fg, _tStyles[i].fg);
+			parseColor(bg, _tStyles[i].bg);
+		} else {
+			parseColor(fg, _gStyles[i].fg);
+			parseColor(bg, _gStyles[i].bg);
+		}
+	}
+
+	const char *const TG_FONT[2] = { "tfont", "gfont" };
+	for (int idx = 0; idx < 2; ++idx) {
+		if (!ConfMan.hasKey(TG_FONT[idx]))
+			continue;
+
+		strncpy(buffer, ConfMan.get(TG_FONT[idx]).c_str(), 254);
+		buffer[255] = '\0';
+		char *style = strtok(buffer, "\r\n\t ");
+		char *font = strtok(nullptr, "\r\n\t ");
+		int i = atoi(style);
+		if (i < 0 || i >= style_NUMSTYLES)
+			continue;
+
+		if (idx == 0)
+			_tStyles[i].font = Fonts::getId(font);
+		else
+			_gStyles[i].font = Fonts::getId(font);
+	}
+
+	Common::copy(_tStyles, _tStyles + style_NUMSTYLES, _tStylesDefault);
+	Common::copy(_gStyles, _gStyles + style_NUMSTYLES, _gStylesDefault);
 }
 
 void Conf::get(const Common::String &key, Common::String &field, const char *defaultVal) {
@@ -119,17 +199,8 @@ void Conf::get(const Common::String &key, Common::String &field, const char *def
 }
 
 void Conf::get(const Common::String &key, byte *color, const byte *defaultColor) {
-	char r[3], g[3], b[3];
-	Common::String str;
-
-	if (ConfMan.hasKey(key) && (str = ConfMan.get(key)).size() == 6) {
-		r[0] = str[0]; r[1] = str[1]; r[2] = 0;
-		g[0] = str[2]; g[1] = str[3]; g[2] = 0;
-		b[0] = str[4]; b[1] = str[5]; b[2] = 0;
-
-		color[0] = strtol(r, NULL, 16);
-		color[1] = strtol(g, NULL, 16);
-		color[2] = strtol(b, NULL, 16);
+	if (ConfMan.hasKey(key)) {
+		parseColor(ConfMan.get(key), color);
 	} else if (defaultColor) {
 		Common::copy(defaultColor, defaultColor + 3, color);
 	} else {
@@ -149,4 +220,18 @@ void Conf::get(const Common::String &key, double &field, double defaultVal) {
 	field = ConfMan.hasKey(key) ?  atof(ConfMan.get(key).c_str()) : defaultVal;
 }
 
+void Conf::parseColor(const Common::String &str, byte *color) {
+	char r[3], g[3], b[3];
+
+	if (str.size() == 6) {
+		r[0] = str[0]; r[1] = str[1]; r[2] = 0;
+		g[0] = str[2]; g[1] = str[3]; g[2] = 0;
+		b[0] = str[4]; b[1] = str[5]; b[2] = 0;
+
+		color[0] = strtol(r, NULL, 16);
+		color[1] = strtol(g, NULL, 16);
+		color[2] = strtol(b, NULL, 16);
+	}
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/conf.h b/engines/gargoyle/conf.h
index 2dbb29f..c63cdc0 100644
--- a/engines/gargoyle/conf.h
+++ b/engines/gargoyle/conf.h
@@ -25,6 +25,7 @@
 
 #include "gargoyle/glk_types.h"
 #include "gargoyle/fonts.h"
+#include "gargoyle/windows.h"
 
 namespace Gargoyle {
 
@@ -55,6 +56,10 @@ private:
 	 */
 	void get(const Common::String &key, double &field, double defaultVal = 0.0);
 
+	/**
+	 * Parse a color
+	 */
+	void parseColor(const Common::String &str, byte *color);
 public:
 	Common::String _morePrompt;
 	byte _moreColor[3], _moreSave[3];
@@ -104,6 +109,10 @@ public:
 	int _speakInput;
 	Common::String _speakLanguage;
 	int _styleHint;
+	WindowStyle _tStyles[style_NUMSTYLES];
+	WindowStyle _gStyles[style_NUMSTYLES];
+	WindowStyle _tStylesDefault[style_NUMSTYLES];
+	WindowStyle _gStylesDefault[style_NUMSTYLES];
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 2c8680b..edea1ab 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "gargoyle/windows.h"
+#include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/streams.h"
 #include "common/algorithm.h"
@@ -59,34 +60,6 @@ int Windows::_overrideFgVal;
 int Windows::_overrideBgVal;
 
 
-WindowStyle T_STYLES[style_NUMSTYLES] = {
-	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Normal
-	{ PROPI,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Emphasized
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Preformatted
-	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Header
-	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Subheader
-	{ PROPZ,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
-	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
-	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
-	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Input
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User1
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User2
-};
-
-WindowStyle G_STYLES[style_NUMSTYLES] = {
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Normal
-	{ MONOI,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Emphasized
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Preformatted
-	{ MONOB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Header
-	{ MONOB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Subheader
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Input
-	{ MONOR,{ 0x60,0x60,0x60 },{ 0xff,0xff,0xff }, 0 }, ///< User1
-	{ MONOR,{ 0x60,0x60,0x60 },{ 0xff,0xff,0xff }, 0 }, ///< User2
-};
-
 /*--------------------------------------------------------------------------*/
 
 Windows::Windows(GargoyleEngine *engine, Graphics::Screen *screen) :
@@ -330,7 +303,7 @@ TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows,
 	inarrayrock.num = 0;
 	line_terminators = nullptr;
 
-	Common::copy(&G_STYLES[0], &G_STYLES[style_NUMSTYLES], styles);
+	Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], styles);
 }
 
 void TextGridWindow::rearrange(const Common::Rect &box) {
@@ -381,7 +354,7 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windo
 	_type = wintype_TextBuffer;
 	Common::fill(&history[0], &history[HISTORYLEN], nullptr);
 
-	Common::copy(&T_STYLES[0], &T_STYLES[style_NUMSTYLES], styles);
+	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], styles);
 }
 
 void TextBufferWindow::rearrange(const Common::Rect &box) {


Commit: 0bc3da9e800b928ac31663180427cd048748814d
    https://github.com/scummvm/scummvm/commit/0bc3da9e800b928ac31663180427cd048748814d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Remove duplicated Windows fields that are now in Conf

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/conf.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/streams.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 0b8eda8..4e8b823 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -25,6 +25,7 @@
 #include "gargoyle/string.h"
 #include "gargoyle/windows.h"
 #include "common/config-manager.h"
+#include "common/system.h"
 
 namespace Gargoyle {
 
@@ -65,6 +66,9 @@ Conf *g_conf;
 
 Conf::Conf() {
 	g_conf = this;
+	_imageW = g_system->getWidth();
+	_imageH = g_system->getHeight();
+	_cellW = _cellH = 8;
 
 	get("moreprompt", _morePrompt, "\207 more \207");
 	get("morecolor", _moreColor);
diff --git a/engines/gargoyle/conf.h b/engines/gargoyle/conf.h
index c63cdc0..5c4446a 100644
--- a/engines/gargoyle/conf.h
+++ b/engines/gargoyle/conf.h
@@ -113,6 +113,9 @@ public:
 	WindowStyle _gStyles[style_NUMSTYLES];
 	WindowStyle _tStylesDefault[style_NUMSTYLES];
 	WindowStyle _gStylesDefault[style_NUMSTYLES];
+
+	int _imageW, _imageH;
+	int _cellW, _cellH;
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index d3dd35b..a7b819c 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -58,6 +58,7 @@ void GargoyleEngine::initialize() {
 
 	initGraphics(640, 480, false);
 	_screen = new Graphics::Screen();
+	_conf = new Conf();
 	_events = new Events();
 	_streams = new Streams(this);
 	_windows = new Windows(this, _screen);
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 3a80366..e45bcc4 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -104,7 +104,7 @@ public:
 	 * Constructor
 	 */
 	WindowStream(Streams *streams, Window *window, uint32 rock = 0, bool unicode = true) :
-		Stream(streams, true, false, rock, unicode), _window(window) {}
+		Stream(streams, false, true, rock, unicode), _window(window) {}
 
 	/**
 	 * Close the stream
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index edea1ab..e39e542 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -32,27 +32,6 @@ namespace Gargoyle {
 #define MAGIC_WINDOW_NUM (9876)
 #define GLI_SUBPIX 8
 
-bool Windows::_confLockCols;
-bool Windows::_confLockRows;
-int Windows::_wMarginx;
-int Windows::_wMarginy;
-int Windows::_wPaddingx;
-int Windows::_wPaddingy;
-int Windows::_wBorderx;
-int Windows::_wBordery;
-int Windows::_tMarginx;
-int Windows::_tMarginy;
-int Windows::_wMarginXsave;
-int Windows::_wMarginYsave;
-int Windows::_cols;
-int Windows::_rows;
-int Windows::_imageW;
-int Windows::_imageH;
-int Windows::_cellW;
-int Windows::_cellH;
-int Windows::_baseLine;
-int Windows::_leading;
-int Windows::_scrollWidth;
 bool Windows::_overrideReverse;
 bool Windows::_overrideFgSet;
 bool Windows::_overrideBgSet;
@@ -66,26 +45,6 @@ Windows::Windows(GargoyleEngine *engine, Graphics::Screen *screen) :
 		_engine(engine), _screen(screen), _forceRedraw(true), _moreFocus(false),
 		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr), _mask(nullptr),
 		_claimSelect(0) {
-	_imageW = _screen->w;
-	_imageH = _screen->h;
-	_cellW = _cellH = 8;
-	_confLockCols = false;
-	_confLockRows = false;
-	_wMarginx = 15;
-	_wMarginy = 15;
-	_wPaddingx = 0;
-	_wPaddingy = 0;
-	_wBorderx = 1;
-	_wBordery = 1;
-	_tMarginx = 7;
-	_tMarginy = 7;
-	_wMarginXsave = 15;
-	_wMarginYsave = 15;
-	_cols = 60;
-	_rows = 25;
-	_baseLine = 15;
-	_leading = 20;
-	_scrollWidth = 0;
 	_overrideReverse = false;
 	_overrideFgSet = false;
 	_overrideBgSet = false;
@@ -218,26 +177,26 @@ void Windows::rearrange() {
 	if (_rootWin) {
 		Common::Rect box;
 
-		if (_confLockCols) {
-			int desired_width = _wMarginXsave * 2 + _cellW * _cols;
-			if (desired_width > _imageW)
-				_wMarginx = _wMarginXsave;
+		if (g_conf->_lockCols) {
+			int desired_width = g_conf->_wMarginSaveX * 2 + g_conf->_cellW * g_conf->_cols;
+			if (desired_width > g_conf->_imageW)
+				g_conf->_wMarginX = g_conf->_wMarginSaveX;
 			else
-				_wMarginx = (_imageW - _cellW * _cols) / 2;
+				g_conf->_wMarginX = (g_conf->_imageW - g_conf->_cellW * g_conf->_cols) / 2;
 		}
 
-		if (_confLockRows) {
-			int desired_height = _wMarginYsave * 2 + _cellH * _rows;
-			if (desired_height > _imageH)
-				_wMarginy = _wMarginYsave;
+		if (g_conf->_lockRows) {
+			int desired_height = g_conf->_wMarginSaveY * 2 + g_conf->_cellH * g_conf->_rows;
+			if (desired_height > g_conf->_imageH)
+				g_conf->_wMarginY = g_conf->_wMarginSaveY;
 			else
-				_wMarginy = (_imageH - _cellH * _rows) / 2;
+				g_conf->_wMarginY = (g_conf->_imageH - g_conf->_cellH * g_conf->_rows) / 2;
 		}
 
-		box.left = _wMarginx;
-		box.top = _wMarginy;
-		box.right = _imageW - _wMarginx;
-		box.bottom = _imageH - _wMarginy;
+		box.left = g_conf->_wMarginX;
+		box.top = g_conf->_wMarginY;
+		box.right = g_conf->_imageW - g_conf->_wMarginX;
+		box.bottom = g_conf->_imageH - g_conf->_wMarginY;
 
 		_rootWin->rearrange(box);
 	}
@@ -310,8 +269,8 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 	Window::rearrange(box);
 	int newwid, newhgt;
 
-	newwid = box.width() / Windows::_cellW;
-	newhgt = box.height() / Windows::_cellH;
+	newwid = box.width() / g_conf->_cellW;
+	newhgt = box.height() / g_conf->_cellH;
 
 	if (newwid == width && newhgt == height)
 		return;
@@ -328,9 +287,14 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 }
 
 void TextGridWindow::touch(int line) {
-	int y = bbox.top + line * Windows::_leading;
+	int y = bbox.top + line * g_conf->_leading;
 	lines[line].dirty = true;
-	_windows->repaint(Common::Rect(bbox.left, y, bbox.right, y + Windows::_leading));
+	_windows->repaint(Common::Rect(bbox.left, y, bbox.right, y + g_conf->_leading));
+}
+
+glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
+	return vertical ? size * g_conf->_cellW + g_conf->_tMarginX * 2 :
+		size * g_conf->_cellH + g_conf->_tMarginY * 2;
 }
 
 /*--------------------------------------------------------------------------*/
@@ -362,11 +326,11 @@ void TextBufferWindow::rearrange(const Common::Rect &box) {
 	int newwid, newhgt;
 	int rnd;
 
-	newwid = (box.width() - Windows::_tMarginx * 2 - Windows::_scrollWidth) / Windows::_cellW;
-	newhgt = (box.height() - Windows::_tMarginy * 2) / Windows::_cellH;
+	newwid = (box.width() - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) / g_conf->_cellW;
+	newhgt = (box.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
 
 	/* align text with bottom */
-	rnd = newhgt * Windows::_cellH + Windows::_tMarginy * 2;
+	rnd = newhgt * g_conf->_cellH + g_conf->_tMarginY * 2;
 	yadj = (box.height() - rnd);
 	bbox.top += (box.height() - rnd);
 
@@ -576,8 +540,8 @@ bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
 		if (lines[0].rpic || numchars)
 			return false;
 
-		radjw = (pic->w + Windows::_tMarginx) * GLI_SUBPIX;
-		radjn = (pic->h + Windows::_cellH - 1) / Windows::_cellH;
+		radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
+		radjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
 		lines[0].rpic = pic;
 		lines[0].rm = radjw;
 		lines[0].rhyper = linkval;
@@ -588,8 +552,8 @@ bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
 		if (lines[0].lpic || numchars)
 			return false;
 
-		ladjw = (pic->w + Windows::_tMarginx) * GLI_SUBPIX;
-		ladjn = (pic->h + Windows::_cellH - 1) / Windows::_cellH;
+		ladjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
+		ladjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
 		lines[0].lpic = pic;
 		lines[0].lm = ladjw;
 		lines[0].lhyper = linkval;
@@ -614,7 +578,7 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 
 	gli_tts_speak(&ch, 1);
 
-	pw = (bbox.right - bbox.left - Windows::_tMarginx * 2 - gli_scroll_width) * GLI_SUBPIX;
+	pw = (bbox.right - bbox.left - g_conf->_tMarginX * 2 - gli_scroll_width) * GLI_SUBPIX;
 	pw = pw - 2 * SLOP - radjw - ladjw;
 
 	color = gli_override_bg_set ? gli_window_color : bgcolor;
@@ -760,10 +724,14 @@ void TextBufferWindow::flowBreak() {
 }
 
 void TextBufferWindow::touch(int line) {
-	int y = bbox.top + Windows::_tMarginy + (height - line - 1) * Windows::_leading;
+	int y = bbox.top + g_conf->_tMarginY + (height - line - 1) * g_conf->_leading;
 	lines[line].dirty = 1;
 	_windows->clearSelection();
-	_windows->repaint(Common::Rect(bbox.left, y - 2, bbox.right, y + Windows::_leading + 2));
+	_windows->repaint(Common::Rect(bbox.left, y - 2, bbox.right, y + g_conf->_leading + 2));
+}
+
+glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
+	return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
 }
 
 /*--------------------------------------------------------------------------*/
@@ -867,9 +835,9 @@ void PairWindow::rearrange(const Common::Rect &box) {
 
 	// We now figure split.
 	if (_vertical)
-		splitwid = Windows::_wPaddingx; // want border?
+		splitwid = g_conf->_wPaddingX; // want border?
 	else
-		splitwid = Windows::_wPaddingy; // want border?
+		splitwid = g_conf->_wPaddingY; // want border?
 
 	switch (_division) {
 	case winmethod_Proportional:
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 0eb5088..6f7a016 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -74,24 +74,6 @@ private:
 	 */
 	void rearrange();
 public:
-	static bool _confLockCols, _confLockRows;
-	static int _wMarginx;
-	static int _wMarginy;
-	static int _wPaddingx;
-	static int _wPaddingy;
-	static int _wBorderx;
-	static int _wBordery;
-	static int _tMarginx;
-	static int _tMarginy;
-	static int _wMarginXsave;
-	static int _wMarginYsave;
-	static int _cols;
-	static int _rows;
-	static int _imageW, _imageH;
-	static int _cellW, _cellH;
-	static int _baseLine;
-	static int _leading;
-	static int _scrollWidth;
 	static bool _overrideReverse;
 	static bool _overrideFgSet;
 	static bool _overrideBgSet;
@@ -296,10 +278,7 @@ public:
 	/**
 	 * Get window split size within parent pair window
 	 */
-	virtual glui32 getSplit(glui32 size, bool vertical) const override {
-		return vertical ? size * Windows::_cellW + Windows::_tMarginx * 2 :
-			size * Windows::_cellH + Windows::_tMarginy * 2;
-	}
+	virtual glui32 getSplit(glui32 size, bool vertical) const override;
 };
 
 /**
@@ -400,9 +379,7 @@ public:
 	/**
 	 * Get window split size within parent pair window
 	 */
-	virtual glui32 getSplit(glui32 size, bool vertical) const override {
-		return (vertical) ? size * Windows::_cellW : size * Windows::_cellH;
-	}
+	virtual glui32 getSplit(glui32 size, bool vertical) const override;
 
 	/**
 	 * Clear the window


Commit: d314cd39ee42c2b37fde0fa9d384fae67683cbea
    https://github.com/scummvm/scummvm/commit/d314cd39ee42c2b37fde0fa9d384fae67683cbea
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Add detection for other games

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


diff --git a/engines/gargoyle/scott/detection.cpp b/engines/gargoyle/scott/detection.cpp
index d2ed439..6c9f5c4 100644
--- a/engines/gargoyle/scott/detection.cpp
+++ b/engines/gargoyle/scott/detection.cpp
@@ -35,6 +35,20 @@ struct ScottGame {
 
 const ScottGame SCOTT_GAMES[] = {
 	{ "ae541fc1085da2f7d561b72ed20a6bc1", 18003, "Adventureland" },
+	{ "cbd47ab4fcfe00231ffd71d52378d410", 18482, "Pirate Adventure" },
+	{ "9251ab2c64e63559d8a6e9e6246760a5", 17227, "Mission Impossible" },
+	{ "be849c5747c7fc3b201984afb4403b8e", 18140, "Voodoo Castle" },
+	{ "85b75b6079b5ee572b5259b29a0e5d21", 19999, "The Count" },
+	{ "c423cae841ac1927b5b2e503607b21bc", 20115, "Strange Odyssey" },
+	{ "326b98b991d401605074e64d474ce566", 19700, "Mystery Fun House" },
+	{ "8ef9010399f055da9adb15ce7745a11c", 20320, "Pyramid Of Doom" },
+	{ "fcdcca8b2acf76ba2d0006cefa3630a1", 20687, "Ghost Town" },
+	{ "c8aaa80f07c40fa8e4b17432644919dc", 22669, "Save Island, Part 1" },
+	{ "2add0f28d9b236c866890cdf8d86ee60", 21169, "Savage Island, Part 2" },
+	{ "675126bd0477e8ed9230ad3db5afc45f", 21401, "The Golden Voyage" },
+	{ "0ef0def798d895ed766041fa99dd28a0", 22346, "Adventure 13" },
+	{ "0bf1bcc649422798332a38c88588fdff", 22087, "Adventure 14" },
+	{ "a0a5423967287dae9cbeb9abe8324479", 21038, "Buckaroo Banzai" },
 	{ nullptr, 0, nullptr }
 };
 


Commit: cc54beadce8852c29be883e957fbfe5f548f8946
    https://github.com/scummvm/scummvm/commit/cc54beadce8852c29be883e957fbfe5f548f8946
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Start of window character output

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/conf.h
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk_types.h
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 4e8b823..125e9f4 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -139,12 +139,13 @@ Conf::Conf() {
 	get("dashes", _dashes, 1);
 	get("spaces", _spaces);
 	get("caps", _caps);
-	get("graphics", _graphics, 1);
-	get("sound", _sound, 1);
+	get("graphics", _graphics, true);
+	get("sound", _sound, true);
 	get("speak", _speak);
 	get("speak_input", _speakInput);
 	get("speak_language", _speakLanguage);
 	get("stylehint", _styleHint, 1);
+	get("safeclicks", _safeClicks);
 
 	Common::copy(T_STYLES, T_STYLES + style_NUMSTYLES, _tStyles);
 	Common::copy(G_STYLES, G_STYLES + style_NUMSTYLES, _gStyles);
@@ -216,6 +217,10 @@ void Conf::get(const Common::String &key, int &field, int defaultVal) {
 	field = ConfMan.hasKey(key) ? strToInt(ConfMan.get(key).c_str()) : defaultVal;
 }
 
+void Conf::get(const Common::String &key, bool &field, bool defaultVal) {
+	field = ConfMan.hasKey(key) ? strToInt(ConfMan.get(key).c_str()) != 0 : defaultVal;
+}
+
 void Conf::get(const Common::String &key, FACES &field, FACES defaultFont) {
 	field = ConfMan.hasKey(key) ? Fonts::getId(ConfMan.get(key)) : defaultFont;
 }
diff --git a/engines/gargoyle/conf.h b/engines/gargoyle/conf.h
index 5c4446a..05dc6c9 100644
--- a/engines/gargoyle/conf.h
+++ b/engines/gargoyle/conf.h
@@ -52,6 +52,11 @@ private:
 	void get(const Common::String &key, int &field, int defaultVal = 0);
 
 	/**
+	 * Get a numeric value
+	 */
+	void get(const Common::String &key, bool &field, bool defaultVal = false);
+
+	/**
 	 * Get a double
 	 */
 	void get(const Common::String &key, double &field, double defaultVal = 0.0);
@@ -103,12 +108,13 @@ public:
 	int _dashes;
 	int _spaces;
 	int _caps;
-	int _graphics;
-	int _sound;
-	int _speak;
-	int _speakInput;
+	bool _graphics;
+	bool _sound;
+	bool _speak;
+	bool _speakInput;
 	Common::String _speakLanguage;
 	int _styleHint;
+	bool _safeClicks;
 	WindowStyle _tStyles[style_NUMSTYLES];
 	WindowStyle _gStyles[style_NUMSTYLES];
 	WindowStyle _tStylesDefault[style_NUMSTYLES];
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 93021ef..1f7cc6a 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -28,4 +28,8 @@ void Events::pollEvents() {
 	// TODO
 }
 
+void Events::clearEvent(Event *ev) {
+	// TODO
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index ac66d0e..476be40 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -27,12 +27,97 @@
 
 namespace Gargoyle {
 
+class Window;
+
+/**
+ * Event types
+ */
+enum EvType {
+	evtype_None         = 0,
+	evtype_Timer        = 1,
+	evtype_CharInput    = 2,
+	evtype_LineInput    = 3,
+	evtype_MouseInput   = 4,
+	evtype_Arrange      = 5,
+	evtype_Redraw       = 6,
+	evtype_SoundNotify  = 7,
+	evtype_Hyperlink    = 8,
+	evtype_VolumeNotify = 9,
+
+	// ScummVM custom events
+	evtype_Quit			= 99
+};
+
+/**
+ * Keycodes
+ */
+enum Keycode {
+	keycode_Unknown  = 0xffffffffU,
+	keycode_Left     = 0xfffffffeU,
+	keycode_Right    = 0xfffffffdU,
+	keycode_Up       = 0xfffffffcU,
+	keycode_Down     = 0xfffffffbU,
+	keycode_Return   = 0xfffffffaU,
+	keycode_Delete   = 0xfffffff9U,
+	keycode_Escape   = 0xfffffff8U,
+	keycode_Tab      = 0xfffffff7U,
+	keycode_PageUp   = 0xfffffff6U,
+	keycode_PageDown = 0xfffffff5U,
+	keycode_Home     = 0xfffffff4U,
+	keycode_End      = 0xfffffff3U,
+	keycode_Func1    = 0xffffffefU,
+	keycode_Func2    = 0xffffffeeU,
+	keycode_Func3    = 0xffffffedU,
+	keycode_Func4    = 0xffffffecU,
+	keycode_Func5    = 0xffffffebU,
+	keycode_Func6    = 0xffffffeaU,
+	keycode_Func7    = 0xffffffe9U,
+	keycode_Func8    = 0xffffffe8U,
+	keycode_Func9    = 0xffffffe7U,
+	keycode_Func10   = 0xffffffe6U,
+	keycode_Func11   = 0xffffffe5U,
+	keycode_Func12   = 0xffffffe4U,
+
+	// non standard keycodes
+	keycode_Erase          = 0xffffef7fU,
+	keycode_MouseWheelUp   = 0xffffeffeU,
+	keycode_MouseWheelDown = 0xffffefffU,
+	keycode_SkipWordLeft   = 0xfffff000U,
+	keycode_SkipWordRight  = 0xfffff001U,
+
+	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
+	keycode_MAXVAL = 28U
+};
+
+/**
+ * Event structure
+ */
+struct Event {
+	EvType _type;
+	Window *_window;
+	uint32 _val1, _val2;
+
+	/**
+	 * Constructor
+	 */
+	Event() : _type(evtype_None), _window(nullptr), _val1(0), _val2(0) {}
+};
+
 class Events {
 public:
+	bool _forceClick;
+public:
+	/**
+	 * Constructor
+	 */
+	Events() : _forceClick(false) {}
+
 	/**
 	 * Checks for new events
 	 */
 	void pollEvents();
+
+	void clearEvent(Event *ev);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index a7b819c..2ca14bf 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -36,9 +36,12 @@
 
 namespace Gargoyle {
 
+GargoyleEngine *g_vm;
+
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _conf(nullptr),
 		_events(nullptr), _screen(nullptr), _windows(nullptr) {
+	g_vm = this;
 }
 
 GargoyleEngine::~GargoyleEngine() {
@@ -60,8 +63,8 @@ void GargoyleEngine::initialize() {
 	_screen = new Graphics::Screen();
 	_conf = new Conf();
 	_events = new Events();
-	_streams = new Streams(this);
-	_windows = new Windows(this, _screen);
+	_streams = new Streams();
+	_windows = new Windows(_screen);
 }
 
 Common::Error GargoyleEngine::run() {
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 39799d7..976221a 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -125,6 +125,8 @@ public:
 	const Common::String &GargoyleEngine::getFilename() const;
 };
 
+extern GargoyleEngine *g_vm;
+
 } // End of namespace Gargoyle
 
 #endif
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index e81e806..ba62de1 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -187,11 +187,15 @@ strid_t Glk::glk_stream_get_current(void) {
 }
 
 void Glk::glk_put_char(unsigned char ch) {
-	// TODO
+	_streams->getCurrent()->putChar(ch);
 }
 
 void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
-	// TODO
+	if (str) {
+		str->putChar(ch);
+	} else {
+		warning("put_char_stream: invalid ref");
+	}
 }
 
 void Glk::glk_put_string(const char *s) {
@@ -375,27 +379,39 @@ glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
 }
 
 void Glk::glk_put_char_uni(glui32 ch) {
-	glk_put_char_stream_uni(_streams->getCurrent(), ch);
+	_streams->getCurrent()->putCharUni(ch);
 }
 
 void Glk::glk_put_string_uni(glui32 *s) {
-	glk_put_buffer_stream_uni(_streams->getCurrent(), s, strlen_uni(s));
+	_streams->getCurrent()->putBufferUni(s, strlen_uni(s));
 }
 
 void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
-	glk_put_buffer_stream_uni(_streams->getCurrent(), buf, len);
+	_streams->getCurrent()->putBufferUni(buf, len);
 }
 
 void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
-//	str->writeUint32LE(ch);
+	if (str) {
+		str->putCharUni(ch);
+	} else {
+		warning("put_char_stream_uni: invalid ref");
+	}
 }
 
 void Glk::glk_put_string_stream_uni(strid_t str, const glui32 *s) {
-	glk_put_buffer_stream_uni(str, s, strlen_uni(s));
+	if (str) {
+		str->putBufferUni(s, strlen_uni(s));
+	} else {
+		warning("put_string_stream_uni: invalid ref");
+	}
 }
 
 void Glk::glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len) {
-//	while (len-- > 0) str->writeUint32LE(*buf++);
+	if (str) {
+		str->putBufferUni(buf, len);
+	} else {
+		warning("put_buffer_stream_uni: invalid ref");
+	}
 }
 
 glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index b6f0d29..8f84cbe 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -82,60 +82,6 @@ enum Gestalt {
 	gestalt_GarglkText             = 0x1100,
 };
 
-enum EvType {
-	evtype_None         = 0,
-	evtype_Timer        = 1,
-	evtype_CharInput    = 2,
-	evtype_LineInput    = 3,
-	evtype_MouseInput   = 4,
-	evtype_Arrange      = 5,
-	evtype_Redraw       = 6,
-	evtype_SoundNotify  = 7,
-	evtype_Hyperlink    = 8,
-	evtype_VolumeNotify = 9,
-
-	// ScummVM custom events
-	evtype_Quit			= 99
-};
-
-enum Keycode {
-	keycode_Unknown  = 0xffffffffU,
-	keycode_Left     = 0xfffffffeU,
-	keycode_Right    = 0xfffffffdU,
-	keycode_Up       = 0xfffffffcU,
-	keycode_Down     = 0xfffffffbU,
-	keycode_Return   = 0xfffffffaU,
-	keycode_Delete   = 0xfffffff9U,
-	keycode_Escape   = 0xfffffff8U,
-	keycode_Tab      = 0xfffffff7U,
-	keycode_PageUp   = 0xfffffff6U,
-	keycode_PageDown = 0xfffffff5U,
-	keycode_Home     = 0xfffffff4U,
-	keycode_End      = 0xfffffff3U,
-	keycode_Func1    = 0xffffffefU,
-	keycode_Func2    = 0xffffffeeU,
-	keycode_Func3    = 0xffffffedU,
-	keycode_Func4    = 0xffffffecU,
-	keycode_Func5    = 0xffffffebU,
-	keycode_Func6    = 0xffffffeaU,
-	keycode_Func7    = 0xffffffe9U,
-	keycode_Func8    = 0xffffffe8U,
-	keycode_Func9    = 0xffffffe7U,
-	keycode_Func10   = 0xffffffe6U,
-	keycode_Func11   = 0xffffffe5U,
-	keycode_Func12   = 0xffffffe4U,
-
-	// non standard keycodes
-	keycode_Erase          = 0xffffef7fU,
-	keycode_MouseWheelUp   = 0xffffeffeU,
-	keycode_MouseWheelDown = 0xffffefffU,
-	keycode_SkipWordLeft   = 0xfffff000U,
-	keycode_SkipWordRight  = 0xfffff001U,
-
-	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
-	keycode_MAXVAL = 28U
-};
-
 enum Style {
 	style_Normal       = 0,
 	style_Emphasized   = 1,
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 382ce28..b505405 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -21,6 +21,9 @@
  */
 
 #include "gargoyle/streams.h"
+#include "gargoyle/conf.h"
+#include "gargoyle/events.h"
+#include "gargoyle/gargoyle.h"
 #include "gargoyle/windows.h"
 
 namespace Gargoyle {
@@ -62,12 +65,46 @@ void WindowStream::close(StreamResult *result) {
 	warning("cannot close window stream");
 }
 
-void WindowStream::writeChar(unsigned char ch) {
+void WindowStream::putChar(unsigned char ch) {
+	if (!_writable)
+		return;
+	++_writeCount;
 
+	if (_window->line_request || _window->line_request_uni) {
+		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+			_window->cancelLineEvent(nullptr);
+			g_vm->_events->_forceClick = false;
+		} else {
+			warning("putChar: window has pending line request");
+		}
+	}
+
+	_window->putChar(ch);
+	if (_window->_echoStream)
+		_window->_echoStream->putChar(ch);
 }
 
-void WindowStream::writeCharUni(uint32 ch) {
+void WindowStream::putCharUni(uint32 ch) {
+	if (!_writable)
+		return;
+	++_writeCount;
+
+	//TODO
+}
+
+void WindowStream::putBuffer(const unsigned char *buf, size_t len) {
+	if (!_writable)
+		return;
+	++_writeCount;
+	//TODO
+
+}
 
+void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
+	if (!_writable)
+		return;
+	++_writeCount;
+	//TODO
 }
 
 /*--------------------------------------------------------------------------*/
@@ -85,26 +122,56 @@ MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode
 	_bufeof = mode == filemode_Write ? _buf : _bufend;
 }
 
-void MemoryStream::writeChar(unsigned char ch) {
+void MemoryStream::putChar(unsigned char ch) {
+	//TODO
 
 }
 
-void MemoryStream::writeCharUni(uint32 ch) {
+void MemoryStream::putCharUni(uint32 ch) {
+	//TODO
 
 }
 
-/*--------------------------------------------------------------------------*/
+void MemoryStream::putBuffer(const unsigned char *buf, size_t len) {
+	//TODO
+
+}
+
+void MemoryStream::putBufferUni(const uint32 *buf, size_t len) {
+	//TODO
+
+}
 
 /*--------------------------------------------------------------------------*/
 
+FileStream::FileStream(Streams *streams, uint32 rock, bool unicode) :
+	Stream(streams, true, false, rock, unicode) {
+}
+
+void FileStream::putChar(unsigned char ch) {
+	//TODO
+}
+
+void FileStream::putCharUni(uint32 ch) {
+	//TODO
+}
+
+void FileStream::putBuffer(const unsigned char *buf, size_t len) {
+	//TODO
+}
+
+void FileStream::putBufferUni(const uint32 *buf, size_t len) {
+	//TODO
+}
+
 /*--------------------------------------------------------------------------*/
 
-Streams::Streams(GargoyleEngine *engine) : _engine(engine), _streamList(nullptr), _currentStream(nullptr) {
+Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) {
 }
 
 Streams::~Streams() {
 	while (_streamList)
-		deleteStream(_streamList);
+		delete _streamList;
 }
 
 WindowStream *Streams::addWindowStream(Window *window) {
@@ -136,6 +203,12 @@ void Streams::removeStream(Stream *stream) {
 		_streamList = next;
 	if (next)
 		next->_prev = prev;
+
+	// Remove the stream as the echo stream of any window
+	for (Windows::iterator i = g_vm->_windows->begin(); i != g_vm->_windows->end(); ++i) {
+		if ((*i)->_echoStream == stream)
+			(*i)->_echoStream = nullptr;
+	}
 }
 
 Stream *Streams::getFirst(uint32 *rock) {
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index e45bcc4..c43e2a0 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -28,7 +28,6 @@
 
 namespace Gargoyle {
 
-class GargoyleEngine;
 class Window;
 class Streams;
 
@@ -84,12 +83,22 @@ public:
 	/**
 	 * Write a character
 	 */
-	virtual void writeChar(unsigned char ch) = 0;
+	virtual void putChar(unsigned char ch) = 0;
 
 	/**
 	 * Write a unicode character
 	 */
-	virtual void writeCharUni(uint32 ch) = 0;
+	virtual void putCharUni(uint32 ch) = 0;
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) = 0;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putBufferUni(const uint32 *buf, size_t len) = 0;
 };
 typedef Stream *strid_t;
 
@@ -114,12 +123,22 @@ public:
 	/**
 	 * Write a character
 	 */
-	virtual void writeChar(unsigned char ch) override;
+	virtual void putChar(unsigned char ch) override;
 
 	/**
 	 * Write a unicode character
 	 */
-	virtual void writeCharUni(uint32 ch) override;
+	virtual void putCharUni(uint32 ch) override;
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putBufferUni(const uint32 *buf, size_t len) override;
 };
 
 /**
@@ -141,12 +160,54 @@ public:
 	/**
 	 * Write a character
 	 */
-	virtual void writeChar(unsigned char ch);
+	virtual void putChar(unsigned char ch) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putCharUni(uint32 ch) override;
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putBufferUni(const uint32 *buf, size_t len) override;
+};
+
+/**
+ * Implements a file stream
+ */
+class FileStream : public Stream {
+private:
+public:
+	/**
+	 * Constructor
+	 */
+	FileStream(Streams *streams, uint32 rock = 0, bool unicode = true);
+
+	/**
+	 * Write a character
+	 */
+	virtual void putChar(unsigned char ch) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putCharUni(uint32 ch) override;
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) override;
 
 	/**
 	 * Write a unicode character
 	 */
-	virtual void writeCharUni(uint32 ch);
+	virtual void putBufferUni(const uint32 *buf, size_t len) override;
 };
 
 /**
@@ -155,7 +216,6 @@ public:
 class Streams {
 	friend class Stream;
 private:
-	GargoyleEngine *_engine;
 	Stream *_streamList;
 	Stream *_currentStream;
 private:
@@ -172,7 +232,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Streams(GargoyleEngine *engine);
+	Streams();
 
 	/**
 	 * Destructor
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index e39e542..3212964 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -38,13 +38,46 @@ bool Windows::_overrideBgSet;
 int Windows::_overrideFgVal;
 int Windows::_overrideBgVal;
 
+/*--------------------------------------------------------------------------*/
+
+Windows::iterator &Windows::iterator::operator++() {
+	if (!_current)
+		return *this;
+
+	PairWindow *pairWin = dynamic_cast<PairWindow *>(_current);
+
+	if (pairWin) {
+		_current = !pairWin->_backward ? pairWin->_child1 : pairWin->_child2;
+	} else {
+		while (_current->_parent) {
+			pairWin = dynamic_cast<PairWindow *>(_current->_parent);
+
+			if (!pairWin->_backward) {
+				if (_current == pairWin->_child1) {
+					_current = pairWin->_child2;
+					return *this;
+				}
+			} else {
+				if (_current == pairWin->_child2) {
+					_current = pairWin->_child1;
+					return *this;
+				}
+			}
+
+			_current = pairWin;
+		}
+
+		_current = nullptr;
+	}
+
+	return *this;
+}
 
 /*--------------------------------------------------------------------------*/
 
-Windows::Windows(GargoyleEngine *engine, Graphics::Screen *screen) :
-		_engine(engine), _screen(screen), _forceRedraw(true), _moreFocus(false),
-		_windowList(nullptr), _rootWin(nullptr), _focusWin(nullptr), _mask(nullptr),
-		_claimSelect(0) {
+Windows::Windows(Graphics::Screen *screen) :
+		_screen(screen), _forceRedraw(true), _moreFocus(false), _windowList(nullptr),
+		_rootWin(nullptr), _focusWin(nullptr), _mask(nullptr), _claimSelect(0) {
 	_overrideReverse = false;
 	_overrideFgSet = false;
 	_overrideBgSet = false;
@@ -89,7 +122,7 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 			return nullptr;
 		}
 
-		oldparent = splitwin->parent;
+		oldparent = splitwin->_parent;
 		if (oldparent && oldparent->_type != wintype_Pair)
 		{
 			warning("window_open: parent window is not Pair");
@@ -112,9 +145,9 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 		pairwin->_child1 = splitwin;
 		pairwin->_child2 = newwin;
 
-		splitwin->parent = pairwin;
-		newwin->parent = pairwin;
-		pairwin->parent = oldparent;
+		splitwin->_parent = pairwin;
+		newwin->_parent = pairwin;
+		pairwin->_parent = oldparent;
 
 		if (oldparent) {
 			PairWindow *parentWin = dynamic_cast<PairWindow *>(oldparent);
@@ -155,20 +188,20 @@ Window *Windows::newWindow(glui32 type, glui32 rock) {
 		error("Unknown window type");
 	}
 
-	win->next = _windowList;
+	win->_next = _windowList;
 	_windowList = win;
-	if (win->next)
-		win->next->prev = win;
+	if (win->_next)
+		win->_next->_prev = win;
 
 	return win;
 }
 
 PairWindow *Windows::newPairWindow(glui32 method, Window *key, glui32 size) {
 	PairWindow *pwin = new PairWindow(this, method, key, size);
-	pwin->next = _windowList;
+	pwin->_next = _windowList;
 	_windowList = pwin;
-	if (pwin->next)
-		pwin->next->prev = pwin;
+	if (pwin->_next)
+		pwin->_next->_prev = pwin;
 
 	return pwin;
 }
@@ -223,7 +256,7 @@ void Windows::repaint(const Common::Rect &box) {
 /*--------------------------------------------------------------------------*/
 
 Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
-		_windows(windows), _rock(rock), _type(0), parent(nullptr), next(nullptr), prev(nullptr),
+		_windows(windows), _rock(rock), _type(0), _parent(nullptr), _next(nullptr), _prev(nullptr),
 		yadj(0), line_request(0), line_request_uni(0), char_request(0), char_request_uni(0),
 		mouse_request(0), hyper_request(0), more_request(0), scroll_request(0), image_loaded(0),
 		echo_line_input(true),  line_terminators(nullptr), termct(0), _echoStream(nullptr) {
@@ -239,10 +272,18 @@ Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
 	Common::fill(&fgcolor[0], &fgcolor[3], 3);
 	disprock.num = 0;
 
-	Streams &streams = *windows->_engine->_streams;
+	Streams &streams = *g_vm->_streams;
 	_stream = streams.addWindowStream(this);
 }
 
+void Window::cancelLineEvent(Event *ev) {
+	Event dummyEv;
+	if (!ev)
+		ev = &dummyEv;
+
+	g_vm->_events->clearEvent(ev);
+}
+
 /*--------------------------------------------------------------------------*/
 
 BlankWindow::BlankWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
@@ -253,14 +294,14 @@ BlankWindow::BlankWindow(Windows *windows, uint32 rock) : Window(windows, rock)
 
 TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
 	_type = wintype_TextGrid;
-	width = height = 0;
-	curx = cury = 0;
-	inbuf = nullptr;
-	inorgx = inorgy = 0;
-	inmax = 0;
-	incurs = inlen = 0;
-	inarrayrock.num = 0;
-	line_terminators = nullptr;
+	_width = _height = 0;
+	_curX = _curY = 0;
+	_inBuf = nullptr;
+	_inorgX = _inorgY = 0;
+	_inMax = 0;
+	_inCurs = _inLen = 0;
+	_inArrayRock.num = 0;
+	_lineTerminators = nullptr;
 
 	Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], styles);
 }
@@ -272,7 +313,7 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 	newwid = box.width() / g_conf->_cellW;
 	newhgt = box.height() / g_conf->_cellH;
 
-	if (newwid == width && newhgt == height)
+	if (newwid == _width && newhgt == _height)
 		return;
 
 	lines.resize(newhgt);
@@ -282,8 +323,8 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 	}
 
 	attr.clear();
-	width = newwid;
-	height = newhgt;
+	_width = newwid;
+	_height = newhgt;
 }
 
 void TextGridWindow::touch(int line) {
@@ -297,6 +338,21 @@ glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
 		size * g_conf->_cellH + g_conf->_tMarginY * 2;
 }
 
+void TextGridWindow::cancelLineEvent(Event *ev) {
+	Event dummyEv;
+
+	if (!ev)
+		ev = &dummyEv;
+
+	g_vm->_events->clearEvent(ev);
+
+	if (!line_request && !line_request_uni)
+		return;
+
+
+	// TODO : textgrid_cancel_line
+}
+
 /*--------------------------------------------------------------------------*/
 
 void TextGridWindow::TextGridRow::resize(size_t newSize) {
@@ -310,13 +366,13 @@ void TextGridWindow::TextGridRow::resize(size_t newSize) {
 /*--------------------------------------------------------------------------*/
 
 TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windows, rock),
-		historypos(0), historyfirst(0), historypresent(0), lastseen(0), scrollpos(0),
-		scrollmax(0), scrollback(SCROLLBACK), width(-1), height(-1), inbuf(nullptr),
-		line_terminators(nullptr), echo_line_input(true), ladjw(0), radjw(0), ladjn(0),
-		radjn(0), numchars(0), chars(nullptr), attrs(nullptr),
-		spaced(0), dashed(0), copybuf(0), copypos(0) {
+		_historyPos(0), _historyFirst(0), _historyPresent(0), _lastSeen(0), _scrollPos(0),
+		_scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1), _inBuf(nullptr),
+		_lineTerminators(nullptr), _echoLineInput(true), _ladjw(0), _radjw(0), _ladjn(0),
+		_radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr),
+		_spaced(0), _dashed(0), copybuf(0), copypos(0) {
 	_type = wintype_TextBuffer;
-	Common::fill(&history[0], &history[HISTORYLEN], nullptr);
+	Common::fill(&_history[0], &_history[HISTORYLEN], nullptr);
 
 	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], styles);
 }
@@ -334,31 +390,31 @@ void TextBufferWindow::rearrange(const Common::Rect &box) {
 	yadj = (box.height() - rnd);
 	bbox.top += (box.height() - rnd);
 
-	if (newwid != width) {
-		width = newwid;
+	if (newwid != _width) {
+		_width = newwid;
 		reflow();
 	}
 
-	if (newhgt != height) {
+	if (newhgt != _height) {
 		/* scroll up if we obscure new lines */
-		if (lastseen >= newhgt - 1)
-			scrollpos += (height - newhgt);
+		if (_lastSeen >= newhgt - 1)
+			_scrollPos += (_height - newhgt);
 
-		height = newhgt;
+		_height = newhgt;
 
 		/* keep window within 'valid' lines */
-		if (scrollpos > scrollmax - height + 1)
-			scrollpos = scrollmax - height + 1;
-		if (scrollpos < 0)
-			scrollpos = 0;
+		if (_scrollPos > _scrollMax - _height + 1)
+			_scrollPos = _scrollMax - _height + 1;
+		if (_scrollPos < 0)
+			_scrollPos = 0;
 		touchScroll();
 
 		/* allocate copy buffer */
 		if (copybuf)
 			delete[] copybuf;
-		copybuf = new glui32[height * TBLINELEN];
+		copybuf = new glui32[_height * TBLINELEN];
 
-		for (int i = 0; i < (height * TBLINELEN); i++)
+		for (int i = 0; i < (_height * TBLINELEN); i++)
 			copybuf[i] = 0;
 
 		copypos = 0;
@@ -371,10 +427,10 @@ void TextBufferWindow::reflow() {
 	int i, k, p, s;
 	int x;
 
-	if (height < 4 || width < 20)
+	if (_height < 4 || _width < 20)
 		return;
 
-	lines[0].len = numchars;
+	_lines[0].len = _numChars;
 
 	/* allocate temp buffers */
 	Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN];
@@ -401,38 +457,38 @@ void TextBufferWindow::reflow() {
 
 	x = 0;
 	p = 0;
-	s = scrollmax < SCROLLBACK ? scrollmax : SCROLLBACK - 1;
+	s = _scrollMax < SCROLLBACK ? _scrollMax : SCROLLBACK - 1;
 
 	for (k = s; k >= 0; k--) {
 		if (k == 0 && line_request)
-			inputbyte = p + infence;
+			inputbyte = p + _inFence;
 
-		if (lines[k].lpic) {
+		if (_lines[k].lpic) {
 			offsetbuf[x] = p;
 			alignbuf[x] = imagealign_MarginLeft;
-			pictbuf[x] = lines[k].lpic;
+			pictbuf[x] = _lines[k].lpic;
 
 			if (pictbuf[x]) pictbuf[x]->increment();
-			hyperbuf[x] = lines[k].lhyper;
+			hyperbuf[x] = _lines[k].lhyper;
 			x++;
 		}
 
-		if (lines[k].rpic) {
+		if (_lines[k].rpic) {
 			offsetbuf[x] = p;
 			alignbuf[x] = imagealign_MarginRight;
-			pictbuf[x] = lines[k].rpic;
+			pictbuf[x] = _lines[k].rpic;
 			if (pictbuf[x]) pictbuf[x]->increment();
-			hyperbuf[x] = lines[k].rhyper;
+			hyperbuf[x] = _lines[k].rhyper;
 			x++;
 		}
 
-		for (i = 0; i < lines[k].len; i++) {
-			attrbuf[p] = curattr = lines[k].attr[i];
-			charbuf[p] = lines[k].chars[i];
+		for (i = 0; i < _lines[k].len; i++) {
+			attrbuf[p] = curattr = _lines[k].attr[i];
+			charbuf[p] = _lines[k].chars[i];
 			p++;
 		}
 
-		if (lines[k].newline) {
+		if (_lines[k].newline) {
 			attrbuf[p] = curattr;
 			charbuf[p] = '\n';
 			p++;
@@ -462,13 +518,13 @@ void TextBufferWindow::reflow() {
 	}
 
 	/* terribly sorry about this... */
-	lastseen = 0;
-	scrollpos = 0;
+	_lastSeen = 0;
+	_scrollPos = 0;
 
 	if (inputbyte != -1) {
-		infence = numchars;
-		putTextUnit(charbuf + inputbyte, p - inputbyte, numchars, 0);
-		incurs = numchars;
+		_inFence = _numChars;
+		putTextUnit(charbuf + inputbyte, p - inputbyte, _numChars, 0);
+		_inCurs = _numChars;
 	}
 
 	// free temp buffers
@@ -488,8 +544,8 @@ void TextBufferWindow::touchScroll() {
 	_windows->clearSelection();
 	_windows->repaint(bbox);
 
-	for (int i = 0; i < scrollmax; i++)
-		lines[i].dirty = true;
+	for (int i = 0; i < _scrollMax; i++)
+		_lines[i].dirty = true;
 }
 
 void TextBufferWindow::clear() {
@@ -501,62 +557,62 @@ void TextBufferWindow::clear() {
 	attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
 	attr.reverse = false;
 
-	ladjw = radjw = 0;
-	ladjn = radjn = 0;
+	_ladjw = _radjw = 0;
+	_ladjn = _radjn = 0;
 
-	spaced = 0;
-	dashed = 0;
+	_spaced = 0;
+	_dashed = 0;
 
-	numchars = 0;
+	_numChars = 0;
 
-	for (i = 0; i < scrollback; i++) {
-		lines[i].len = 0;
+	for (i = 0; i < _scrollBack; i++) {
+		_lines[i].len = 0;
 
-		if (lines[i].lpic) lines[i].lpic->decrement();
-		lines[i].lpic = nullptr;
-		if (lines[i].rpic) lines[i].rpic->decrement();
-		lines[i].rpic = nullptr;
+		if (_lines[i].lpic) _lines[i].lpic->decrement();
+		_lines[i].lpic = nullptr;
+		if (_lines[i].rpic) _lines[i].rpic->decrement();
+		_lines[i].rpic = nullptr;
 
-		lines[i].lhyper = 0;
-		lines[i].rhyper = 0;
-		lines[i].lm = 0;
-		lines[i].rm = 0;
-		lines[i].newline = 0;
-		lines[i].dirty = true;
-		lines[i].repaint = false;
+		_lines[i].lhyper = 0;
+		_lines[i].rhyper = 0;
+		_lines[i].lm = 0;
+		_lines[i].rm = 0;
+		_lines[i].newline = 0;
+		_lines[i].dirty = true;
+		_lines[i].repaint = false;
 	}
 
-	lastseen = 0;
-	scrollpos = 0;
-	scrollmax = 0;
+	_lastSeen = 0;
+	_scrollPos = 0;
+	_scrollMax = 0;
 
-	for (i = 0; i < height; i++)
+	for (i = 0; i < _height; i++)
 		touch(i);
 }
 
 bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
 	if (align == imagealign_MarginRight)
 	{
-		if (lines[0].rpic || numchars)
+		if (_lines[0].rpic || _numChars)
 			return false;
 
-		radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
-		radjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
-		lines[0].rpic = pic;
-		lines[0].rm = radjw;
-		lines[0].rhyper = linkval;
+		_radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
+		_radjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
+		_lines[0].rpic = pic;
+		_lines[0].rm = _radjw;
+		_lines[0].rhyper = linkval;
 	} else {
-		if (align != imagealign_MarginLeft && numchars)
+		if (align != imagealign_MarginLeft && _numChars)
 			putCharUni('\n');
 
-		if (lines[0].lpic || numchars)
+		if (_lines[0].lpic || _numChars)
 			return false;
 
-		ladjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
-		ladjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
-		lines[0].lpic = pic;
-		lines[0].lm = ladjw;
-		lines[0].lhyper = linkval;
+		_ladjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
+		_ladjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
+		_lines[0].lpic = pic;
+		_lines[0].lm = _ladjw;
+		_lines[0].lhyper = linkval;
 
 		if (align != imagealign_MarginLeft)
 			flowBreak();
@@ -724,8 +780,8 @@ void TextBufferWindow::flowBreak() {
 }
 
 void TextBufferWindow::touch(int line) {
-	int y = bbox.top + g_conf->_tMarginY + (height - line - 1) * g_conf->_leading;
-	lines[line].dirty = 1;
+	int y = bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
+	_lines[line].dirty = 1;
 	_windows->clearSelection();
 	_windows->repaint(Common::Rect(bbox.left, y - 2, bbox.right, y + g_conf->_leading + 2));
 }
@@ -734,6 +790,21 @@ glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
 	return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
 }
 
+void TextBufferWindow::cancelLineEvent(Event *ev) {
+	Event dummyEv;
+
+	if (!ev)
+		ev = &dummyEv;
+
+	g_vm->_events->clearEvent(ev);
+
+	if (!line_request && !line_request_uni)
+		return;
+
+
+	// TODO : textbuffer_cancel_line
+}
+
 /*--------------------------------------------------------------------------*/
 
 TextBufferWindow::TextBufferRow::TextBufferRow() : len(0), newline(0), dirty(false), repaint(false),
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 6f7a016..f09ebdb 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -27,6 +27,7 @@
 #include "common/list.h"
 #include "common/rect.h"
 #include "graphics/screen.h"
+#include "gargoyle/events.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/fonts.h"
 #include "gargoyle/picture.h"
@@ -34,7 +35,6 @@
 
 namespace Gargoyle {
 
-class GargoyleEngine;
 class Window;
 class PairWindow;
 struct WindowMask;
@@ -47,9 +47,37 @@ struct WindowMask;
  * Main windows manager
  */
 class Windows {
-	friend class Window;
+public:
+	class iterator {
+	private:
+		Window *_current;
+	public:
+		/**
+		 * Constructor
+		 */
+		iterator(Window *start) : _current(start) {}
+
+		/**
+		 * Dereference
+		 */
+		Window *operator*() const { return _current; }
+
+		/**
+		 * Move to next
+		 */
+		iterator &operator++();
+
+		/**
+		 * Equality test
+		 */
+		bool operator==(const iterator &i) { return _current == i._current; }
+
+		/**
+		 * Inequality test
+		 */
+		bool operator!=(const iterator &i) { return _current != i._current; }
+	};
 private:
-	GargoyleEngine *_engine;
 	Graphics::Screen *_screen;
 	Window * _windowList;      ///< List of all windows
 	Window *_rootWin;          ///< The topmost window
@@ -83,7 +111,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Windows(GargoyleEngine *engine, Graphics::Screen *screen);
+	Windows(Graphics::Screen *screen);
 
 	/**
 	 * Open a new window
@@ -102,6 +130,16 @@ public:
 	 * Repaint an area of the windows
 	 */
 	void repaint(const Common::Rect &box);
+
+	/**
+	 * Get an iterator that will move over the tree
+	 */
+	iterator begin() { return iterator(_windowList); }
+
+	/**
+	 * Returns the end point of window iteration
+	 */
+	iterator end() { return iterator(nullptr); }
 };
 
 /**
@@ -159,12 +197,13 @@ public:
 	glui32 _rock;
 	glui32 _type;
 
-	Window *parent;            ///< pair window which contains this one
+	Window *_parent;       ///< pair window which contains this one
+	Window *_next, *_prev; ///< in the big linked list of windows
 	Common::Rect bbox;
 	int yadj;
 
-	Stream *_stream;      ///< the window stream.
-	Stream *_echoStream;  ///< the window's echo stream, if any.
+	Stream *_stream;       ///< the window stream.
+	Stream *_echoStream;   ///< the window's echo stream, if any.
 
 	int line_request;
 	int line_request_uni;
@@ -185,7 +224,6 @@ public:
 	byte fgcolor[3];
 
 	gidispatch_rock_t disprock;
-	Window *next, *prev;       ///< in the big linked list of windows
 public:
 	/**
 	 * Constructor
@@ -206,6 +244,31 @@ public:
 	 * Get window split size within parent pair window
 	 */
 	virtual glui32 getSplit(glui32 size, bool vertical) const { return 0; }
+
+	/**
+	 * Cancel a line event
+	 */
+	virtual void cancelLineEvent(Event *ev);
+
+	/**
+	 * Write a character
+	 */
+	virtual void putChar(unsigned char ch) { /* TODO */ }
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putCharUni(uint32 ch) { /* TODO */ }
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) { /* TODO */ }
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putBufferUni(const uint32 *buf, size_t len) { /* TODO */ }
 };
 typedef Window *winid_t;
 
@@ -249,19 +312,19 @@ private:
 	 */
 	void touch(int line);
 public:
-	int width, height;
+	int _width, _height;
 	TextGridRows lines;
 
-	int curx, cury;     ///< the window cursor position
+	int _curX, _curY;    ///< the window cursor position
 
-                        ///< for line input
-	void *inbuf;        ///< unsigned char* for latin1, glui32* for unicode
-	int inorgx, inorgy;
-	int inmax;
-	int incurs, inlen;
-	Attributes origattr;
-	gidispatch_rock_t inarrayrock;
-	glui32 *line_terminators;
+                         ///< for line input
+	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
+	int _inorgX, _inorgY;
+	int _inMax;
+	int _inCurs, _inLen;
+	Attributes _origAttr;
+	gidispatch_rock_t _inArrayRock;
+	glui32 *_lineTerminators;
 
 	WindowStyle styles[style_NUMSTYLES]; ///< style hints and settings
 public:
@@ -279,6 +342,11 @@ public:
 	 * Get window split size within parent pair window
 	 */
 	virtual glui32 getSplit(glui32 size, bool vertical) const override;
+
+	/**
+	 * Cancel a line event
+	 */
+	virtual void cancelLineEvent(Event *ev) override;
 };
 
 /**
@@ -321,43 +389,43 @@ private:
 	 */
 	void touch(int line);
 public:
-	int width, height;
-	int spaced;
-	int dashed;
+	int _width, _height;
+	int _spaced;
+	int _dashed;
 
-	TextBufferRows lines;
-	int scrollback;
+	TextBufferRows _lines;
+	int _scrollBack;
 
-	int numchars;       ///< number of chars in last line: lines[0]
-	glui32 *chars;      ///< alias to lines[0].chars
-	Attributes *attrs;  ///< alias to lines[0].attrs
+	int _numChars;        ///< number of chars in last line: lines[0]
+	glui32 *_chars;       ///< alias to lines[0].chars
+	Attributes *_attrs;  ///< alias to lines[0].attrs
 
-						///< adjust margins temporarily for images
-	int ladjw;
-	int ladjn;
-	int radjw;
-	int radjn;
+    ///< adjust margins temporarily for images
+	int _ladjw;
+	int _ladjn;
+	int _radjw;
+	int _radjn;
 
 	/* Command history. */
-	glui32 *history[HISTORYLEN];
-	int historypos;
-	int historyfirst, historypresent;
+	glui32 *_history[HISTORYLEN];
+	int _historyPos;
+	int _historyFirst, _historyPresent;
 
 	/* for paging */
-	int lastseen;
-	int scrollpos;
-	int scrollmax;
+	int _lastSeen;
+	int _scrollPos;
+	int _scrollMax;
 
 	/* for line input */
-	void *inbuf;        ///< unsigned char* for latin1, glui32* for unicode
-	int inmax;
-	long infence;
-	long incurs;
-	Attributes origattr;
-	gidispatch_rock_t inarrayrock;
+	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
+	int _inMax;
+	long _inFence;
+	long _inCurs;
+	Attributes _origAttr;
+	gidispatch_rock_t _inArrayRock;
 
-	glui32 echo_line_input;
-	glui32 *line_terminators;
+	glui32 _echoLineInput;
+	glui32 *_lineTerminators;
 
 	/* style hints and settings */
 	WindowStyle styles[style_NUMSTYLES];
@@ -382,6 +450,11 @@ public:
 	virtual glui32 getSplit(glui32 size, bool vertical) const override;
 
 	/**
+	 * Cancel a line event
+	 */
+	virtual void cancelLineEvent(Event *ev) override;
+
+	/**
 	 * Clear the window
 	 */
 	void clear();


Commit: ac9a122e3d140b2cb2e82c8d9dfeff2ee38fff36
    https://github.com/scummvm/scummvm/commit/ac9a122e3d140b2cb2e82c8d9dfeff2ee38fff36
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Renaming of window classes fields

Changed paths:
    engines/gargoyle/streams.cpp
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index b505405..7b6c1ab 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -70,7 +70,7 @@ void WindowStream::putChar(unsigned char ch) {
 		return;
 	++_writeCount;
 
-	if (_window->line_request || _window->line_request_uni) {
+	if (_window->_lineRequest || _window->_lineRequestUni) {
 		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
 			_window->cancelLineEvent(nullptr);
 			g_vm->_events->_forceClick = false;
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 3212964..bf671ab 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -257,20 +257,20 @@ void Windows::repaint(const Common::Rect &box) {
 
 Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
 		_windows(windows), _rock(rock), _type(0), _parent(nullptr), _next(nullptr), _prev(nullptr),
-		yadj(0), line_request(0), line_request_uni(0), char_request(0), char_request_uni(0),
-		mouse_request(0), hyper_request(0), more_request(0), scroll_request(0), image_loaded(0),
-		echo_line_input(true),  line_terminators(nullptr), termct(0), _echoStream(nullptr) {
-	attr.fgset = 0;
-	attr.bgset = 0;
-	attr.reverse = 0;
-	attr.style = 0;
-	attr.fgcolor = 0;
-	attr.bgcolor = 0;
-	attr.hyper = 0;
-
-	Common::fill(&bgcolor[0], &bgcolor[3], 3);
-	Common::fill(&fgcolor[0], &fgcolor[3], 3);
-	disprock.num = 0;
+		yadj(0), _lineRequest(0), _lineRequestUni(0), _charRequest(0), _charRequestUni(0),
+		_mouseRequest(0), _hyperRequest(0), _moreRequest(0), _scrollRequest(0), _imageLoaded(0),
+		_echoLineInput(true),  _lineTerminators(nullptr), _termCt(0), _echoStream(nullptr) {
+	_attr.fgset = 0;
+	_attr.bgset = 0;
+	_attr.reverse = 0;
+	_attr.style = 0;
+	_attr.fgcolor = 0;
+	_attr.bgcolor = 0;
+	_attr.hyper = 0;
+
+	Common::fill(&_bgColor[0], &_bgColor[3], 3);
+	Common::fill(&_fgColor[0], &_fgColor[3], 3);
+	_dispRock.num = 0;
 
 	Streams &streams = *g_vm->_streams;
 	_stream = streams.addWindowStream(this);
@@ -322,7 +322,7 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 		touch(y);
 	}
 
-	attr.clear();
+	_attr.clear();
 	_width = newwid;
 	_height = newhgt;
 }
@@ -346,7 +346,7 @@ void TextGridWindow::cancelLineEvent(Event *ev) {
 
 	g_vm->_events->clearEvent(ev);
 
-	if (!line_request && !line_request_uni)
+	if (!_lineRequest && !_lineRequestUni)
 		return;
 
 
@@ -370,7 +370,7 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windo
 		_scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1), _inBuf(nullptr),
 		_lineTerminators(nullptr), _echoLineInput(true), _ladjw(0), _radjw(0), _ladjn(0),
 		_radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr),
-		_spaced(0), _dashed(0), copybuf(0), copypos(0) {
+		_spaced(0), _dashed(0), _copyBuf(0), _copyPos(0) {
 	_type = wintype_TextBuffer;
 	Common::fill(&_history[0], &_history[HISTORYLEN], nullptr);
 
@@ -410,14 +410,14 @@ void TextBufferWindow::rearrange(const Common::Rect &box) {
 		touchScroll();
 
 		/* allocate copy buffer */
-		if (copybuf)
-			delete[] copybuf;
-		copybuf = new glui32[_height * TBLINELEN];
+		if (_copyBuf)
+			delete[] _copyBuf;
+		_copyBuf = new glui32[_height * TBLINELEN];
 
 		for (int i = 0; i < (_height * TBLINELEN); i++)
-			copybuf[i] = 0;
+			_copyBuf[i] = 0;
 
-		copypos = 0;
+		_copyPos = 0;
 	}
 }
 
@@ -452,7 +452,7 @@ void TextBufferWindow::reflow() {
 
 	/* copy text to temp buffers */
 
-	oldattr = attr;
+	oldattr = _attr;
 	curattr.clear();
 
 	x = 0;
@@ -460,7 +460,7 @@ void TextBufferWindow::reflow() {
 	s = _scrollMax < SCROLLBACK ? _scrollMax : SCROLLBACK - 1;
 
 	for (k = s; k >= 0; k--) {
-		if (k == 0 && line_request)
+		if (k == 0 && _lineRequest)
 			inputbyte = p + _inFence;
 
 		if (_lines[k].lpic) {
@@ -507,7 +507,7 @@ void TextBufferWindow::reflow() {
 	for (i = 0; i < p; i++) {
 		if (i == inputbyte)
 			break;
-		attr = attrbuf[i];
+		_attr = attrbuf[i];
 
 		if (offsetbuf[x] == i) {
 			putPicture(pictbuf[x], alignbuf[x], hyperbuf[x]);
@@ -535,7 +535,7 @@ void TextBufferWindow::reflow() {
 	delete[] hyperbuf;
 	delete[] offsetbuf;
 
-	attr = oldattr;
+	_attr = oldattr;
 
 	touchScroll();
 }
@@ -551,11 +551,11 @@ void TextBufferWindow::touchScroll() {
 void TextBufferWindow::clear() {
 	int i;
 
-	attr.fgset = Windows::_overrideFgSet;
-	attr.bgset = Windows::_overrideBgSet;
-	attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
-	attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
-	attr.reverse = false;
+	_attr.fgset = Windows::_overrideFgSet;
+	_attr.bgset = Windows::_overrideBgSet;
+	_attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
+	_attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
+	_attr.reverse = false;
 
 	_ladjw = _radjw = 0;
 	_ladjn = _radjn = 0;
@@ -798,7 +798,7 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 
 	g_vm->_events->clearEvent(ev);
 
-	if (!line_request && !line_request_uni)
+	if (!_lineRequest && !_lineRequestUni)
 		return;
 
 
@@ -822,9 +822,9 @@ void TextBufferWindow::TextBufferRow::resize(size_t newSize) {
 /*--------------------------------------------------------------------------*/
 
 GraphicsWindow::GraphicsWindow(Windows *windows, uint32 rock) : Window(windows, rock),
-		w(0), h(0), dirty(false), _surface(nullptr) {
+_w(0), _h(0), _dirty(false), _surface(nullptr) {
 	_type = wintype_Graphics;
-	Common::copy(&bgcolor[0], &bgcolor[3], bgnd);
+	Common::copy(&_bgColor[0], &_bgColor[3], _bgnd);
 }
 
 void GraphicsWindow::rearrange(const Common::Rect &box) {
@@ -837,21 +837,21 @@ void GraphicsWindow::rearrange(const Common::Rect &box) {
 
 	newwid = box.width();
 	newhgt = box.height();
-	oldw = w;
-	oldh = h;
+	oldw = _w;
+	oldh = _h;
 
 	if (newwid <= 0 || newhgt <= 0) {
-		w = 0;
-		h = 0;
+		_w = 0;
+		_h = 0;
 		delete _surface;
 		_surface = NULL;
 		return;
 	}
 
-	bothwid = w;
+	bothwid = _w;
 	if (newwid < bothwid)
 		bothwid = newwid;
-	bothhgt = h;
+	bothhgt = _h;
 	if (newhgt < bothhgt)
 		bothhgt = newhgt;
 
@@ -864,14 +864,14 @@ void GraphicsWindow::rearrange(const Common::Rect &box) {
 
 	delete _surface;
 	_surface = newSurface;
-	w = newwid;
-	h = newhgt;
+	_w = newwid;
+	_h = newhgt;
 
 	touch();
 }
 
 void GraphicsWindow::touch() {
-	dirty = true;
+	_dirty = true;
 	_windows->repaint(bbox);
 }
 
@@ -881,10 +881,10 @@ PairWindow::PairWindow(Windows *windows, glui32 method, Window *key, glui32 size
 		Window(windows, 0),
 		_dir(method & winmethod_DirMask),
 		_division(method & winmethod_DivisionMask),
-		_wborder((method & winmethod_BorderMask) == winmethod_Border),
+		_wBorder((method & winmethod_BorderMask) == winmethod_Border),
 		_vertical(_dir == winmethod_Left || _dir == winmethod_Right),
 		_backward(_dir == winmethod_Left || _dir == winmethod_Above),
-		_key(key), _size(size), _keydamage(0), _child1(nullptr), _child2(nullptr) {
+		_key(key), _size(size), _keyDamage(0), _child1(nullptr), _child2(nullptr) {
 	_type = wintype_Pair;
 }
 
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index f09ebdb..b84ce27 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -205,25 +205,25 @@ public:
 	Stream *_stream;       ///< the window stream.
 	Stream *_echoStream;   ///< the window's echo stream, if any.
 
-	int line_request;
-	int line_request_uni;
-	int char_request;
-	int char_request_uni;
-	int mouse_request;
-	int hyper_request;
-	int more_request;
-	int scroll_request;
-	int image_loaded;
-
-	glui32 echo_line_input;
-	glui32 *line_terminators;
-	glui32 termct;
-
-	Attributes attr;
-	byte bgcolor[3];
-	byte fgcolor[3];
-
-	gidispatch_rock_t disprock;
+	int _lineRequest;
+	int _lineRequestUni;
+	int _charRequest;
+	int _charRequestUni;
+	int _mouseRequest;
+	int _hyperRequest;
+	int _moreRequest;
+	int _scrollRequest;
+	int _imageLoaded;
+
+	glui32 _echoLineInput;
+	glui32 *_lineTerminators;
+	glui32 _termCt;
+
+	Attributes _attr;
+	byte _bgColor[3];
+	byte _fgColor[3];
+
+	gidispatch_rock_t _dispRock;
 public:
 	/**
 	 * Constructor
@@ -431,8 +431,8 @@ public:
 	WindowStyle styles[style_NUMSTYLES];
 
 	/* for copy selection */
-	glui32 *copybuf;
-	int copypos;
+	glui32 *_copyBuf;
+	int _copyPos;
 public:
 	/**
 	 * Constructor
@@ -467,9 +467,9 @@ class GraphicsWindow : public Window {
 private:
 	void touch();
 public:
-	unsigned char bgnd[3];
-	bool dirty;
-	int w, h;
+	unsigned char _bgnd[3];
+	bool _dirty;
+	int _w, _h;
 	Graphics::ManagedSurface *_surface;
 public:
 	/**
@@ -502,9 +502,9 @@ public:
 	bool _vertical, _backward; ///< flags
 	glui32 _division;          ///< winmethod_Fixed or winmethod_Proportional
 	Window *_key;              ///< NULL or a leaf-descendant (not a Pair)
-	int _keydamage;            ///< used as scratch space in window closing
+	int _keyDamage;            ///< used as scratch space in window closing
 	glui32 _size;              ///< size value
-	glui32 _wborder;           ///< winMethod_Border, NoBorder
+	glui32 _wBorder;           ///< winMethod_Border, NoBorder
 public:
 	/**
 	 * Constructor


Commit: 855fd39a9eb6676760d4e374bfc920000ff51f2e
    https://github.com/scummvm/scummvm/commit/855fd39a9eb6676760d4e374bfc920000ff51f2e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added WindowStream put methods

Changed paths:
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 2ca14bf..8620e88 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -40,7 +40,8 @@ GargoyleEngine *g_vm;
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _conf(nullptr),
-		_events(nullptr), _screen(nullptr), _windows(nullptr) {
+		_events(nullptr), _screen(nullptr), _windows(nullptr),
+		gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
 	g_vm = this;
 }
 
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 976221a..1d81cb9 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -30,6 +30,7 @@
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
 #include "graphics/screen.h"
+#include "gargoyle/glk_types.h"
 
 namespace Gargoyle {
 
@@ -95,6 +96,10 @@ public:
 	Events *_events;
 	Streams *_streams;
 	Windows *_windows;
+	void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock);
+	gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, const char *typecode);
+	void (*gli_unregister_arr)(void *array, glui32 len, const char *typecode, gidispatch_rock_t objrock);
+
 public:
 	GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
 	virtual ~GargoyleEngine();
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 7b6c1ab..c2a2ff8 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -89,22 +89,60 @@ void WindowStream::putCharUni(uint32 ch) {
 		return;
 	++_writeCount;
 
-	//TODO
+	if (_window->_lineRequest || _window->_lineRequestUni) {
+		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+			_window->cancelLineEvent(nullptr);
+			g_vm->_events->_forceClick = false;
+		} else {
+			warning("putCharUni: window has pending line request");
+		}
+	}
+
+	_window->putCharUni(ch);
+	if (_window->_echoStream)
+		_window->_echoStream->putCharUni(ch);
 }
 
-void WindowStream::putBuffer(const unsigned char *buf, size_t len) {
+void WindowStream::putBuffer(const char *buf, size_t len) {
 	if (!_writable)
 		return;
-	++_writeCount;
-	//TODO
+	_writeCount += len;
+
+	if (_window->_lineRequest || _window->_lineRequestUni) {
+		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+			_window->cancelLineEvent(nullptr);
+			g_vm->_events->_forceClick = false;
+		} else {
+			warning("putBuffer: window has pending line request");
+		}
+	}
+
+	for (size_t lx = 0; lx < len; lx++, buf++)
+		_window->putChar(*buf);
+	if (_window->_echoStream)
+		_window->_echoStream->putBuffer(buf, len);
 
 }
 
 void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
 	if (!_writable)
 		return;
-	++_writeCount;
-	//TODO
+	_writeCount += len;
+
+	if (_window->_lineRequest || _window->_lineRequestUni) {
+		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+			_window->cancelLineEvent(nullptr);
+			g_vm->_events->_forceClick = false;
+		}
+		else {
+			warning("putBuffer: window has pending line request");
+		}
+	}
+
+	for (size_t lx = 0; lx < len; lx++, buf++)
+		_window->putCharUni(*buf);
+	if (_window->_echoStream)
+		_window->_echoStream->putBufferUni(buf, len);
 }
 
 /*--------------------------------------------------------------------------*/
@@ -132,7 +170,7 @@ void MemoryStream::putCharUni(uint32 ch) {
 
 }
 
-void MemoryStream::putBuffer(const unsigned char *buf, size_t len) {
+void MemoryStream::putBuffer(const char *buf, size_t len) {
 	//TODO
 
 }
@@ -156,7 +194,7 @@ void FileStream::putCharUni(uint32 ch) {
 	//TODO
 }
 
-void FileStream::putBuffer(const unsigned char *buf, size_t len) {
+void FileStream::putBuffer(const char *buf, size_t len) {
 	//TODO
 }
 
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index c43e2a0..fd04a1e 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -93,12 +93,28 @@ public:
 	/**
 	 * Write a buffer
 	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) = 0;
+	virtual void putBuffer(const char *buf, size_t len) = 0;
 
 	/**
 	 * Write a unicode character
 	 */
 	virtual void putBufferUni(const uint32 *buf, size_t len) = 0;
+
+	/**
+	 * Send a line to the stream with a trailing newline
+	 */
+	void echoLine(char *buf, glui32 len) {
+		putBuffer(buf, len);
+		putChar('\n');
+	};
+
+	/**
+	 * Send a line to the stream with a trailing newline
+	 */
+	void echoLineUni(glui32 *buf, glui32 len) {
+		putBufferUni(buf, len);
+		putCharUni('\n');
+	}
 };
 typedef Stream *strid_t;
 
@@ -133,7 +149,7 @@ public:
 	/**
 	 * Write a buffer
 	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+	virtual void putBuffer(const char *buf, size_t len) override;
 
 	/**
 	 * Write a unicode character
@@ -170,7 +186,7 @@ public:
 	/**
 	 * Write a buffer
 	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+	virtual void putBuffer(const char *buf, size_t len) override;
 
 	/**
 	 * Write a unicode character
@@ -202,7 +218,7 @@ public:
 	/**
 	 * Write a buffer
 	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+	virtual void putBuffer(const char *buf, size_t len) override;
 
 	/**
 	 * Write a unicode character
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index bf671ab..6070128 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -297,7 +297,7 @@ TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows,
 	_width = _height = 0;
 	_curX = _curY = 0;
 	_inBuf = nullptr;
-	_inorgX = _inorgY = 0;
+	_inOrgX = _inOrgY = 0;
 	_inMax = 0;
 	_inCurs = _inLen = 0;
 	_inArrayRock.num = 0;
@@ -316,9 +316,9 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 	if (newwid == _width && newhgt == _height)
 		return;
 
-	lines.resize(newhgt);
+	_lines.resize(newhgt);
 	for (int y = 0; y < newhgt; ++y) {
-		lines[y].resize(newwid);
+		_lines[y].resize(newwid);
 		touch(y);
 	}
 
@@ -329,7 +329,7 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 
 void TextGridWindow::touch(int line) {
 	int y = bbox.top + line * g_conf->_leading;
-	lines[line].dirty = true;
+	_lines[line].dirty = true;
 	_windows->repaint(Common::Rect(bbox.left, y, bbox.right, y + g_conf->_leading));
 }
 
@@ -339,6 +339,12 @@ glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
 }
 
 void TextGridWindow::cancelLineEvent(Event *ev) {
+	int ix;
+	void *inbuf;
+	int inmax;
+	int unicode = _lineRequestUni;
+	gidispatch_rock_t inarrayrock;
+	TextGridRow *ln = &_lines[_inOrgY];
 	Event dummyEv;
 
 	if (!ev)
@@ -350,7 +356,51 @@ void TextGridWindow::cancelLineEvent(Event *ev) {
 		return;
 
 
-	// TODO : textgrid_cancel_line
+	inbuf = _inBuf;
+	inmax = _inMax;
+	inarrayrock = _inArrayRock;
+
+	if (!unicode) {
+		for (ix = 0; ix<_inLen; ix++)
+		{
+			glui32 ch = ln->chars[_inOrgX + ix];
+			if (ch > 0xff)
+				ch = '?';
+			((char *)inbuf)[ix] = (char)ch;
+		}
+		if (_echoStream)
+			_echoStream->echoLine((char *)_inBuf, _inLen);
+	} else {
+		for (ix = 0; ix<_inLen; ix++)
+			((glui32 *)inbuf)[ix] = ln->chars[_inOrgX + ix];
+		if (_echoStream)
+			_echoStream->echoLineUni((glui32 *)inbuf, _inLen);
+	}
+
+	_curY = _inOrgY + 1;
+	_curX = 0;
+	_attr = _origAttr;
+
+	ev->_type = evtype_LineInput;
+	ev->_window = this;
+	ev->_val1 = _inLen;
+	ev->_val2 = 0;
+
+	_lineRequest = false;
+	_lineRequestUni = false;
+
+	if (_lineTerminators) {
+		free(_lineTerminators);
+		_lineTerminators = nullptr;
+	}
+
+	_inBuf = nullptr;
+	_inMax = 0;
+	_inOrgX = 0;
+	_inOrgY = 0;
+
+	if (g_vm->gli_unregister_arr)
+		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
 }
 
 /*--------------------------------------------------------------------------*/
@@ -791,6 +841,12 @@ glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
 }
 
 void TextBufferWindow::cancelLineEvent(Event *ev) {
+	gidispatch_rock_t inarrayrock;
+	int ix;
+	int len;
+	void *inbuf;
+	int inmax;
+	int unicode = _lineRequestUni;
 	Event dummyEv;
 
 	if (!ev)
@@ -801,8 +857,61 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 	if (!_lineRequest && !_lineRequestUni)
 		return;
 
+	if (!_inBuf)
+		return;
+
+	inbuf = _inBuf;
+	inmax = _inMax;
+	inarrayrock = _inArrayRock;
+
+	len = _numChars - _inFence;
+	if (_echoStream)
+		_echoStream->echoLineUni(_chars + _inFence, len);
+
+	if (len > inmax)
+		len = inmax;
+
+	if (!unicode)
+	{
+		for (ix = 0; ix<len; ix++)
+		{
+			glui32 ch = _chars[_inFence + ix];
+			if (ch > 0xff)
+				ch = '?';
+			((char *)inbuf)[ix] = (char)ch;
+		}
+	}
+	else
+	{
+		for (ix = 0; ix<len; ix++)
+			((glui32 *)inbuf)[ix] = _chars[_inFence + ix];
+	}
+
+	_attr = _origAttr;
+
+	ev->_type = evtype_LineInput;
+	ev->_window = this;
+	ev->_val1 = len;
+	ev->_val2 = 0;
+
+	_lineRequest = false;
+	_lineRequestUni = false;
+	if (_lineTerminators) {
+		free(_lineTerminators);
+		_lineTerminators = nullptr;
+	}
+	_inBuf = nullptr;
+	_inMax = 0;
+
+	if (_echoLineInput) {
+		putCharUni('\n');
+	} else {
+		_numChars = _inFence;
+		touch(0);
+	}
 
-	// TODO : textbuffer_cancel_line
+	if (g_vm->gli_unregister_arr)
+		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
 }
 
 /*--------------------------------------------------------------------------*/
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index b84ce27..0d635fd 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -313,13 +313,13 @@ private:
 	void touch(int line);
 public:
 	int _width, _height;
-	TextGridRows lines;
+	TextGridRows _lines;
 
 	int _curX, _curY;    ///< the window cursor position
 
                          ///< for line input
 	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
-	int _inorgX, _inorgY;
+	int _inOrgX, _inOrgY;
 	int _inMax;
 	int _inCurs, _inLen;
 	Attributes _origAttr;


Commit: 5bfdffc9dc2b811dccd050c9d70200416465bb50
    https://github.com/scummvm/scummvm/commit/5bfdffc9dc2b811dccd050c9d70200416465bb50
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added remaining stream write methods

Changed paths:
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index c2a2ff8..7dec179 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -133,8 +133,7 @@ void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
 		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
 			_window->cancelLineEvent(nullptr);
 			g_vm->_events->_forceClick = false;
-		}
-		else {
+		} else {
 			warning("putBuffer: window has pending line request");
 		}
 	}
@@ -149,57 +148,265 @@ void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
 
 MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
 		Stream(streams, mode != filemode_Write, mode != filemode_Read, rock, unicode),
-		_buf(buf), _buflen(buflen), _bufptr(buf) {
-	assert(_buf && _buflen);
+		_buf(buf), _bufLen(buflen), _bufPtr(buf) {
+	assert(_buf && _bufLen);
 	assert(mode == filemode_Read || mode == filemode_Write || mode == filemode_ReadWrite);
 
 	if (unicode)
-		_bufend = (uint32 *)buf + buflen;
+		_bufEnd = (uint32 *)buf + buflen;
 	else
-		_bufend = (byte *)buf + buflen;
-	_bufeof = mode == filemode_Write ? _buf : _bufend;
+		_bufEnd = (byte *)buf + buflen;
+	_bufEof = mode == filemode_Write ? _buf : _bufEnd;
 }
 
 void MemoryStream::putChar(unsigned char ch) {
-	//TODO
+	if (!_writable)
+		return;
+	++_writeCount;
+
+	if (_bufPtr < _bufEnd) {
+		if (_unicode) {
+			*((glui32 *)_bufPtr) = ch;
+			_bufPtr = ((glui32 *)_bufPtr) + 1;
+		} else {
+			*((unsigned char *)_bufPtr) = ch;
+			_bufPtr = ((unsigned char *)_bufPtr) + 1;
+		}
 
+		if (_bufPtr > _bufEof)
+			_bufEof = _bufPtr;
+	}
 }
 
 void MemoryStream::putCharUni(uint32 ch) {
-	//TODO
+	if (!_writable)
+		return;
+	++_writeCount;
 
+	if (_bufPtr < _bufEnd) {
+		if (_unicode) {
+			*((glui32 *)_bufPtr) = ch;
+			_bufPtr = ((glui32 *)_bufPtr) + 1;
+		} else {
+			*((unsigned char *)_bufPtr) = (unsigned char)ch;
+			_bufPtr = ((unsigned char *)_bufPtr) + 1;
+		}
+		if (_bufPtr > _bufEof)
+			_bufEof = _bufPtr;
+	}
 }
 
 void MemoryStream::putBuffer(const char *buf, size_t len) {
-	//TODO
+	size_t lx;
+
+	if (!_writable)
+		return;
+	_writeCount += len;
 
+	if (_bufPtr >= _bufEnd) {
+		len = 0;
+	} else {
+		if (!_unicode) {
+			unsigned char *bp = (unsigned char *)_bufPtr;
+			if (bp + len > (unsigned char *)_bufEnd)
+			{
+				lx = (bp + len) - (unsigned char *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+			if (len) {
+				memmove(bp, buf, len);
+				bp += len;
+				if (bp >(unsigned char *)_bufEof)
+					_bufEof = bp;
+			}
+			_bufPtr = bp;
+		} else {
+			glui32 *bp = (glui32 *)_bufPtr;
+			if (bp + len > (glui32 *)_bufEnd) {
+				lx = (bp + len) - (glui32 *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+			if (len) {
+				glui32 i;
+				for (i = 0; i < len; i++)
+					bp[i] = buf[i];
+				bp += len;
+				if (bp >(glui32 *)_bufEof)
+					_bufEof = bp;
+			}
+			_bufPtr = bp;
+		}
+	}
 }
 
 void MemoryStream::putBufferUni(const uint32 *buf, size_t len) {
-	//TODO
+	size_t lx;
+
+	if (!_writable)
+		return;
+	_writeCount += len;
 
+	if (_bufPtr >= _bufEnd) {
+		len = 0;
+	} else {
+		if (!_unicode) {
+			unsigned char *bp = (unsigned char *)_bufPtr;
+			if (bp + len > (unsigned char *)_bufEnd) {
+				lx = (bp + len) - (unsigned char *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+			if (len) {
+				glui32 i;
+				for (i = 0; i < len; i++) {
+					glui32 ch = buf[i];
+					if (ch > 0xff)
+						ch = '?';
+					bp[i] = (unsigned char)ch;
+				}
+				bp += len;
+				if (bp > (unsigned char *)_bufEof)
+					_bufEof = bp;
+			}
+			_bufPtr = bp;
+		} else {
+			glui32 *bp = (glui32 *)_bufPtr;
+			if (bp + len > (glui32 *)_bufEnd) {
+				lx = (bp + len) - (glui32 *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+			if (len) {
+				memmove(bp, buf, len * 4);
+				bp += len;
+				if (bp >(glui32 *)_bufEof)
+					_bufEof = bp;
+			}
+			_bufPtr = bp;
+		}
+	}
 }
 
 /*--------------------------------------------------------------------------*/
 
 FileStream::FileStream(Streams *streams, uint32 rock, bool unicode) :
-	Stream(streams, true, false, rock, unicode) {
+	Stream(streams, true, false, rock, unicode), _lastOp(0), _textFile(false) {
+	// TODO: Set up files
+	_outFile = nullptr;
+	_inFile = nullptr;
+}
+
+void FileStream::ensureOp(FileMode mode) {
+	// No implementation
 }
 
 void FileStream::putChar(unsigned char ch) {
-	//TODO
+	if (!_writable)
+		return;
+	++_writeCount;
+
+	ensureOp(filemode_Write);
+	if (!_unicode) {
+		_outFile->writeByte(ch);
+	} else if (_textFile) {
+		putCharUtf8((glui32)ch);
+	} else {
+		_outFile->writeUint32BE(ch);
+	}
+
+	_outFile->flush();
 }
 
 void FileStream::putCharUni(uint32 ch) {
-	//TODO
+	if (!_writable)
+		return;
+	++_writeCount;
+
+	ensureOp(filemode_Write);
+	if (!_unicode) {
+		if (ch >= 0x100)
+			ch = '?';
+		_outFile->writeByte(ch);
+	} else if (_textFile) {
+		putCharUtf8(ch);
+	} else {
+		_outFile->writeUint32BE(ch);
+	}
+
+	_outFile->flush();
 }
 
 void FileStream::putBuffer(const char *buf, size_t len) {
-	//TODO
+	if (!_writable)
+		return;
+	_writeCount += len;
+
+	ensureOp(filemode_Write);
+	for (size_t lx = 0; lx < len; lx++) {
+		unsigned char ch = ((unsigned char *)buf)[lx];
+		if (!_unicode) {
+			_outFile->writeByte(ch);
+		} else if (_textFile) {
+			putCharUtf8((glui32)ch);
+		} else {
+			_outFile->writeUint32BE(ch);
+		}
+	}
+
+	_outFile->flush();
 }
 
 void FileStream::putBufferUni(const uint32 *buf, size_t len) {
-	//TODO
+	if (!_writable)
+		return;
+	_writeCount += len;
+
+
+	ensureOp(filemode_Write);
+	for (size_t lx = 0; lx<len; lx++) {
+		glui32 ch = buf[lx];
+		if (!_unicode) {
+			if (ch >= 0x100)
+				ch = '?';
+			_outFile->writeByte(ch);
+		} else if (_textFile) {
+			putCharUtf8(ch);
+		} else {
+			_outFile->writeUint32BE(ch);
+		}
+	}
+
+	_outFile->flush();
+}
+
+void FileStream::putCharUtf8(glui32 val) {
+	if (val < 0x80) {
+		_outFile->writeByte(val);
+	} else if (val < 0x800) {
+		_outFile->writeByte((0xC0 | ((val & 0x7C0) >> 6)));
+		_outFile->writeByte((0x80 | (val & 0x03F)));
+	} else if (val < 0x10000) {
+		_outFile->writeByte((0xE0 | ((val & 0xF000) >> 12)));
+		_outFile->writeByte((0x80 | ((val & 0x0FC0) >> 6)));
+		_outFile->writeByte((0x80 | (val & 0x003F)));
+	} else if (val < 0x200000) {
+		_outFile->writeByte((0xF0 | ((val & 0x1C0000) >> 18)));
+		_outFile->writeByte((0x80 | ((val & 0x03F000) >> 12)));
+		_outFile->writeByte((0x80 | ((val & 0x000FC0) >> 6)));
+		_outFile->writeByte((0x80 | (val & 0x00003F)));
+	} else {
+		_outFile->writeByte('?');
+	}
 }
 
 /*--------------------------------------------------------------------------*/
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index fd04a1e..82d6465 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -24,6 +24,7 @@
 #define GARGOYLE_STREAMS_H
 
 #include "common/scummsys.h"
+#include "common/savefile.h"
 #include "gargoyle/glk_types.h"
 
 namespace Gargoyle {
@@ -163,10 +164,10 @@ public:
 class MemoryStream : public Stream {
 private:
 	void *_buf;		///< unsigned char* for latin1, glui32* for unicode
-	void *_bufptr;
-	void *_bufend;
-	void *_bufeof;
-	size_t _buflen;	///< # of bytes for latin1, # of 4-byte words for unicode
+	void *_bufPtr;
+	void *_bufEnd;
+	void *_bufEof;
+	size_t _bufLen;	///< # of bytes for latin1, # of 4-byte words for unicode
 public:
 	/**
 	 * Constructor
@@ -199,6 +200,20 @@ public:
  */
 class FileStream : public Stream {
 private:
+	Common::OutSaveFile *_outFile;
+	Common::InSaveFile *_inFile;
+	uint32 _lastOp;					///< 0, filemode_Write, or filemode_Read
+	bool _textFile;
+private:
+	/**
+	 * Ensure the stream is ready for the given operation
+	 */
+	void ensureOp(FileMode mode);
+
+	/**
+	 * Put a UTF8 character
+	 */
+	void putCharUtf8(glui32 val);
 public:
 	/**
 	 * Constructor


Commit: 71389c2dd2b0af525c8f458cee723b61dcfeb614
    https://github.com/scummvm/scummvm/commit/71389c2dd2b0af525c8f458cee723b61dcfeb614
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added window class destructors

Changed paths:
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 6070128..ac16471 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -306,6 +306,16 @@ TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows,
 	Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], styles);
 }
 
+TextGridWindow::~TextGridWindow() {
+	if (_inBuf) {
+		if (g_vm->gli_unregister_arr)
+			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
+		_inBuf = nullptr;
+	}
+
+	delete[] _lineTerminators;
+}
+
 void TextGridWindow::rearrange(const Common::Rect &box) {
 	Window::rearrange(box);
 	int newwid, newhgt;
@@ -427,6 +437,24 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windo
 	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], styles);
 }
 
+TextBufferWindow::~TextBufferWindow() {
+	if (_inBuf) {
+		if (g_vm->gli_unregister_arr)
+			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
+		_inBuf = nullptr;
+	}
+
+	delete[] _copyBuf;
+	delete[] _lineTerminators;
+
+	for (int i = 0; i < _scrollBack; i++) {
+		if (_lines[i].lpic)
+			_lines[i].lpic->decrement();
+		if (_lines[i].rpic)
+			_lines[i].rpic->decrement();
+	}
+}
+
 void TextBufferWindow::rearrange(const Common::Rect &box) {
 	Window::rearrange(box);
 	int newwid, newhgt;
@@ -936,6 +964,10 @@ _w(0), _h(0), _dirty(false), _surface(nullptr) {
 	Common::copy(&_bgColor[0], &_bgColor[3], _bgnd);
 }
 
+GraphicsWindow::~GraphicsWindow() {
+	delete _surface;
+}
+
 void GraphicsWindow::rearrange(const Common::Rect &box) {
 	int newwid, newhgt;
 	int bothwid, bothhgt;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 0d635fd..24043c6 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -334,6 +334,11 @@ public:
 	TextGridWindow(Windows *windows, uint32 rock);
 
 	/**
+	 * Destructor
+	 */
+	virtual ~TextGridWindow();
+
+	/**
 	 * Rearranges the window
 	 */
 	virtual void rearrange(const Common::Rect &box) override;
@@ -440,6 +445,11 @@ public:
 	TextBufferWindow(Windows *windows, uint32 rock);
 
 	/**
+	 * Destructor
+	 */
+	virtual ~TextBufferWindow();
+
+	/**
 	 * Rearranges the window
 	 */
 	virtual void rearrange(const Common::Rect &box) override;
@@ -478,6 +488,11 @@ public:
 	GraphicsWindow(Windows *windows, uint32 rock);
 
 	/**
+	 * Destructor
+	 */
+	virtual ~GraphicsWindow();
+
+	/**
 	 * Rearranges the window
 	 */
 	virtual void rearrange(const Common::Rect &box) override;


Commit: 0d3ad2dc8ad49c684e8a74285ec832335d7675e7
    https://github.com/scummvm/scummvm/commit/0d3ad2dc8ad49c684e8a74285ec832335d7675e7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added TextGridWindow methods and support

Changed paths:
  A engines/gargoyle/draw.cpp
  A engines/gargoyle/draw.h
  A engines/gargoyle/window_mask.cpp
  A engines/gargoyle/window_mask.h
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/module.mk
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/draw.cpp b/engines/gargoyle/draw.cpp
new file mode 100644
index 0000000..86d5fb0
--- /dev/null
+++ b/engines/gargoyle/draw.cpp
@@ -0,0 +1,32 @@
+/* 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 "gargoyle/draw.h"
+
+namespace Gargoyle {
+
+int Draw::drawStringUni(int x, int y, int fidx, byte *rgb, glui32 *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/draw.h b/engines/gargoyle/draw.h
new file mode 100644
index 0000000..61dfe18
--- /dev/null
+++ b/engines/gargoyle/draw.h
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_DRAW_H
+#define GARGOYLE_DRAW_H
+
+#include "common/events.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+class Draw {
+protected:
+	int drawStringUni(int x, int y, int fidx, byte *rgb, glui32 *s, int n, int spw);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 1f7cc6a..3373628 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -32,4 +32,8 @@ void Events::clearEvent(Event *ev) {
 	// TODO
 }
 
+void Events::eventStore(EvType type, Window *win, uint32 val1, uint32 val2) {
+	// TODO
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 476be40..4ee9470 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -118,6 +118,8 @@ public:
 	void pollEvents();
 
 	void clearEvent(Event *ev);
+
+	void eventStore(EvType type, Window *win, uint32 val1 = 0, uint32 val2 = 0);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 8f84cbe..eaebb6f 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -165,6 +165,20 @@ enum StyleHint {
 	stylehint_just_RightFlush = 3,
 };
 
+/**
+ * These constants define the classes of opaque objects. It's a bit ugly to put
+ * them in this header file, since more classes may be added in the future.
+ * But if you find yourself stuck with an obsolete version of this file,
+ * adding new class definitions will be easy enough -- they will be numbered 
+ * sequentially, and the numeric constants can be found in the Glk specification.
+ */
+enum giDisp {
+	gidisp_Class_Window   = 0,
+	gidisp_Class_Stream   = 1,
+	gidisp_Class_Fileref  = 2,
+	gidisp_Class_Schannel = 3,
+};
+
 #ifdef GLK_MODULE_IMAGE
 
 enum ImageAlign {
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 925c66d..46f2d31 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/gargoyle
 MODULE_OBJS := \
 	conf.o \
 	detection.o \
+	draw.o \
 	events.o \
 	fonts.o \
 	gargoyle.o \
@@ -10,6 +11,7 @@ MODULE_OBJS := \
 	picture.o \
 	streams.o \
 	string.o \
+	window_mask.o \
 	windows.o \
 	scott/detection.o \
 	scott/scott.o
diff --git a/engines/gargoyle/window_mask.cpp b/engines/gargoyle/window_mask.cpp
new file mode 100644
index 0000000..f0f6aa3
--- /dev/null
+++ b/engines/gargoyle/window_mask.cpp
@@ -0,0 +1,336 @@
+/* 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 "gargoyle/window_mask.h"
+#include "gargoyle/conf.h"
+#include "gargoyle/gargoyle.h"
+#include "gargoyle/windows.h"
+
+namespace Gargoyle {
+
+int WindowMask::_lastX;
+int WindowMask::_lastY;
+
+WindowMask::WindowMask() : _hor(0), _ver(0), _links(nullptr) {
+	_lastX = _lastY = 0;
+}
+
+void WindowMask::resize(size_t x, size_t y) {
+	// Deallocate old storage
+	for (size_t i = 0; i < _hor; i++) {
+		if (_links[i])
+			delete _links[i];
+	}
+
+	delete _links;
+
+	_hor = x + 1;
+	_ver = y + 1;
+
+	// allocate new storage
+	_links = new glui32 *[_hor];
+	if (!_links) {
+		warning("resize_mask: out of memory");
+		_hor = _ver = 0;
+		return;
+	}
+
+	for (size_t i = 0; i < _hor; i++) {
+		_links[i] = new glui32[_ver];
+		if (!_links[i]) {
+			warning("resize_mask: could not allocate new memory");
+			return;
+		}
+	}
+
+	_select.left = 0;
+	_select.top = 0;
+	_select.right = 0;
+	_select.bottom = 0;
+}
+
+void WindowMask::putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1) {
+	uint i, k;
+	size_t tx0 = x0 < x1 ? x0 : x1;
+	size_t tx1 = x0 < x1 ? x1 : x0;
+	size_t ty0 = y0 < y1 ? y0 : y1;
+	size_t ty1 = y0 < y1 ? y1 : y0;
+
+	if (!_hor || !_ver) {
+		warning("putHyperlink: struct not initialized");
+		return;
+	}
+
+	if (tx0 >= _hor
+		|| tx1 >= _hor
+		|| ty0 >= _ver || ty1 >= _ver
+		|| !_links[tx0] || !_links[tx1]) {
+		warning("putHyperlink: invalid range given");
+		return;
+	}
+
+	for (i = tx0; i < tx1; i++) {
+		for (k = ty0; k < ty1; k++)
+			_links[i][k] = linkval;
+	}
+}
+
+glui32 WindowMask::getHyperlink(const Common::Point &pos) {
+	if (!_hor || !_ver) {
+		warning("getHyperlink: struct not initialized");
+		return 0;
+	}
+
+	if (pos.x >= (int16)_hor
+		|| pos.y >= (int16)_ver
+		|| !_links[pos.x]) {
+		warning("getHyperlink: invalid range given");
+		return 0;
+	}
+
+	return _links[pos.x][pos.y];
+}
+
+void WindowMask::startSelection(const Common::Point &pos) {
+	int tx, ty;
+
+	if (!_hor || !_ver) {
+		warning("startSelection: mask not initialized");
+		return;
+	}
+
+	tx = MIN(pos.x, (int16)_hor);
+	ty = MIN(pos.y, (int16)_ver);
+
+	_select.left = _lastX = tx;
+	_select.top = _lastY = ty;
+	_select.right = 0;
+	_select.bottom = 0;
+
+	g_vm->_windows->selectionChanged();
+}
+
+void WindowMask::moveSelection(const Common::Point &pos) {
+	int tx, ty;
+
+	if (ABS(pos.x - _lastX) < 5 && abs(pos.y - _lastY) < 5)
+		return;
+
+	if (!_hor || !_ver) {
+		warning("moveSelection: mask not initialized");
+		return;
+	}
+
+	tx = MIN(pos.x, (int16)_hor);
+	ty = MIN(pos.y, (int16)_ver);
+
+	_select.right = _lastX = tx;
+	_select.bottom = _lastY = ty;
+
+	g_vm->_windows->selectionChanged();
+}
+
+void WindowMask::clearSelection() {
+	if (_select.left || _select.right
+		|| _select.top || _select.bottom)
+		Windows::_forceRedraw = true;
+
+	_select.left = 0;
+	_select.top = 0;
+	_select.right = 0;
+	_select.bottom = 0;
+	g_vm->_windows->clearClaimSelect();
+}
+
+int WindowMask::checkSelection(uint x0, uint y0, uint x1, uint y1) {
+	uint cx0, cx1, cy0, cy1;
+
+	cx0 = _select.left < _select.right
+		? _select.left
+		: _select.right;
+
+	cx1 = _select.left < _select.right
+		? _select.right
+		: _select.left;
+
+	cy0 = _select.top < _select.bottom
+		? _select.top
+		: _select.bottom;
+
+	cy1 = _select.top < _select.bottom
+		? _select.bottom
+		: _select.top;
+
+	if (!cx0 || !cx1 || !cy0 || !cy1)
+		return false;
+
+	if (cx0 >= x0 && cx0 <= x1
+		&& cy0 >= y0 && cy0 <= y1)
+		return true;
+
+	if (cx0 >= x0 && cx0 <= x1
+		&& cy1 >= y0 && cy1 <= y1)
+		return true;
+
+	if (cx1 >= x0 && cx1 <= x1
+		&& cy0 >= y0 && cy0 <= y1)
+		return true;
+
+	if (cx1 >= x0 && cx1 <= x1
+		&& cy1 >= y0 && cy1 <= y1)
+		return true;
+
+	return false;
+}
+
+int WindowMask::getSelection(uint x0, uint y0, uint x1, uint y1, uint *rx0, uint *rx1) {
+	uint row, upper, lower, above, below;
+	int row_selected, found_left, found_right;
+	int from_right, from_below, is_above, is_below;
+	uint cx0, cx1, cy0, cy1;
+
+	row = (y0 + y1) / 2;
+	upper = row - (row - y0) / 2;
+	lower = row + (y1 - row) / 2;
+	above = upper - (g_conf->_leading) / 2;
+	below = lower + (g_conf->_leading) / 2;
+
+	cx0 = _select.left < _select.right
+		? _select.left
+		: _select.right;
+
+	cx1 = _select.left < _select.right
+		? _select.right
+		: _select.left;
+
+	cy0 = _select.top < _select.bottom
+		? _select.top
+		: _select.bottom;
+
+	cy1 = _select.top < _select.bottom
+		? _select.bottom
+		: _select.top;
+
+	row_selected = false;
+
+	if ((cy0 >= upper && cy0 <= lower)
+		|| (cy1 >= upper && cy1 <= lower))
+		row_selected = true;
+
+	if (row >= cy0 && row <= cy1)
+		row_selected = true;
+
+	if (!row_selected)
+		return false;
+
+	from_right = (_select.left != (int16)cx0);
+	from_below = (_select.top != (int16)cy0);
+	is_above = (above >= cy0 && above <= cy1);
+	is_below = (below >= cy0 && below <= cy1);
+
+	*rx0 = 0;
+	*rx1 = 0;
+
+	found_left = false;
+	found_right = false;
+
+	if (is_above && is_below) {
+		*rx0 = x0;
+		*rx1 = x1;
+		found_left = true;
+		found_right = true;
+	} else if (!is_above && is_below) {
+		if (from_below) {
+			if (from_right) {
+				*rx0 = cx0;
+				*rx1 = x1;
+				found_left = true;
+				found_right = true;
+			} else {
+				*rx0 = cx1;
+				*rx1 = x1;
+				found_left = true;
+				found_right = true;
+			}
+		} else {
+			if (from_right) {
+				*rx0 = cx1;
+				*rx1 = x1;
+				found_left = true;
+				found_right = true;
+			} else {
+				*rx1 = x1;
+				found_right = true;
+			}
+		}
+	} else if (is_above && !is_below) {
+		if (from_below) {
+			if (from_right) {
+				*rx0 = x0;
+				*rx1 = cx1;
+				found_left = true;
+				found_right = true;
+			} else {
+				*rx0 = x0;
+				*rx1 = cx0;
+				found_left = true;
+				found_right = true;
+			}
+		} else {
+			if (from_right) {
+				if (x0 > cx0)
+					return false;
+				*rx0 = x0;
+				*rx1 = cx0;
+				found_left = true;
+				found_right = true;
+			} else {
+				*rx0 = x0;
+				found_left = true;
+			}
+		}
+	}
+
+	if (found_left && found_right)
+		return true;
+
+	for (uint i = x0; i <= x1; i++) {
+		if (i >= cx0 && i <= cx1) {
+			if (!found_left) {
+				*rx0 = i;
+				found_left = true;
+				if (found_right)
+					return true;
+			} else {
+				if (!found_right)
+					*rx1 = i;
+			}
+		}
+	}
+
+	if (rx0 && !rx1)
+		*rx1 = x1;
+
+	return (rx0 && rx1);
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_mask.h b/engines/gargoyle/window_mask.h
new file mode 100644
index 0000000..db0d7e6
--- /dev/null
+++ b/engines/gargoyle/window_mask.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 GARGOYLE_WINDOW_MASK_H
+#define GARGOYLE_WINDOW_MASK_H
+
+#include "common/rect.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+class Window;
+
+struct WindowMask {
+	size_t _hor, _ver;
+	glui32 **_links;
+	Common::Rect _select;
+
+	static int _lastX, _lastY;
+
+	/**
+	 * Constructor
+	 */
+	WindowMask();
+
+	/**
+	 * Resize the links array
+	 */
+	void resize(size_t x, size_t y);
+
+	void putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1);
+
+	glui32 getHyperlink(const Common::Point &pos);
+
+	void startSelection(const Common::Point &pos);
+
+	void moveSelection(const Common::Point &pos);
+
+	void clearSelection();
+
+	int checkSelection(uint x0, uint y0, uint x1, uint y1);
+
+	int getSelection(uint x0, uint y0, uint x1, uint y1, uint *rx0, uint *rx1);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index ac16471..a625040 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -35,8 +35,15 @@ namespace Gargoyle {
 bool Windows::_overrideReverse;
 bool Windows::_overrideFgSet;
 bool Windows::_overrideBgSet;
+bool Windows::_forceRedraw;
 int Windows::_overrideFgVal;
 int Windows::_overrideBgVal;
+int Windows::_zcolor_fg;
+int Windows::_zcolor_bg;
+byte Windows::_zcolor_LightGrey[3];
+byte Windows::_zcolor_Foreground[3];
+byte Windows::_zcolor_Background[3];
+byte Windows::_zcolor_Bright[3];
 
 /*--------------------------------------------------------------------------*/
 
@@ -76,13 +83,25 @@ Windows::iterator &Windows::iterator::operator++() {
 /*--------------------------------------------------------------------------*/
 
 Windows::Windows(Graphics::Screen *screen) :
-		_screen(screen), _forceRedraw(true), _moreFocus(false), _windowList(nullptr),
+		_screen(screen), _moreFocus(false), _windowList(nullptr),
 		_rootWin(nullptr), _focusWin(nullptr), _mask(nullptr), _claimSelect(0) {
+	_mask = new WindowMask();
 	_overrideReverse = false;
 	_overrideFgSet = false;
 	_overrideBgSet = false;
+	_forceRedraw = true;
 	_overrideFgVal = 0;
 	_overrideBgVal = 0;
+	_zcolor_fg = _zcolor_bg = 0;
+
+	_zcolor_LightGrey[0] = _zcolor_LightGrey[1] = _zcolor_LightGrey[2] = 181;
+	_zcolor_Foreground[0] = _zcolor_Foreground[1] = _zcolor_Foreground[2] = 0;
+	_zcolor_Background[0] = _zcolor_Background[1] = _zcolor_Background[2] = 0;
+	_zcolor_Bright[0] = _zcolor_Bright[1] = _zcolor_Bright[2] = 0;
+}
+
+Windows::~Windows() {
+	delete _mask;
 }
 
 Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
@@ -235,31 +254,43 @@ void Windows::rearrange() {
 	}
 }
 
-void Windows::clearSelection() {
-	if (!_mask) {
-		warning("clear_selection: mask not initialized");
-		return;
-	}
+void Windows::selectionChanged() {
+	_claimSelect = false;
+	_forceRedraw = true;
+	redraw();
+}
 
-	if (_mask->select.left || _mask->select.right
-		|| _mask->select.top || _mask->select.bottom)
-		_forceRedraw = true;
+void Windows::clearSelection() {
+	_mask->clearSelection();
+}
 
-	_mask->select = Common::Rect();
-	_claimSelect = false;
+void Windows::redraw() {
+	// TODO: gli_windows_redraw
 }
 
 void Windows::repaint(const Common::Rect &box) {
 	// TODO
 }
 
+void Windows::drawRect(int x0, int y0, int w, int h, const byte *rgb) {
+	// TODO
+}
+
+byte *Windows::rgbShift(byte *rgb) {
+	_zcolor_Bright[0] = (rgb[0] + 0x30) < 0xff ? (rgb[0] + 0x30) : 0xff;
+	_zcolor_Bright[1] = (rgb[1] + 0x30) < 0xff ? (rgb[1] + 0x30) : 0xff;
+	_zcolor_Bright[2] = (rgb[2] + 0x30) < 0xff ? (rgb[2] + 0x30) : 0xff;
+
+	return _zcolor_Bright;
+}
+
 /*--------------------------------------------------------------------------*/
 
 Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
 		_windows(windows), _rock(rock), _type(0), _parent(nullptr), _next(nullptr), _prev(nullptr),
-		yadj(0), _lineRequest(0), _lineRequestUni(0), _charRequest(0), _charRequestUni(0),
+		_yAdj(0), _lineRequest(0), _lineRequestUni(0), _charRequest(0), _charRequestUni(0),
 		_mouseRequest(0), _hyperRequest(0), _moreRequest(0), _scrollRequest(0), _imageLoaded(0),
-		_echoLineInput(true),  _lineTerminators(nullptr), _termCt(0), _echoStream(nullptr) {
+		_echoLineInput(true), _lineTerminatorsBase(nullptr), _termCt(0), _echoStream(nullptr) {
 	_attr.fgset = 0;
 	_attr.bgset = 0;
 	_attr.reverse = 0;
@@ -276,6 +307,27 @@ Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
 	_stream = streams.addWindowStream(this);
 }
 
+Window::~Window() {
+	if (g_vm->gli_unregister_obj)
+		(*g_vm->gli_unregister_obj)(this, gidisp_Class_Window, _dispRock);
+
+	
+	_echoStream = nullptr;
+	delete _stream;
+
+	delete[] _lineTerminatorsBase;
+
+	Window *prev = _prev;
+	Window *next = _next;
+
+	if (prev)
+		prev->_next = next;
+	else
+		_windows->_windowList = next;
+	if (next)
+		next->_prev = prev;
+}
+
 void Window::cancelLineEvent(Event *ev) {
 	Event dummyEv;
 	if (!ev)
@@ -284,6 +336,31 @@ void Window::cancelLineEvent(Event *ev) {
 	g_vm->_events->clearEvent(ev);
 }
 
+void Window::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
+	warning("requestLineEvent: window does not support keyboard input");
+}
+
+void Window::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
+	warning("requestLineEventUni: window does not support keyboard input");
+}
+
+void Window::redraw() {
+	if (Windows::_forceRedraw) {
+		unsigned char *color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+		int y0 = _yAdj ? _bbox.top - _yAdj : _bbox.top;
+		_windows->drawRect(_bbox.left, y0, _bbox.width(), _bbox.bottom - y0, color);
+	}
+}
+
+bool Window::checkTerminator(glui32 ch) {
+	if (ch == keycode_Escape)
+		return true;
+	else if (ch >= keycode_Func12 && ch <= keycode_Func1)
+		return true;
+	else
+		return false;
+}
+
 /*--------------------------------------------------------------------------*/
 
 BlankWindow::BlankWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
@@ -338,9 +415,9 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 }
 
 void TextGridWindow::touch(int line) {
-	int y = bbox.top + line * g_conf->_leading;
+	int y = _bbox.top + line * g_conf->_leading;
 	_lines[line].dirty = true;
-	_windows->repaint(Common::Rect(bbox.left, y, bbox.right, y + g_conf->_leading));
+	_windows->repaint(Common::Rect(_bbox.left, y, _bbox.right, y + g_conf->_leading));
 }
 
 glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
@@ -348,6 +425,251 @@ glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
 		size * g_conf->_cellH + g_conf->_tMarginY * 2;
 }
 
+void TextGridWindow::putChar(unsigned char ch) {
+
+}
+
+void TextGridWindow::putCharUni(uint32 ch) {
+	TextGridRow *ln;
+
+	// Canonicalize the cursor position. That is, the cursor may have been
+	// left outside the window area; wrap it if necessary.
+	if (_curX < 0) {
+		_curX = 0;
+	} else if (_curX >= _width) {
+		_curX = 0;
+		_curY++;
+	}
+	if (_curY < 0)
+		_curY = 0;
+	else if (_curY >= _height)
+		return; /* outside the window */
+
+	if (ch == '\n') {
+		/* a newline just moves the cursor. */
+		_curY++;
+		_curX = 0;
+		return;
+	}
+
+	touch(_curY);
+
+	ln = &(_lines[_curY]);
+	ln->_chars[_curX] = ch;
+	ln->_attrs[_curX] = _attr;
+
+	_curX++;
+	// We can leave the cursor outside the window, since it will be
+	// canonicalized next time a character is printed.
+}
+
+bool TextGridWindow::unputCharUni(uint32 ch) {
+	TextGridRow *ln;
+	int oldx = _curX, oldy = _curY;
+
+	/* Move the cursor back. */
+	if (_curX >= _width)
+		_curX = _width - 1;
+	else
+		_curX--;
+
+	/* Canonicalize the cursor position. That is, the cursor may have been
+	left outside the window area; wrap it if necessary. */
+	if (_curX < 0) {
+		_curX = _width - 1;
+		_curY--;
+	}
+	if (_curY < 0)
+		_curY = 0;
+	else if (_curY >= _height)
+		return false; // outside the window
+
+	if (ch == '\n') {
+		// a newline just moves the cursor.
+		if (_curX == _width - 1)
+			return 1; // deleted a newline
+		_curX = oldx;
+		_curY = oldy;
+		return 0;    // it wasn't there */
+	}
+
+	ln = &(_lines[_curY]);
+	if (ln->_chars[_curX] == ch) {
+		ln->_chars[_curX] = ' ';
+		ln->_attrs[_curX].clear();
+		touch(_curY);
+		return true; // deleted the char
+	} else {
+		_curX = oldx;
+		_curY = oldy;
+		return false; // it wasn't there
+	}
+}
+
+void TextGridWindow::putBuffer(const unsigned char *buf, size_t len) {
+	// TODO
+}
+
+void TextGridWindow::putBufferUni(const uint32 *buf, size_t len) {
+	// TODO
+}
+
+void TextGridWindow::moveCursor(const Common::Point &pos) {
+	// If the values are negative, they're really huge positive numbers --
+	// remember that they were cast from glui32. So set them huge and
+	// let canonicalization take its course.
+	_curX = (pos.x < 0) ? 32767 : pos.x;
+	_curY = (pos.y < 0) ? 32767 : pos.y;
+}
+
+void TextGridWindow::clear() {
+	_attr.fgset = Windows::_overrideFgSet;
+	_attr.bgset = Windows::_overrideBgSet;
+	_attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
+	_attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
+	_attr.reverse = false;
+
+	for (int k = 0; k < _height; k++) {
+		TextGridRow &ln = _lines[k];
+		touch(k);
+		for (uint j = 0; j < ln._attrs.size(); ++j) {
+			ln._chars[j] = ' ';
+			ln._attrs[j].clear();
+		}
+	}
+
+	_curX = 0;
+	_curY = 0;
+}
+
+void TextGridWindow::click(const Common::Point &newPos) {
+	int x = newPos.x - _bbox.left;
+	int y = newPos.y - _bbox.top;
+
+	if (_lineRequest || _charRequest || _lineRequestUni || _charRequestUni
+			|| _moreRequest || _scrollRequest)
+		_windows->setFocus(this);
+
+	if (_mouseRequest) {
+		g_vm->_events->eventStore(evtype_MouseInput, this, x / g_conf->_cellW, y / g_conf->_leading);
+		_mouseRequest = false;
+		if (g_conf->_safeClicks)
+			g_vm->_events->_forceClick = true;
+	}
+
+	if (_hyperRequest) {
+		glui32 linkval = _windows->getHyperlink(newPos);
+		if (linkval)
+		{
+			g_vm->_events->eventStore(evtype_Hyperlink, this, linkval, 0);
+			_hyperRequest = false;
+			if (g_conf->_safeClicks)
+				g_vm->_events->_forceClick = true;
+		}
+	}
+}
+
+void TextGridWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
+	{
+		warning("request_line_event: window already has keyboard request");
+		return;
+	}
+
+	if ((int)maxlen > (_width - _curX))
+		maxlen = (_width - _curX);
+
+	_inBuf = buf;
+	_inMax = maxlen;
+	_inLen = 0;
+	_inCurs = 0;
+	_inOrgX = _curX;
+	_inOrgY = _curY;
+	_origAttr = _attr;
+	_attr.set(style_Input);
+
+	if (initlen > maxlen)
+		initlen = maxlen;
+
+	if (initlen) {
+		TextGridRow *ln = &_lines[_inOrgY];
+
+		for (glui32 ix = 0; ix < initlen; ix++) {
+			ln->_attrs[_inOrgX + ix].set(style_Input);
+			ln->_chars[_inOrgX + ix] = buf[ix];
+		}
+
+		_inCurs += initlen;
+		_inLen += initlen;
+		_curX = _inOrgX + _inCurs;
+		_curY = _inOrgY;
+
+		touch(_inOrgY);
+	}
+
+	if (_lineTerminatorsBase && _termCt) {
+		_lineTerminators = new glui32[_termCt + 1];
+
+		if (_lineTerminators) {
+			memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+			_lineTerminators[_termCt] = 0;
+		}
+	}
+
+	if (g_vm->gli_register_arr)
+		_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Cn");
+}
+
+void TextGridWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
+		warning("requestLineEventUni: window already has keyboard request");
+		return;
+	}
+
+	if ((int)maxlen > (_width - _curX))
+		maxlen = (_width - _curX);
+
+	_inBuf = buf;
+	_inMax = maxlen;
+	_inLen = 0;
+	_inCurs = 0;
+	_inOrgX = _curX;
+	_inOrgY = _curY;
+	_origAttr = _attr;
+	_attr.set(style_Input);
+
+	if (initlen > maxlen)
+		initlen = maxlen;
+
+	if (initlen) {
+		TextGridRow *ln = &(_lines[_inOrgY]);
+
+		for (glui32 ix = 0; ix<initlen; ix++) {
+			ln->_attrs[_inOrgX + ix].set(style_Input);
+			ln->_chars[_inOrgX + ix] = buf[ix];
+		}
+
+		_inCurs += initlen;
+		_inLen += initlen;
+		_curX = _inOrgX + _inCurs;
+		_curY = _inOrgY;
+
+		touch(_inOrgY);
+	}
+	
+	if (_lineTerminatorsBase && _termCt) {
+		_lineTerminators = new glui32[_termCt + 1];
+
+		if (_lineTerminators) {
+			memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+			_lineTerminators[_termCt] = 0;
+		}
+	}
+
+	if (g_vm->gli_register_arr)
+		_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Iu");
+}
+
 void TextGridWindow::cancelLineEvent(Event *ev) {
 	int ix;
 	void *inbuf;
@@ -373,7 +695,7 @@ void TextGridWindow::cancelLineEvent(Event *ev) {
 	if (!unicode) {
 		for (ix = 0; ix<_inLen; ix++)
 		{
-			glui32 ch = ln->chars[_inOrgX + ix];
+			glui32 ch = ln->_chars[_inOrgX + ix];
 			if (ch > 0xff)
 				ch = '?';
 			((char *)inbuf)[ix] = (char)ch;
@@ -382,7 +704,7 @@ void TextGridWindow::cancelLineEvent(Event *ev) {
 			_echoStream->echoLine((char *)_inBuf, _inLen);
 	} else {
 		for (ix = 0; ix<_inLen; ix++)
-			((glui32 *)inbuf)[ix] = ln->chars[_inOrgX + ix];
+			((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
 		if (_echoStream)
 			_echoStream->echoLineUni((glui32 *)inbuf, _inLen);
 	}
@@ -413,14 +735,274 @@ void TextGridWindow::cancelLineEvent(Event *ev) {
 		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
 }
 
+void TextGridWindow::acceptReadChar(glui32 arg) {
+	glui32 key;
+
+	switch (arg)
+	{
+	case keycode_Erase:
+		key = keycode_Delete;
+		break;
+	case keycode_MouseWheelUp:
+	case keycode_MouseWheelDown:
+		return;
+	default:
+		key = arg;
+	}
+
+	if (key > 0xff && key < (0xffffffff - keycode_MAXVAL + 1))
+	{
+		if (!(_charRequestUni) || key > 0x10ffff)
+			key = keycode_Unknown;
+	}
+
+	_charRequest = false;
+	_charRequestUni = false;
+	g_vm->_events->eventStore(evtype_CharInput, this, key, 0);
+}
+
+void TextGridWindow::acceptLine(glui32 keycode) {
+	int ix;
+	void *inbuf;
+	int inmax;
+	gidispatch_rock_t inarrayrock;
+	TextGridRow *ln = &(_lines[_inOrgY]);
+	int unicode = _lineRequestUni;
+
+	if (!_inBuf)
+		return;
+
+	inbuf = _inBuf;
+	inmax = _inMax;
+	inarrayrock = _inArrayRock;
+
+	if (!unicode) {
+		for (ix = 0; ix<_inLen; ix++)
+			((char *)inbuf)[ix] = (char)ln->_chars[_inOrgX + ix];
+		if (_echoStream)
+			_echoStream->echoLine((char *)inbuf, _inLen);
+	} else {
+		for (ix = 0; ix<_inLen; ix++)
+			((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
+		if (_echoStream)
+			_echoStream->echoLineUni((glui32 *)inbuf, _inLen);
+	}
+
+	_curY = _inOrgY + 1;
+	_curX = 0;
+	_attr = _origAttr;
+
+	if (_lineTerminators)
+	{
+		glui32 val2 = keycode;
+		if (val2 == keycode_Return)
+			val2 = 0;
+		g_vm->_events->eventStore(evtype_LineInput, this, _inLen, val2);
+		free(_lineTerminators);
+		_lineTerminators = NULL;
+	} else {
+		g_vm->_events->eventStore(evtype_LineInput, this, _inLen, 0);
+	}
+	_lineRequest = false;
+	_lineRequestUni = false;
+	_inBuf = NULL;
+	_inMax = 0;
+	_inOrgX = 0;
+	_inOrgY = 0;
+
+	if (g_vm->gli_unregister_arr)
+		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+void TextGridWindow::acceptReadLine(glui32 arg) {
+	int ix;
+	TextGridRow *ln = &(_lines[_inOrgY]);
+
+	if (!_inBuf)
+		return;
+
+	if (_lineTerminators && checkTerminator(arg)) {
+		glui32 *cx;
+		for (cx = _lineTerminators; *cx; cx++) {
+			if (*cx == arg) {
+				acceptLine(arg);
+				return;
+			}
+		}
+	}
+
+	switch (arg) {
+
+		/* Delete keys, during line input. */
+
+	case keycode_Delete:
+		if (_inLen <= 0)
+			return;
+		if (_inCurs <= 0)
+			return;
+		for (ix = _inCurs; ix<_inLen; ix++)
+			ln->_chars[_inOrgX + ix - 1] = ln->_chars[_inOrgX + ix];
+		ln->_chars[_inOrgX + _inLen - 1] = ' ';
+		_inCurs--;
+		_inLen--;
+		break;
+
+	case keycode_Erase:
+		if (_inLen <= 0)
+			return;
+		if (_inCurs >= _inLen)
+			return;
+		for (ix = _inCurs; ix<_inLen - 1; ix++)
+			ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix + 1];
+		ln->_chars[_inOrgX + _inLen - 1] = ' ';
+		_inLen--;
+		break;
+
+	case keycode_Escape:
+		if (_inLen <= 0)
+			return;
+		for (ix = 0; ix<_inLen; ix++)
+			ln->_chars[_inOrgX + ix] = ' ';
+		_inLen = 0;
+		_inCurs = 0;
+		break;
+
+		/* Cursor movement keys, during line input. */
+
+	case keycode_Left:
+		if (_inCurs <= 0)
+			return;
+		_inCurs--;
+		break;
+
+	case keycode_Right:
+		if (_inCurs >= _inLen)
+			return;
+		_inCurs++;
+		break;
+
+	case keycode_Home:
+		if (_inCurs <= 0)
+			return;
+		_inCurs = 0;
+		break;
+
+	case keycode_End:
+		if (_inCurs >= _inLen)
+			return;
+		_inCurs = _inLen;
+		break;
+
+	case keycode_Return:
+		acceptLine(arg);
+		break;
+
+	default:
+		if (_inLen >= _inMax)
+			return;
+
+		if (arg < 32 || arg > 0xff)
+			return;
+
+		if (g_conf->_caps && (arg > 0x60 && arg < 0x7b))
+			arg -= 0x20;
+
+		for (ix = _inLen; ix>_inCurs; ix--)
+			ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix - 1];
+		ln->_attrs[_inOrgX + _inLen].set(style_Input);
+		ln->_chars[_inOrgX + _inCurs] = arg;
+
+		_inCurs++;
+		_inLen++;
+	}
+
+	_curX = _inOrgX + _inCurs;
+	_curY = _inOrgY;
+
+	touch(_inOrgY);
+}
+
+void TextGridWindow::redraw() {
+	TextGridRow *ln;
+	int x0, y0;
+	int x, y, w;
+	int i, a, b, k, o;
+	glui32 link;
+	int font;
+	byte *fgcolor, *bgcolor;
+
+	x0 = _bbox.left;
+	y0 = _bbox.top;
+
+	for (i = 0; i < _height; i++) {
+		ln = &_lines[i];
+		if (ln->dirty || Windows::_forceRedraw) {
+			ln->dirty = 0;
+
+			x = x0;
+			y = y0 + i * g_conf->_leading;
+
+			/* clear any stored hyperlink coordinates */
+			_windows->setHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
+
+			a = 0;
+			for (b = 0; b < _width; b++) {
+				if (ln->_attrs[a] == ln->_attrs[b]) {
+					link = ln->_attrs[a].hyper;
+					font = ln->_attrs[a].attrFont(styles);
+					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
+					bgcolor = ln->_attrs[a].attrBg(styles);
+					w = (b - a) * g_conf->_cellW;
+					_windows->drawRect(x, y, w, g_conf->_leading, bgcolor);
+					o = x;
+
+					for (k = a; k < b; k++) {
+						drawStringUni(o * GLI_SUBPIX,
+							y + g_conf->_baseLine, font, fgcolor,
+							&ln->_chars[k], 1, -1);
+						o += g_conf->_cellW;
+					}
+					if (link) {
+						_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
+							g_conf->_linkStyle, g_conf->_linkColor);
+						_windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading);
+					}
+					x += w;
+					a = b;
+				}
+			}
+			link = ln->_attrs[a].hyper;
+			font = ln->_attrs[a].attrFont(styles);
+			fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
+			bgcolor = ln->_attrs[a].attrBg(styles);
+			w = (b - a) * g_conf->_cellW;
+			w += _bbox.right - (x + w);
+			_windows->drawRect(x, y, w, g_conf->_leading, bgcolor);
+
+			o = x;
+			for (k = a; k < b; k++) {
+				drawStringUni(o * GLI_SUBPIX,
+					y + g_conf->_baseLine, font, fgcolor,
+					&ln->_chars[k], 1, -1);
+				o += g_conf->_cellW;
+			}
+			if (link) {
+				_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
+					g_conf->_linkStyle, g_conf->_linkColor);
+				_windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading);
+			}
+		}
+	}
+}
+
 /*--------------------------------------------------------------------------*/
 
 void TextGridWindow::TextGridRow::resize(size_t newSize) {
-	chars.clear();
-	attr.clear();
-	chars.resize(newSize);
-	attr.resize(newSize);
-	Common::fill(&chars[0], &chars[0] + newSize, ' ');
+	_chars.clear();
+	_attrs.clear();
+	_chars.resize(newSize);
+	_attrs.resize(newSize);
+	Common::fill(&_chars[0], &_chars[0] + newSize, ' ');
 }
 
 /*--------------------------------------------------------------------------*/
@@ -465,8 +1047,8 @@ void TextBufferWindow::rearrange(const Common::Rect &box) {
 
 	/* align text with bottom */
 	rnd = newhgt * g_conf->_cellH + g_conf->_tMarginY * 2;
-	yadj = (box.height() - rnd);
-	bbox.top += (box.height() - rnd);
+	_yAdj = (box.height() - rnd);
+	_bbox.top += (box.height() - rnd);
 
 	if (newwid != _width) {
 		_width = newwid;
@@ -601,7 +1183,7 @@ void TextBufferWindow::reflow() {
 
 	if (inputbyte != -1) {
 		_inFence = _numChars;
-		putTextUnit(charbuf + inputbyte, p - inputbyte, _numChars, 0);
+		putTextUni(charbuf + inputbyte, p - inputbyte, _numChars, 0);
 		_inCurs = _numChars;
 	}
 
@@ -620,54 +1202,12 @@ void TextBufferWindow::reflow() {
 
 void TextBufferWindow::touchScroll() {
 	_windows->clearSelection();
-	_windows->repaint(bbox);
+	_windows->repaint(_bbox);
 
 	for (int i = 0; i < _scrollMax; i++)
 		_lines[i].dirty = true;
 }
 
-void TextBufferWindow::clear() {
-	int i;
-
-	_attr.fgset = Windows::_overrideFgSet;
-	_attr.bgset = Windows::_overrideBgSet;
-	_attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
-	_attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
-	_attr.reverse = false;
-
-	_ladjw = _radjw = 0;
-	_ladjn = _radjn = 0;
-
-	_spaced = 0;
-	_dashed = 0;
-
-	_numChars = 0;
-
-	for (i = 0; i < _scrollBack; i++) {
-		_lines[i].len = 0;
-
-		if (_lines[i].lpic) _lines[i].lpic->decrement();
-		_lines[i].lpic = nullptr;
-		if (_lines[i].rpic) _lines[i].rpic->decrement();
-		_lines[i].rpic = nullptr;
-
-		_lines[i].lhyper = 0;
-		_lines[i].rhyper = 0;
-		_lines[i].lm = 0;
-		_lines[i].rm = 0;
-		_lines[i].newline = 0;
-		_lines[i].dirty = true;
-		_lines[i].repaint = false;
-	}
-
-	_lastSeen = 0;
-	_scrollPos = 0;
-	_scrollMax = 0;
-
-	for (i = 0; i < _height; i++)
-		touch(i);
-}
-
 bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
 	if (align == imagealign_MarginRight)
 	{
@@ -696,7 +1236,29 @@ bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
 			flowBreak();
 	}
 
-	return true ;
+	return true;
+}
+
+void TextBufferWindow::flowBreak() {
+	// TODO
+}
+
+void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldlen) {
+	// TODO
+}
+
+void TextBufferWindow::touch(int line) {
+	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
+	_lines[line].dirty = 1;
+	_windows->clearSelection();
+	_windows->repaint(Common::Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
+}
+
+glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
+	return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
+}
+
+void TextBufferWindow::putChar(unsigned char ch) {
 }
 
 void TextBufferWindow::putCharUni(glui32 ch) {
@@ -712,10 +1274,10 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 
 	gli_tts_speak(&ch, 1);
 
-	pw = (bbox.right - bbox.left - g_conf->_tMarginX * 2 - gli_scroll_width) * GLI_SUBPIX;
+	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - gli_scroll_width) * GLI_SUBPIX;
 	pw = pw - 2 * SLOP - radjw - ladjw;
 
-	color = gli_override_bg_set ? gli_window_color : bgcolor;
+	color = Windows::_overrideBgSet ? gli_window_color : bgcolor;
 
 	// oops ... overflow
 	if (numchars + 1 >= TBLINELEN)
@@ -730,7 +1292,7 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 		// fails for 'tis a wonderful day in the '80s
 		if (gli_conf_quotes > 1 && ch == '\'')
 		{
-			if (numchars == 0 || leftquote(chars[numchars - 1]))
+			if (numchars == 0 || leftquote(_chars[numchars - 1]))
 				ch = UNI_LSQUO;
 		}
 
@@ -742,7 +1304,7 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 
 		if (ch == '"')
 		{
-			if (numchars == 0 || leftquote(chars[numchars - 1]))
+			if (numchars == 0 || leftquote(_chars[numchars - 1]))
 				ch = UNI_LDQUO;
 			else
 				ch = UNI_RDQUO;
@@ -810,23 +1372,23 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 		}
 	}
 
-	chars[numchars] = ch;
+	_chars[numchars] = ch;
 	attrs[numchars] = attr;
 	numchars++;
 
 	// kill spaces at the end for line width calculation
 	linelen = numchars;
-	while (linelen > 1 && chars[linelen - 1] == ' '
+	while (linelen > 1 && _chars[linelen - 1] == ' '
 		&& styles[attrs[linelen - 1].style].bg == color
 		&& !styles[attrs[linelen - 1].style].reverse)
 		linelen--;
 
-	if (calcwidth(dwin, chars, attrs, 0, linelen, -1) >= pw)
+	if (calcwidth(dwin, _chars, attrs, 0, linelen, -1) >= pw)
 	{
 		bpoint = numchars;
 
 		for (i = numchars - 1; i > 0; i--)
-			if (chars[i] == ' ')
+			if (_chars[i] == ' ')
 			{
 				bpoint = i + 1; // skip space
 				break;
@@ -834,13 +1396,13 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 
 		saved = numchars - bpoint;
 
-		memcpy(bchars, chars + bpoint, saved * 4);
+		memcpy(bchars, _chars + bpoint, saved * 4);
 		memcpy(battrs, attrs + bpoint, saved * sizeof(attr_t));
 		numchars = bpoint;
 
 		scrolloneline(dwin, 0);
 
-		memcpy(chars, bchars, saved * 4);
+		memcpy(_chars, bchars, saved * 4);
 		memcpy(attrs, battrs, saved * sizeof(attr_t));
 		numchars = saved;
 	}
@@ -849,23 +1411,83 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 	*/
 }
 
-void TextBufferWindow::putTextUnit(const glui32 *buf, int len, int pos, int oldlen) {
+bool TextBufferWindow::unputCharUni(uint32 ch) {
 	// TODO
+	return false;
 }
 
-void TextBufferWindow::flowBreak() {
+void TextBufferWindow::putBuffer(const unsigned char *buf, size_t len) {
 	// TODO
 }
 
-void TextBufferWindow::touch(int line) {
-	int y = bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
-	_lines[line].dirty = 1;
-	_windows->clearSelection();
-	_windows->repaint(Common::Rect(bbox.left, y - 2, bbox.right, y + g_conf->_leading + 2));
+void TextBufferWindow::putBufferUni(const uint32 *buf, size_t len) {
+	// TODO
 }
 
-glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
-	return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
+void TextBufferWindow::moveCursor(const Common::Point &newPos) {
+	// TODO
+}
+
+void TextBufferWindow::clear() {
+	int i;
+
+	_attr.fgset = Windows::_overrideFgSet;
+	_attr.bgset = Windows::_overrideBgSet;
+	_attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
+	_attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
+	_attr.reverse = false;
+
+	_ladjw = _radjw = 0;
+	_ladjn = _radjn = 0;
+
+	_spaced = 0;
+	_dashed = 0;
+
+	_numChars = 0;
+
+	for (i = 0; i < _scrollBack; i++) {
+		_lines[i].len = 0;
+
+		if (_lines[i].lpic) _lines[i].lpic->decrement();
+		_lines[i].lpic = nullptr;
+		if (_lines[i].rpic) _lines[i].rpic->decrement();
+		_lines[i].rpic = nullptr;
+
+		_lines[i].lhyper = 0;
+		_lines[i].rhyper = 0;
+		_lines[i].lm = 0;
+		_lines[i].rm = 0;
+		_lines[i].newline = 0;
+		_lines[i].dirty = true;
+		_lines[i].repaint = false;
+	}
+
+	_lastSeen = 0;
+	_scrollPos = 0;
+	_scrollMax = 0;
+
+	for (i = 0; i < _height; i++)
+		touch(i);
+}
+
+void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
+	{
+		warning("request_line_event: window already has keyboard request");
+		return;
+	}
+
+	// TODO
+}
+
+void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
+	{
+		warning("request_line_event_uni: window already has keyboard request");
+		return;
+	}
+
+	// TODO
 }
 
 void TextBufferWindow::cancelLineEvent(Event *ev) {
@@ -899,18 +1521,15 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 	if (len > inmax)
 		len = inmax;
 
-	if (!unicode)
-	{
-		for (ix = 0; ix<len; ix++)
-		{
+	if (!unicode) {
+		for (ix = 0; ix<len; ix++) {
 			glui32 ch = _chars[_inFence + ix];
 			if (ch > 0xff)
 				ch = '?';
 			((char *)inbuf)[ix] = (char)ch;
 		}
 	}
-	else
-	{
+	else {
 		for (ix = 0; ix<len; ix++)
 			((glui32 *)inbuf)[ix] = _chars[_inFence + ix];
 	}
@@ -933,7 +1552,8 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 
 	if (_echoLineInput) {
 		putCharUni('\n');
-	} else {
+	}
+	else {
 		_numChars = _inFence;
 		touch(0);
 	}
@@ -942,6 +1562,10 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
 }
 
+void TextBufferWindow::redraw() {
+	// TODO
+}
+
 /*--------------------------------------------------------------------------*/
 
 TextBufferWindow::TextBufferRow::TextBufferRow() : len(0), newline(0), dirty(false), repaint(false),
@@ -974,7 +1598,7 @@ void GraphicsWindow::rearrange(const Common::Rect &box) {
 	int oldw, oldh;
 	Graphics::ManagedSurface *newSurface;
 
-	bbox = box;
+	_bbox = box;
 
 	newwid = box.width();
 	newhgt = box.height();
@@ -1013,7 +1637,11 @@ void GraphicsWindow::rearrange(const Common::Rect &box) {
 
 void GraphicsWindow::touch() {
 	_dirty = true;
-	_windows->repaint(bbox);
+	_windows->repaint(_bbox);
+}
+
+void GraphicsWindow::redraw() {
+	// TODO
 }
 
 /*--------------------------------------------------------------------------*/
@@ -1034,14 +1662,14 @@ void PairWindow::rearrange(const Common::Rect &box) {
 	int min, diff, split, splitwid, max;
 	Window *ch1, *ch2;
 
-	bbox = box;
+	_bbox = box;
 
 	if (_vertical) {
-		min = bbox.left;
-		max = bbox.right;
+		min = _bbox.left;
+		max = _bbox.right;
 	} else {
-		min = bbox.top;
-		max = bbox.bottom;
+		min = _bbox.top;
+		max = _bbox.bottom;
 	}
 	diff = max - min;
 
@@ -1080,23 +1708,23 @@ void PairWindow::rearrange(const Common::Rect &box) {
 	}
 
 	if (_vertical) {
-		box1.left = bbox.left;
+		box1.left = _bbox.left;
 		box1.right = split;
 		box2.left = split + splitwid;
-		box2.right = bbox.right;
-		box1.top = bbox.top;
-		box1.bottom = bbox.bottom;
-		box2.top = bbox.top;
-		box2.bottom = bbox.bottom;
+		box2.right = _bbox.right;
+		box1.top = _bbox.top;
+		box1.bottom = _bbox.bottom;
+		box2.top = _bbox.top;
+		box2.bottom = _bbox.bottom;
 	} else {
-		box1.top = bbox.top;
+		box1.top = _bbox.top;
 		box1.bottom = split;
 		box2.top = split + splitwid;
-		box2.bottom = bbox.bottom;
-		box1.left = bbox.left;
-		box1.right = bbox.right;
-		box2.left = bbox.left;
-		box2.right = bbox.right;
+		box2.bottom = _bbox.bottom;
+		box1.left = _bbox.left;
+		box1.right = _bbox.right;
+		box2.left = _bbox.left;
+		box2.right = _bbox.right;
 	}
 
 	if (!_backward) {
@@ -1111,6 +1739,10 @@ void PairWindow::rearrange(const Common::Rect &box) {
 	ch2->rearrange(box2);
 }
 
+void PairWindow::redraw() {
+	// TODO
+}
+
 /*--------------------------------------------------------------------------*/
 
 void Attributes::clear() {
@@ -1123,4 +1755,88 @@ void Attributes::clear() {
 	style = 0;
 }
 
+byte *Attributes::attrBg(WindowStyle *styles) {
+	int revset = reverse || (styles[style].reverse && !Windows::_overrideReverse);
+
+	int zfset = fgset ? fgset : Windows::_overrideFgSet;
+	int zbset = bgset ? bgset : Windows::_overrideBgSet;
+
+	int zfore = fgset ? fgcolor : Windows::_overrideFgVal;
+	int zback = bgset ? bgcolor : Windows::_overrideBgVal;
+
+	if (zfset && zfore != Windows::_zcolor_fg) {
+		Windows::_zcolor_Foreground[0] = (zfore >> 16) & 0xff;
+		Windows::_zcolor_Foreground[1] = (zfore >> 8) & 0xff;
+		Windows::_zcolor_Foreground[2] = (zfore)& 0xff;
+		Windows::_zcolor_fg = zfore;
+	}
+
+	if (zbset && zback != Windows::_zcolor_bg) {
+		Windows::_zcolor_Background[0] = (zback >> 16) & 0xff;
+		Windows::_zcolor_Background[1] = (zback >> 8) & 0xff;
+		Windows::_zcolor_Background[2] = (zback)& 0xff;
+		Windows::_zcolor_bg = zback;
+	}
+
+	if (!revset) {
+		if (zbset)
+			return Windows::_zcolor_Background;
+		else
+			return styles[style].bg;
+	} else {
+		if (zfset)
+			if (zfore == zback)
+				return Windows::rgbShift(Windows::_zcolor_Foreground);
+			else
+				return Windows::_zcolor_Foreground;
+		else
+			if (zbset && !memcmp(styles[style].fg, Windows::_zcolor_Background, 3))
+				return Windows::_zcolor_LightGrey;
+			else
+				return styles[style].fg;
+	}
+}
+
+byte *Attributes::attrFg(WindowStyle *styles) {
+	int revset = reverse || (styles[style].reverse && !Windows::_overrideReverse);
+
+	int zfset = fgset ? fgset : Windows::_overrideFgSet;
+	int zbset = bgset ? bgset : Windows::_overrideBgSet;
+
+	int zfore = fgset ? fgcolor : Windows::_overrideFgVal;
+	int zback = bgset ? bgcolor : Windows::_overrideBgVal;
+
+	if (zfset && zfore != Windows::_zcolor_fg) {
+		Windows::_zcolor_Foreground[0] = (zfore >> 16) & 0xff;
+		Windows::_zcolor_Foreground[1] = (zfore >> 8) & 0xff;
+		Windows::_zcolor_Foreground[2] = (zfore)& 0xff;
+		Windows::_zcolor_fg = zfore;
+	}
+
+	if (zbset && zback != Windows::_zcolor_bg) {
+		Windows::_zcolor_Background[0] = (zback >> 16) & 0xff;
+		Windows::_zcolor_Background[1] = (zback >> 8) & 0xff;
+		Windows::_zcolor_Background[2] = (zback)& 0xff;
+		Windows::_zcolor_bg = zback;
+	}
+
+	if (!revset) {
+		if (zfset)
+			if (zfore == zback)
+				return Windows::rgbShift(Windows::_zcolor_Foreground);
+			else
+				return Windows::_zcolor_Foreground;
+		else
+			if (zbset && !memcmp(styles[style].fg, Windows::_zcolor_Background, 3))
+				return Windows::_zcolor_LightGrey;
+			else
+				return styles[style].fg;
+	} else {
+		if (zbset)
+			return Windows::_zcolor_Background;
+		else
+			return styles[style].bg;
+	}
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 24043c6..9a09641 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -27,17 +27,18 @@
 #include "common/list.h"
 #include "common/rect.h"
 #include "graphics/screen.h"
+#include "gargoyle/draw.h"
 #include "gargoyle/events.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/fonts.h"
 #include "gargoyle/picture.h"
 #include "gargoyle/streams.h"
+#include "gargoyle/window_mask.h"
 
 namespace Gargoyle {
 
 class Window;
 class PairWindow;
-struct WindowMask;
 
 #define HISTORYLEN 100
 #define SCROLLBACK 512
@@ -47,6 +48,7 @@ struct WindowMask;
  * Main windows manager
  */
 class Windows {
+	friend class Window;
 public:
 	class iterator {
 	private:
@@ -82,7 +84,6 @@ private:
 	Window * _windowList;      ///< List of all windows
 	Window *_rootWin;          ///< The topmost window
 	Window *_focusWin;         ///< The window selected by the player
-	bool _forceRedraw;
 	bool _moreFocus;
 	bool _claimSelect;
 	WindowMask *_mask;
@@ -105,8 +106,16 @@ public:
 	static bool _overrideReverse;
 	static bool _overrideFgSet;
 	static bool _overrideBgSet;
+	static bool _forceRedraw;
 	static int _overrideFgVal;
 	static int _overrideBgVal;
+	static int _zcolor_fg, _zcolor_bg;
+	static byte _zcolor_LightGrey[3];
+	static byte _zcolor_Foreground[3];
+	static byte _zcolor_Background[3];
+	static byte _zcolor_Bright[3];
+
+	static byte *rgbShift(byte *rgb);
 public:
 	/**
 	 * Constructor
@@ -114,6 +123,11 @@ public:
 	Windows(Graphics::Screen *screen);
 
 	/**
+	 * Destructor
+	 */
+	~Windows();
+
+	/**
 	 * Open a new window
 	 */
 	Window *windowOpen(Window *splitwin, glui32 method, glui32 size,
@@ -124,14 +138,35 @@ public:
 	 */
 	Window *getRoot() const { return _rootWin; }
 
+	/**
+	 * Gets the focused window
+	 */
+	Window *getFocusWindow() const { return _focusWin; }
+
+	/**
+	 * Setst the focused window
+	 */
+	void setFocus(Window *win) { _focusWin = win; }
+
 	void clearSelection();
 
+	void selectionChanged();
+
+	void clearClaimSelect() { _claimSelect = false; }
+
+	void redraw();
+
 	/**
 	 * Repaint an area of the windows
 	 */
 	void repaint(const Common::Rect &box);
 
 	/**
+	 * Draw an area of the windows
+	 */
+	void drawRect(int x0, int y0, int w, int h, const byte *rgb);
+
+	/**
 	 * Get an iterator that will move over the tree
 	 */
 	iterator begin() { return iterator(_windowList); }
@@ -140,6 +175,18 @@ public:
 	 * Returns the end point of window iteration
 	 */
 	iterator end() { return iterator(nullptr); }
+
+	/**
+	 * Gets a hyperlink
+	 */
+	glui32 getHyperlink(const Common::Point &pos) { return _mask->getHyperlink(pos); }
+
+	/**
+	 * Sets a hyperlink
+	 */
+	void setHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1) {
+		return _mask->putHyperlink(linkval, x0, y0, x1, y1);
+	}
 };
 
 /**
@@ -176,21 +223,37 @@ struct Attributes {
 	 * Clear
 	 */
 	void clear();
-};
 
-struct WindowMask {
-	int hor;
-	int ver;
-	glui32 **links;
-	Common::Rect select;
+	/**
+	 * Set the style
+	 */
+	void set(glui32 s) {
+		clear();
+		style = s;
+	}
+
+	/**
+	 * Equality comparison
+	 */
+	bool operator==(const Attributes &src) {
+		return fgset == src.fgset && bgset == src.bgset && reverse == src.reverse
+			&& style == src.style && fgcolor == src.fgcolor && bgcolor == src.bgcolor
+			&& hyper == src.hyper;
+	}
+
+	byte *attrBg(WindowStyle *styles);
+	byte *attrFg(WindowStyle *styles);
 
-	WindowMask() : hor(0), ver(0), links(nullptr) {}
+	/**
+	 * Get the font from the attribute's style
+	 */
+	FACES attrFont(WindowStyle *styles) const { return styles[style].font; }
 };
 
 /**
  * Window definition
  */
-class Window {
+class Window : public Draw {
 public:
 	Windows *_windows;
 	glui32 _magicnum;
@@ -199,8 +262,8 @@ public:
 
 	Window *_parent;       ///< pair window which contains this one
 	Window *_next, *_prev; ///< in the big linked list of windows
-	Common::Rect bbox;
-	int yadj;
+	Common::Rect _bbox;
+	int _yAdj;
 
 	Stream *_stream;       ///< the window stream.
 	Stream *_echoStream;   ///< the window's echo stream, if any.
@@ -216,7 +279,7 @@ public:
 	int _imageLoaded;
 
 	glui32 _echoLineInput;
-	glui32 *_lineTerminators;
+	glui32 *_lineTerminatorsBase;
 	glui32 _termCt;
 
 	Attributes _attr;
@@ -224,6 +287,8 @@ public:
 	byte _fgColor[3];
 
 	gidispatch_rock_t _dispRock;
+protected:
+	bool checkTerminator(glui32 ch);
 public:
 	/**
 	 * Constructor
@@ -233,12 +298,12 @@ public:
 	/**
 	 * Destructor
 	 */
-	virtual ~Window() {}
+	virtual ~Window();
 
 	/**
 	 * Rearranges the window
 	 */
-	virtual void rearrange(const Common::Rect &box) { bbox = box; }
+	virtual void rearrange(const Common::Rect &box) { _bbox = box; }
 
 	/**
 	 * Get window split size within parent pair window
@@ -246,29 +311,74 @@ public:
 	virtual glui32 getSplit(glui32 size, bool vertical) const { return 0; }
 
 	/**
-	 * Cancel a line event
+	 * Write a character
 	 */
-	virtual void cancelLineEvent(Event *ev);
+	virtual void putChar(unsigned char ch) {}
 
 	/**
-	 * Write a character
+	 * Write a unicode character
 	 */
-	virtual void putChar(unsigned char ch) { /* TODO */ }
+	virtual void putCharUni(uint32 ch) {}
 
 	/**
-	 * Write a unicode character
+	 * Unput a unicode character
 	 */
-	virtual void putCharUni(uint32 ch) { /* TODO */ }
+	virtual bool unputCharUni(uint32 ch) { return false; }
 
 	/**
 	 * Write a buffer
 	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) { /* TODO */ }
+	virtual void putBuffer(const unsigned char *buf, size_t len) {}
 
 	/**
 	 * Write a unicode character
 	 */
-	virtual void putBufferUni(const uint32 *buf, size_t len) { /* TODO */ }
+	virtual void putBufferUni(const uint32 *buf, size_t len) {}
+
+	/**
+	 * Move the cursor
+	 */
+	virtual void moveCursor(const Common::Point &newPos) {}
+
+	/**
+	 * Clear the window
+	 */
+	virtual void clear() {}
+
+	/**
+	 * Click the window
+	 */
+	virtual void click(const Common::Point &newPos) {}
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen);
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen);
+
+	/**
+	 * Cancel an input line event
+	 */
+	virtual void cancelLineEvent(Event *ev);
+
+	/**
+	 * Cancel a mouse event
+	 */
+	virtual void cancelMouseEvent() {}
+
+	/**
+	 * Cancel a hyperlink event
+	 */
+	virtual void cancelHyperlinkEvent() {}
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw();
 };
 typedef Window *winid_t;
 
@@ -291,8 +401,8 @@ class TextGridWindow : public Window {
 	 * Structure for a row within the grid window
 	 */
 	struct TextGridRow {
-		Common::Array<uint32> chars;
-		Common::Array<Attributes> attr;
+		Common::Array<uint32> _chars;
+		Common::Array<Attributes> _attrs;
 		bool dirty;
 
 		/**
@@ -311,13 +421,25 @@ private:
 	 * Mark a given text row as modified
 	 */
 	void touch(int line);
+
+	void acceptReadChar(glui32 arg);
+
+	/**
+	 * Return or enter, during line input. Ends line input. 
+	 */
+	void acceptLine(glui32 keycode);
+
+	/**
+	 * Any regular key, during line input.
+	 */
+	void acceptReadLine(glui32 arg);
 public:
 	int _width, _height;
 	TextGridRows _lines;
 
 	int _curX, _curY;    ///< the window cursor position
 
-                         ///< for line input
+						 ///< for line input
 	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
 	int _inOrgX, _inOrgY;
 	int _inMax;
@@ -349,9 +471,74 @@ public:
 	virtual glui32 getSplit(glui32 size, bool vertical) const override;
 
 	/**
-	 * Cancel a line event
+	 * Write a character
+	 */
+	virtual void putChar(unsigned char ch) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putCharUni(uint32 ch) override;
+
+	/**
+	 * Unput a unicode character
+	 */
+	virtual bool unputCharUni(uint32 ch) override;
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+	/**
+	 * Move the cursor
+	 */
+	virtual void moveCursor(const Common::Point &newPos) override;
+
+	/**
+	 * Clear the window
+	 */
+	virtual void clear() override;
+
+	/**
+	 * Click the window
+	 */
+	virtual void click(const Common::Point &newPos) override;
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) override;
+
+	/**
+	 * Cancel an input line event
 	 */
 	virtual void cancelLineEvent(Event *ev) override;
+
+	/**
+	 * Cancel a mouse event
+	 */
+	virtual void cancelMouseEvent() override { _mouseRequest = false; }
+
+	/**
+	 * Cancel a hyperlink event
+	 */
+	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
 };
 
 /**
@@ -385,8 +572,7 @@ private:
 	void reflow();
 	void touchScroll();
 	bool putPicture(Picture *pic, glui32 align, glui32 linkval);
-	void putCharUni(glui32 ch);
-	void putTextUnit(const glui32 *buf, int len, int pos, int oldlen);
+	void putTextUni(const glui32 *buf, int len, int pos, int oldlen);
 	void flowBreak();
 
 	/**
@@ -460,14 +646,64 @@ public:
 	virtual glui32 getSplit(glui32 size, bool vertical) const override;
 
 	/**
-	 * Cancel a line event
+	 * Write a character
 	 */
-	virtual void cancelLineEvent(Event *ev) override;
+	virtual void putChar(unsigned char ch) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putCharUni(uint32 ch) override;
+
+	/**
+	 * Unput a unicode character
+	 */
+	virtual bool unputCharUni(uint32 ch) override;
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+	/**
+	 * Move the cursor
+	 */
+	virtual void moveCursor(const Common::Point &newPos) override;
 
 	/**
 	 * Clear the window
 	 */
-	void clear();
+	virtual void clear() override;
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) override;
+
+	/**
+	 * Cancel an input line event
+	 */
+	virtual void cancelLineEvent(Event *ev) override;
+
+	/**
+	 * Cancel a hyperlink event
+	 */
+	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
 };
 
 /**
@@ -479,7 +715,7 @@ private:
 public:
 	unsigned char _bgnd[3];
 	bool _dirty;
-	int _w, _h;
+	glui32 _w, _h;
 	Graphics::ManagedSurface *_surface;
 public:
 	/**
@@ -503,6 +739,29 @@ public:
 	virtual glui32 getSplit(glui32 size, bool vertical) const override {
 		return size;
 	}
+
+	/**
+	 * Cancel a mouse event
+	 */
+	virtual void cancelMouseEvent() override { _mouseRequest = false; }
+
+	/**
+	 * Cancel a hyperlink event
+	 */
+	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
+
+	/**
+	 * Get the window dimensions
+	 */
+	void getSize(glui32 *w, glui32 *h) {
+		*w = _w;
+		*h = _h;
+	}
 };
 
 /**
@@ -530,6 +789,11 @@ public:
 	 * Rearranges the window
 	 */
 	virtual void rearrange(const Common::Rect &box) override;
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
 };
 
 } // End of namespace Gargoyle


Commit: 8708ed4f9aead0876d09dd9b6bb9096407cd961c
    https://github.com/scummvm/scummvm/commit/8708ed4f9aead0876d09dd9b6bb9096407cd961c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Split the windows.cpp file into separate files for each window class

Changed paths:
  A engines/gargoyle/window_graphics.cpp
  A engines/gargoyle/window_graphics.h
  A engines/gargoyle/window_pair.cpp
  A engines/gargoyle/window_pair.h
  A engines/gargoyle/window_text_buffer.cpp
  A engines/gargoyle/window_text_buffer.h
  A engines/gargoyle/window_text_grid.cpp
  A engines/gargoyle/window_text_grid.h
    engines/gargoyle/module.mk
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 46f2d31..e684ff9 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -11,8 +11,12 @@ MODULE_OBJS := \
 	picture.o \
 	streams.o \
 	string.o \
-	window_mask.o \
 	windows.o \
+	window_mask.o \
+	window_graphics.o \
+	window_pair.o \
+	window_text_buffer.o \
+	window_text_grid.o \
 	scott/detection.o \
 	scott/scott.o
 
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
new file mode 100644
index 0000000..cc9a5dd
--- /dev/null
+++ b/engines/gargoyle/window_graphics.cpp
@@ -0,0 +1,89 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/window_graphics.h"
+
+namespace Gargoyle {
+
+GraphicsWindow::GraphicsWindow(Windows *windows, uint32 rock) : Window(windows, rock),
+_w(0), _h(0), _dirty(false), _surface(nullptr) {
+	_type = wintype_Graphics;
+	Common::copy(&_bgColor[0], &_bgColor[3], _bgnd);
+}
+
+GraphicsWindow::~GraphicsWindow() {
+	delete _surface;
+}
+
+void GraphicsWindow::rearrange(const Common::Rect &box) {
+	int newwid, newhgt;
+	int bothwid, bothhgt;
+	int oldw, oldh;
+	Graphics::ManagedSurface *newSurface;
+
+	_bbox = box;
+
+	newwid = box.width();
+	newhgt = box.height();
+	oldw = _w;
+	oldh = _h;
+
+	if (newwid <= 0 || newhgt <= 0) {
+		_w = 0;
+		_h = 0;
+		delete _surface;
+		_surface = NULL;
+		return;
+	}
+
+	bothwid = _w;
+	if (newwid < bothwid)
+		bothwid = newwid;
+	bothhgt = _h;
+	if (newhgt < bothhgt)
+		bothhgt = newhgt;
+
+	newSurface = new Graphics::ManagedSurface(newwid, newhgt,
+		Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0));
+
+	// If the new surface is equal or bigger than the old one, copy it over
+	if (_surface && bothwid && bothhgt)
+		newSurface->blitFrom(*_surface);
+
+	delete _surface;
+	_surface = newSurface;
+	_w = newwid;
+	_h = newhgt;
+
+	touch();
+}
+
+void GraphicsWindow::touch() {
+	_dirty = true;
+	_windows->repaint(_bbox);
+}
+
+void GraphicsWindow::redraw() {
+	// TODO
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h
new file mode 100644
index 0000000..77e4910
--- /dev/null
+++ b/engines/gargoyle/window_graphics.h
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_WINDOW_GRAPHICS_H
+#define GARGOYLE_WINDOW_GRAPHICS_H
+
+#include "gargoyle/windows.h"
+#include "gargoyle/picture.h"
+
+namespace Gargoyle {
+
+/**
+ * Graphics window
+ */
+class GraphicsWindow : public Window {
+private:
+	void touch();
+public:
+	unsigned char _bgnd[3];
+	bool _dirty;
+	glui32 _w, _h;
+	Graphics::ManagedSurface *_surface;
+public:
+	/**
+	 * Constructor
+	 */
+	GraphicsWindow(Windows *windows, uint32 rock);
+
+	/**
+	 * Destructor
+	 */
+	virtual ~GraphicsWindow();
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box) override;
+
+	/**
+	 * Get window split size within parent pair window
+	 */
+	virtual glui32 getSplit(glui32 size, bool vertical) const override {
+		return size;
+	}
+
+	/**
+	 * Cancel a mouse event
+	 */
+	virtual void cancelMouseEvent() override { _mouseRequest = false; }
+
+	/**
+	 * Cancel a hyperlink event
+	 */
+	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
+
+	/**
+	 * Get the window dimensions
+	 */
+	void getSize(glui32 *w, glui32 *h) {
+		*w = _w;
+		*h = _h;
+	}
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/window_pair.cpp b/engines/gargoyle/window_pair.cpp
new file mode 100644
index 0000000..af3ed4c
--- /dev/null
+++ b/engines/gargoyle/window_pair.cpp
@@ -0,0 +1,125 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/window_pair.h"
+#include "gargoyle/conf.h"
+
+namespace Gargoyle {
+
+PairWindow::PairWindow(Windows *windows, glui32 method, Window *key, glui32 size) :
+		Window(windows, 0),
+		_dir(method & winmethod_DirMask),
+		_division(method & winmethod_DivisionMask),
+		_wBorder((method & winmethod_BorderMask) == winmethod_Border),
+		_vertical(_dir == winmethod_Left || _dir == winmethod_Right),
+		_backward(_dir == winmethod_Left || _dir == winmethod_Above),
+		_key(key), _size(size), _keyDamage(0), _child1(nullptr), _child2(nullptr) {
+	_type = wintype_Pair;
+}
+
+void PairWindow::rearrange(const Common::Rect &box) {
+	Common::Rect box1, box2;
+	int min, diff, split, splitwid, max;
+	Window *ch1, *ch2;
+
+	_bbox = box;
+
+	if (_vertical) {
+		min = _bbox.left;
+		max = _bbox.right;
+	} else {
+		min = _bbox.top;
+		max = _bbox.bottom;
+	}
+	diff = max - min;
+
+	// We now figure split.
+	if (_vertical)
+		splitwid = g_conf->_wPaddingX; // want border?
+	else
+		splitwid = g_conf->_wPaddingY; // want border?
+
+	switch (_division) {
+	case winmethod_Proportional:
+		split = (diff * _size) / 100;
+		break;
+
+	case winmethod_Fixed:
+		split = !_key ? 0 : _key->getSplit(_size, _vertical);
+		break;
+
+	default:
+		split = diff / 2;
+		break;
+	}
+
+	if (!_backward)
+		split = max - split - splitwid;
+	else
+		split = min + split;
+
+	if (min >= max) {
+		split = min;
+	} else {
+		if (split < min)
+			split = min;
+		else if (split > max - splitwid)
+			split = max - splitwid;
+	}
+
+	if (_vertical) {
+		box1.left = _bbox.left;
+		box1.right = split;
+		box2.left = split + splitwid;
+		box2.right = _bbox.right;
+		box1.top = _bbox.top;
+		box1.bottom = _bbox.bottom;
+		box2.top = _bbox.top;
+		box2.bottom = _bbox.bottom;
+	} else {
+		box1.top = _bbox.top;
+		box1.bottom = split;
+		box2.top = split + splitwid;
+		box2.bottom = _bbox.bottom;
+		box1.left = _bbox.left;
+		box1.right = _bbox.right;
+		box2.left = _bbox.left;
+		box2.right = _bbox.right;
+	}
+
+	if (!_backward) {
+		ch1 = _child1;
+		ch2 = _child2;
+	} else {
+		ch1 = _child2;
+		ch2 = _child1;
+	}
+
+	ch1->rearrange(box1);
+	ch2->rearrange(box2);
+}
+
+void PairWindow::redraw() {
+	// TODO
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_pair.h b/engines/gargoyle/window_pair.h
new file mode 100644
index 0000000..b25b324
--- /dev/null
+++ b/engines/gargoyle/window_pair.h
@@ -0,0 +1,64 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_WINDOW_PAIR_H
+#define GARGOYLE_WINDOW_PAIR_H
+
+#include "gargoyle/windows.h"
+
+namespace Gargoyle {
+
+/**
+ * Pair window
+ */
+class PairWindow : public Window {
+public:
+	Window *_child1, *_child2;
+
+	/* split info... */
+	glui32 _dir;               ///< winmethod_Left, Right, Above, or Below
+	bool _vertical, _backward; ///< flags
+	glui32 _division;          ///< winmethod_Fixed or winmethod_Proportional
+	Window *_key;              ///< NULL or a leaf-descendant (not a Pair)
+	int _keyDamage;            ///< used as scratch space in window closing
+	glui32 _size;              ///< size value
+	glui32 _wBorder;           ///< winMethod_Border, NoBorder
+public:
+	/**
+	 * Constructor
+	 */
+	PairWindow(Windows *windows, glui32 method, Window *key, glui32 size);
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box) override;
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
new file mode 100644
index 0000000..1e85a24
--- /dev/null
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -0,0 +1,602 @@
+/* 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 "gargoyle/window_text_buffer.h"
+#include "gargoyle/conf.h"
+#include "gargoyle/gargoyle.h"
+
+namespace Gargoyle {
+
+TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windows, rock),
+		_historyPos(0), _historyFirst(0), _historyPresent(0), _lastSeen(0), _scrollPos(0),
+		_scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1), _inBuf(nullptr),
+		_lineTerminators(nullptr), _echoLineInput(true), _ladjw(0), _radjw(0), _ladjn(0),
+		_radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr),
+		_spaced(0), _dashed(0), _copyBuf(0), _copyPos(0) {
+	_type = wintype_TextBuffer;
+	Common::fill(&_history[0], &_history[HISTORYLEN], nullptr);
+
+	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], styles);
+}
+
+TextBufferWindow::~TextBufferWindow() {
+	if (_inBuf) {
+		if (g_vm->gli_unregister_arr)
+			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
+		_inBuf = nullptr;
+	}
+
+	delete[] _copyBuf;
+	delete[] _lineTerminators;
+
+	for (int i = 0; i < _scrollBack; i++) {
+		if (_lines[i].lpic)
+			_lines[i].lpic->decrement();
+		if (_lines[i].rpic)
+			_lines[i].rpic->decrement();
+	}
+}
+
+void TextBufferWindow::rearrange(const Common::Rect &box) {
+	Window::rearrange(box);
+	int newwid, newhgt;
+	int rnd;
+
+	newwid = (box.width() - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) / g_conf->_cellW;
+	newhgt = (box.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
+
+	/* align text with bottom */
+	rnd = newhgt * g_conf->_cellH + g_conf->_tMarginY * 2;
+	_yAdj = (box.height() - rnd);
+	_bbox.top += (box.height() - rnd);
+
+	if (newwid != _width) {
+		_width = newwid;
+		reflow();
+	}
+
+	if (newhgt != _height) {
+		/* scroll up if we obscure new lines */
+		if (_lastSeen >= newhgt - 1)
+			_scrollPos += (_height - newhgt);
+
+		_height = newhgt;
+
+		/* keep window within 'valid' lines */
+		if (_scrollPos > _scrollMax - _height + 1)
+			_scrollPos = _scrollMax - _height + 1;
+		if (_scrollPos < 0)
+			_scrollPos = 0;
+		touchScroll();
+
+		/* allocate copy buffer */
+		if (_copyBuf)
+			delete[] _copyBuf;
+		_copyBuf = new glui32[_height * TBLINELEN];
+
+		for (int i = 0; i < (_height * TBLINELEN); i++)
+			_copyBuf[i] = 0;
+
+		_copyPos = 0;
+	}
+}
+
+void TextBufferWindow::reflow() {
+	int inputbyte = -1;
+	Attributes curattr, oldattr;
+	int i, k, p, s;
+	int x;
+
+	if (_height < 4 || _width < 20)
+		return;
+
+	_lines[0].len = _numChars;
+
+	/* allocate temp buffers */
+	Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN];
+	glui32 *charbuf = new glui32[SCROLLBACK * TBLINELEN];
+	int *alignbuf = new int[SCROLLBACK];
+	Picture **pictbuf = new Picture *[SCROLLBACK];
+	glui32 *hyperbuf = new glui32[SCROLLBACK];
+	int *offsetbuf = new int[SCROLLBACK];
+
+	if (!attrbuf || !charbuf || !alignbuf || !pictbuf || !hyperbuf || !offsetbuf) {
+		delete[] attrbuf;
+		delete[] charbuf;
+		delete[] alignbuf;
+		delete[] pictbuf;
+		delete[] hyperbuf;
+		delete[] offsetbuf;
+		return;
+	}
+
+	/* copy text to temp buffers */
+
+	oldattr = _attr;
+	curattr.clear();
+
+	x = 0;
+	p = 0;
+	s = _scrollMax < SCROLLBACK ? _scrollMax : SCROLLBACK - 1;
+
+	for (k = s; k >= 0; k--) {
+		if (k == 0 && _lineRequest)
+			inputbyte = p + _inFence;
+
+		if (_lines[k].lpic) {
+			offsetbuf[x] = p;
+			alignbuf[x] = imagealign_MarginLeft;
+			pictbuf[x] = _lines[k].lpic;
+
+			if (pictbuf[x]) pictbuf[x]->increment();
+			hyperbuf[x] = _lines[k].lhyper;
+			x++;
+		}
+
+		if (_lines[k].rpic) {
+			offsetbuf[x] = p;
+			alignbuf[x] = imagealign_MarginRight;
+			pictbuf[x] = _lines[k].rpic;
+			if (pictbuf[x]) pictbuf[x]->increment();
+			hyperbuf[x] = _lines[k].rhyper;
+			x++;
+		}
+
+		for (i = 0; i < _lines[k].len; i++) {
+			attrbuf[p] = curattr = _lines[k].attr[i];
+			charbuf[p] = _lines[k].chars[i];
+			p++;
+		}
+
+		if (_lines[k].newline) {
+			attrbuf[p] = curattr;
+			charbuf[p] = '\n';
+			p++;
+		}
+	}
+
+	offsetbuf[x] = -1;
+
+	/* clear window */
+
+	clear();
+
+	/* and dump text back */
+
+	x = 0;
+	for (i = 0; i < p; i++) {
+		if (i == inputbyte)
+			break;
+		_attr = attrbuf[i];
+
+		if (offsetbuf[x] == i) {
+			putPicture(pictbuf[x], alignbuf[x], hyperbuf[x]);
+			x++;
+		}
+
+		putCharUni(charbuf[i]);
+	}
+
+	/* terribly sorry about this... */
+	_lastSeen = 0;
+	_scrollPos = 0;
+
+	if (inputbyte != -1) {
+		_inFence = _numChars;
+		putTextUni(charbuf + inputbyte, p - inputbyte, _numChars, 0);
+		_inCurs = _numChars;
+	}
+
+	// free temp buffers
+	delete[] attrbuf;
+	delete[] charbuf;
+	delete[] alignbuf;
+	delete[] pictbuf;
+	delete[] hyperbuf;
+	delete[] offsetbuf;
+
+	_attr = oldattr;
+
+	touchScroll();
+}
+
+void TextBufferWindow::touchScroll() {
+	_windows->clearSelection();
+	_windows->repaint(_bbox);
+
+	for (int i = 0; i < _scrollMax; i++)
+		_lines[i].dirty = true;
+}
+
+bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
+	if (align == imagealign_MarginRight)
+	{
+		if (_lines[0].rpic || _numChars)
+			return false;
+
+		_radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
+		_radjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
+		_lines[0].rpic = pic;
+		_lines[0].rm = _radjw;
+		_lines[0].rhyper = linkval;
+	} else {
+		if (align != imagealign_MarginLeft && _numChars)
+			putCharUni('\n');
+
+		if (_lines[0].lpic || _numChars)
+			return false;
+
+		_ladjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
+		_ladjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
+		_lines[0].lpic = pic;
+		_lines[0].lm = _ladjw;
+		_lines[0].lhyper = linkval;
+
+		if (align != imagealign_MarginLeft)
+			flowBreak();
+	}
+
+	return true;
+}
+
+void TextBufferWindow::flowBreak() {
+	// TODO
+}
+
+void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldlen) {
+	// TODO
+}
+
+void TextBufferWindow::touch(int line) {
+	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
+	_lines[line].dirty = 1;
+	_windows->clearSelection();
+	_windows->repaint(Common::Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
+}
+
+glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
+	return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
+}
+
+void TextBufferWindow::putChar(unsigned char ch) {
+}
+
+void TextBufferWindow::putCharUni(glui32 ch) {
+	/*
+	glui32 bchars[TBLINELEN];
+	Attributes battrs[TBLINELEN];
+	int pw;
+	int bpoint;
+	int saved;
+	int i;
+	int linelen;
+	unsigned char *color;
+
+	gli_tts_speak(&ch, 1);
+
+	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - gli_scroll_width) * GLI_SUBPIX;
+	pw = pw - 2 * SLOP - radjw - ladjw;
+
+	color = Windows::_overrideBgSet ? gli_window_color : bgcolor;
+
+	// oops ... overflow
+	if (numchars + 1 >= TBLINELEN)
+		scrolloneline(dwin, 0);
+
+	if (ch == '\n') {
+		scrolloneline(dwin, 1);
+		return;
+	}
+
+	if (gli_conf_quotes) {
+		// fails for 'tis a wonderful day in the '80s
+		if (gli_conf_quotes > 1 && ch == '\'')
+		{
+			if (numchars == 0 || leftquote(_chars[numchars - 1]))
+				ch = UNI_LSQUO;
+		}
+
+		if (ch == '`')
+			ch = UNI_LSQUO;
+
+		if (ch == '\'')
+			ch = UNI_RSQUO;
+
+		if (ch == '"')
+		{
+			if (numchars == 0 || leftquote(_chars[numchars - 1]))
+				ch = UNI_LDQUO;
+			else
+				ch = UNI_RDQUO;
+		}
+	}
+
+	if (gli_conf_dashes && attr.style != style_Preformatted)
+	{
+		if (ch == '-')
+		{
+			dashed++;
+			if (dashed == 2)
+			{
+				numchars--;
+				if (gli_conf_dashes == 2)
+					ch = UNI_NDASH;
+				else
+					ch = UNI_MDASH;
+			}
+			if (dashed == 3)
+			{
+				numchars--;
+				ch = UNI_MDASH;
+				dashed = 0;
+			}
+		}
+		else
+			dashed = 0;
+	}
+
+	if (gli_conf_spaces && attr.style != style_Preformatted
+		&& styles[attr.style].bg == color
+		&& !styles[attr.style].reverse)
+	{
+		// turn (period space space) into (period space)
+		if (gli_conf_spaces == 1)
+		{
+			if (ch == '.')
+				spaced = 1;
+			else if (ch == ' ' && spaced == 1)
+				spaced = 2;
+			else if (ch == ' ' && spaced == 2)
+			{
+				spaced = 0;
+				return;
+			}
+			else
+				spaced = 0;
+		}
+
+		// Turn (per sp x) into (per sp sp x)
+		if (gli_conf_spaces == 2)
+		{
+			if (ch == '.')
+				spaced = 1;
+			else if (ch == ' ' && spaced == 1)
+				spaced = 2;
+			else if (ch != ' ' && spaced == 2)
+			{
+				spaced = 0;
+				win_textbuffer_putchar_uni(win, ' ');
+			}
+			else
+				spaced = 0;
+		}
+	}
+
+	_chars[numchars] = ch;
+	attrs[numchars] = attr;
+	numchars++;
+
+	// kill spaces at the end for line width calculation
+	linelen = numchars;
+	while (linelen > 1 && _chars[linelen - 1] == ' '
+		&& styles[attrs[linelen - 1].style].bg == color
+		&& !styles[attrs[linelen - 1].style].reverse)
+		linelen--;
+
+	if (calcwidth(dwin, _chars, attrs, 0, linelen, -1) >= pw)
+	{
+		bpoint = numchars;
+
+		for (i = numchars - 1; i > 0; i--)
+			if (_chars[i] == ' ')
+			{
+				bpoint = i + 1; // skip space
+				break;
+			}
+
+		saved = numchars - bpoint;
+
+		memcpy(bchars, _chars + bpoint, saved * 4);
+		memcpy(battrs, attrs + bpoint, saved * sizeof(attr_t));
+		numchars = bpoint;
+
+		scrolloneline(dwin, 0);
+
+		memcpy(_chars, bchars, saved * 4);
+		memcpy(attrs, battrs, saved * sizeof(attr_t));
+		numchars = saved;
+	}
+
+	touch(0);
+	*/
+}
+
+bool TextBufferWindow::unputCharUni(uint32 ch) {
+	// TODO
+	return false;
+}
+
+void TextBufferWindow::putBuffer(const unsigned char *buf, size_t len) {
+	// TODO
+}
+
+void TextBufferWindow::putBufferUni(const uint32 *buf, size_t len) {
+	// TODO
+}
+
+void TextBufferWindow::moveCursor(const Common::Point &newPos) {
+	// TODO
+}
+
+void TextBufferWindow::clear() {
+	int i;
+
+	_attr.fgset = Windows::_overrideFgSet;
+	_attr.bgset = Windows::_overrideBgSet;
+	_attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
+	_attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
+	_attr.reverse = false;
+
+	_ladjw = _radjw = 0;
+	_ladjn = _radjn = 0;
+
+	_spaced = 0;
+	_dashed = 0;
+
+	_numChars = 0;
+
+	for (i = 0; i < _scrollBack; i++) {
+		_lines[i].len = 0;
+
+		if (_lines[i].lpic) _lines[i].lpic->decrement();
+		_lines[i].lpic = nullptr;
+		if (_lines[i].rpic) _lines[i].rpic->decrement();
+		_lines[i].rpic = nullptr;
+
+		_lines[i].lhyper = 0;
+		_lines[i].rhyper = 0;
+		_lines[i].lm = 0;
+		_lines[i].rm = 0;
+		_lines[i].newline = 0;
+		_lines[i].dirty = true;
+		_lines[i].repaint = false;
+	}
+
+	_lastSeen = 0;
+	_scrollPos = 0;
+	_scrollMax = 0;
+
+	for (i = 0; i < _height; i++)
+		touch(i);
+}
+
+void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
+	{
+		warning("request_line_event: window already has keyboard request");
+		return;
+	}
+
+	// TODO
+}
+
+void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
+	{
+		warning("request_line_event_uni: window already has keyboard request");
+		return;
+	}
+
+	// TODO
+}
+
+void TextBufferWindow::cancelLineEvent(Event *ev) {
+	gidispatch_rock_t inarrayrock;
+	int ix;
+	int len;
+	void *inbuf;
+	int inmax;
+	int unicode = _lineRequestUni;
+	Event dummyEv;
+
+	if (!ev)
+		ev = &dummyEv;
+
+	g_vm->_events->clearEvent(ev);
+
+	if (!_lineRequest && !_lineRequestUni)
+		return;
+
+	if (!_inBuf)
+		return;
+
+	inbuf = _inBuf;
+	inmax = _inMax;
+	inarrayrock = _inArrayRock;
+
+	len = _numChars - _inFence;
+	if (_echoStream)
+		_echoStream->echoLineUni(_chars + _inFence, len);
+
+	if (len > inmax)
+		len = inmax;
+
+	if (!unicode) {
+		for (ix = 0; ix<len; ix++) {
+			glui32 ch = _chars[_inFence + ix];
+			if (ch > 0xff)
+				ch = '?';
+			((char *)inbuf)[ix] = (char)ch;
+		}
+	}
+	else {
+		for (ix = 0; ix<len; ix++)
+			((glui32 *)inbuf)[ix] = _chars[_inFence + ix];
+	}
+
+	_attr = _origAttr;
+
+	ev->_type = evtype_LineInput;
+	ev->_window = this;
+	ev->_val1 = len;
+	ev->_val2 = 0;
+
+	_lineRequest = false;
+	_lineRequestUni = false;
+	if (_lineTerminators) {
+		free(_lineTerminators);
+		_lineTerminators = nullptr;
+	}
+	_inBuf = nullptr;
+	_inMax = 0;
+
+	if (_echoLineInput) {
+		putCharUni('\n');
+	}
+	else {
+		_numChars = _inFence;
+		touch(0);
+	}
+
+	if (g_vm->gli_unregister_arr)
+		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+void TextBufferWindow::redraw() {
+	// TODO
+}
+
+/*--------------------------------------------------------------------------*/
+
+TextBufferWindow::TextBufferRow::TextBufferRow() : len(0), newline(0), dirty(false), repaint(false),
+		lpic(nullptr), rpic(nullptr), lhyper(0), rhyper(0), lm(0), rm(0) {
+}
+
+void TextBufferWindow::TextBufferRow::resize(size_t newSize) {
+	chars.clear();
+	attr.clear();
+	chars.resize(newSize);
+	attr.resize(newSize);
+	Common::fill(&chars[0], &chars[0] + newSize, ' ');
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
new file mode 100644
index 0000000..f7427d7
--- /dev/null
+++ b/engines/gargoyle/window_text_buffer.h
@@ -0,0 +1,198 @@
+/* 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 GARGOYLE_WINDOW_TEXT_BUFFER_H
+#define GARGOYLE_WINDOW_TEXT_BUFFER_H
+
+#include "gargoyle/windows.h"
+#include "gargoyle/picture.h"
+
+namespace Gargoyle {
+
+/**
+ * Text Buffer window
+ */
+class TextBufferWindow : public Window {
+	/**
+	 * Structure for a row within the window
+	 */
+	struct TextBufferRow {
+		Common::Array<uint32> chars;
+		Common::Array<Attributes> attr;
+		int len, newline;
+		bool dirty, repaint;
+		Picture *lpic, *rpic;
+		glui32 lhyper, rhyper;
+		int lm, rm;
+
+		/**
+		 * Constructor
+		 */
+		TextBufferRow();
+
+		/**
+		 * Resize the row
+		 */
+		void resize(size_t newSize);
+	};
+	typedef Common::Array<TextBufferRow> TextBufferRows;
+private:
+	void reflow();
+	void touchScroll();
+	bool putPicture(Picture *pic, glui32 align, glui32 linkval);
+	void putTextUni(const glui32 *buf, int len, int pos, int oldlen);
+	void flowBreak();
+
+	/**
+	 * Mark a given text row as modified
+	 */
+	void touch(int line);
+public:
+	int _width, _height;
+	int _spaced;
+	int _dashed;
+
+	TextBufferRows _lines;
+	int _scrollBack;
+
+	int _numChars;        ///< number of chars in last line: lines[0]
+	glui32 *_chars;       ///< alias to lines[0].chars
+	Attributes *_attrs;  ///< alias to lines[0].attrs
+
+    ///< adjust margins temporarily for images
+	int _ladjw;
+	int _ladjn;
+	int _radjw;
+	int _radjn;
+
+	/* Command history. */
+	glui32 *_history[HISTORYLEN];
+	int _historyPos;
+	int _historyFirst, _historyPresent;
+
+	/* for paging */
+	int _lastSeen;
+	int _scrollPos;
+	int _scrollMax;
+
+	/* for line input */
+	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
+	int _inMax;
+	long _inFence;
+	long _inCurs;
+	Attributes _origAttr;
+	gidispatch_rock_t _inArrayRock;
+
+	glui32 _echoLineInput;
+	glui32 *_lineTerminators;
+
+	/* style hints and settings */
+	WindowStyle styles[style_NUMSTYLES];
+
+	/* for copy selection */
+	glui32 *_copyBuf;
+	int _copyPos;
+public:
+	/**
+	 * Constructor
+	 */
+	TextBufferWindow(Windows *windows, uint32 rock);
+
+	/**
+	 * Destructor
+	 */
+	virtual ~TextBufferWindow();
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box) override;
+
+	/**
+	 * Get window split size within parent pair window
+	 */
+	virtual glui32 getSplit(glui32 size, bool vertical) const override;
+
+	/**
+	 * Write a character
+	 */
+	virtual void putChar(unsigned char ch) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putCharUni(uint32 ch) override;
+
+	/**
+	 * Unput a unicode character
+	 */
+	virtual bool unputCharUni(uint32 ch) override;
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+	/**
+	 * Move the cursor
+	 */
+	virtual void moveCursor(const Common::Point &newPos) override;
+
+	/**
+	 * Clear the window
+	 */
+	virtual void clear() override;
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) override;
+
+	/**
+	 * Cancel an input line event
+	 */
+	virtual void cancelLineEvent(Event *ev) override;
+
+	/**
+	 * Cancel a hyperlink event
+	 */
+	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
new file mode 100644
index 0000000..41444ed
--- /dev/null
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -0,0 +1,665 @@
+/* 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 "gargoyle/window_text_grid.h"
+#include "gargoyle/conf.h"
+#include "gargoyle/gargoyle.h"
+
+namespace Gargoyle {
+
+TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
+	_type = wintype_TextGrid;
+	_width = _height = 0;
+	_curX = _curY = 0;
+	_inBuf = nullptr;
+	_inOrgX = _inOrgY = 0;
+	_inMax = 0;
+	_inCurs = _inLen = 0;
+	_inArrayRock.num = 0;
+	_lineTerminators = nullptr;
+
+	Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], styles);
+}
+
+TextGridWindow::~TextGridWindow() {
+	if (_inBuf) {
+		if (g_vm->gli_unregister_arr)
+			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
+		_inBuf = nullptr;
+	}
+
+	delete[] _lineTerminators;
+}
+
+void TextGridWindow::rearrange(const Common::Rect &box) {
+	Window::rearrange(box);
+	int newwid, newhgt;
+
+	newwid = box.width() / g_conf->_cellW;
+	newhgt = box.height() / g_conf->_cellH;
+
+	if (newwid == _width && newhgt == _height)
+		return;
+
+	_lines.resize(newhgt);
+	for (int y = 0; y < newhgt; ++y) {
+		_lines[y].resize(newwid);
+		touch(y);
+	}
+
+	_attr.clear();
+	_width = newwid;
+	_height = newhgt;
+}
+
+void TextGridWindow::touch(int line) {
+	int y = _bbox.top + line * g_conf->_leading;
+	_lines[line].dirty = true;
+	_windows->repaint(Common::Rect(_bbox.left, y, _bbox.right, y + g_conf->_leading));
+}
+
+glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
+	return vertical ? size * g_conf->_cellW + g_conf->_tMarginX * 2 :
+		size * g_conf->_cellH + g_conf->_tMarginY * 2;
+}
+
+void TextGridWindow::putChar(unsigned char ch) {
+
+}
+
+void TextGridWindow::putCharUni(uint32 ch) {
+	TextGridRow *ln;
+
+	// Canonicalize the cursor position. That is, the cursor may have been
+	// left outside the window area; wrap it if necessary.
+	if (_curX < 0) {
+		_curX = 0;
+	} else if (_curX >= _width) {
+		_curX = 0;
+		_curY++;
+	}
+	if (_curY < 0)
+		_curY = 0;
+	else if (_curY >= _height)
+		return; /* outside the window */
+
+	if (ch == '\n') {
+		/* a newline just moves the cursor. */
+		_curY++;
+		_curX = 0;
+		return;
+	}
+
+	touch(_curY);
+
+	ln = &(_lines[_curY]);
+	ln->_chars[_curX] = ch;
+	ln->_attrs[_curX] = _attr;
+
+	_curX++;
+	// We can leave the cursor outside the window, since it will be
+	// canonicalized next time a character is printed.
+}
+
+bool TextGridWindow::unputCharUni(uint32 ch) {
+	TextGridRow *ln;
+	int oldx = _curX, oldy = _curY;
+
+	/* Move the cursor back. */
+	if (_curX >= _width)
+		_curX = _width - 1;
+	else
+		_curX--;
+
+	/* Canonicalize the cursor position. That is, the cursor may have been
+	left outside the window area; wrap it if necessary. */
+	if (_curX < 0) {
+		_curX = _width - 1;
+		_curY--;
+	}
+	if (_curY < 0)
+		_curY = 0;
+	else if (_curY >= _height)
+		return false; // outside the window
+
+	if (ch == '\n') {
+		// a newline just moves the cursor.
+		if (_curX == _width - 1)
+			return 1; // deleted a newline
+		_curX = oldx;
+		_curY = oldy;
+		return 0;    // it wasn't there */
+	}
+
+	ln = &(_lines[_curY]);
+	if (ln->_chars[_curX] == ch) {
+		ln->_chars[_curX] = ' ';
+		ln->_attrs[_curX].clear();
+		touch(_curY);
+		return true; // deleted the char
+	} else {
+		_curX = oldx;
+		_curY = oldy;
+		return false; // it wasn't there
+	}
+}
+
+void TextGridWindow::putBuffer(const unsigned char *buf, size_t len) {
+	// TODO
+}
+
+void TextGridWindow::putBufferUni(const uint32 *buf, size_t len) {
+	// TODO
+}
+
+void TextGridWindow::moveCursor(const Common::Point &pos) {
+	// If the values are negative, they're really huge positive numbers --
+	// remember that they were cast from glui32. So set them huge and
+	// let canonicalization take its course.
+	_curX = (pos.x < 0) ? 32767 : pos.x;
+	_curY = (pos.y < 0) ? 32767 : pos.y;
+}
+
+void TextGridWindow::clear() {
+	_attr.fgset = Windows::_overrideFgSet;
+	_attr.bgset = Windows::_overrideBgSet;
+	_attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
+	_attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
+	_attr.reverse = false;
+
+	for (int k = 0; k < _height; k++) {
+		TextGridRow &ln = _lines[k];
+		touch(k);
+		for (uint j = 0; j < ln._attrs.size(); ++j) {
+			ln._chars[j] = ' ';
+			ln._attrs[j].clear();
+		}
+	}
+
+	_curX = 0;
+	_curY = 0;
+}
+
+void TextGridWindow::click(const Common::Point &newPos) {
+	int x = newPos.x - _bbox.left;
+	int y = newPos.y - _bbox.top;
+
+	if (_lineRequest || _charRequest || _lineRequestUni || _charRequestUni
+			|| _moreRequest || _scrollRequest)
+		_windows->setFocus(this);
+
+	if (_mouseRequest) {
+		g_vm->_events->eventStore(evtype_MouseInput, this, x / g_conf->_cellW, y / g_conf->_leading);
+		_mouseRequest = false;
+		if (g_conf->_safeClicks)
+			g_vm->_events->_forceClick = true;
+	}
+
+	if (_hyperRequest) {
+		glui32 linkval = _windows->getHyperlink(newPos);
+		if (linkval)
+		{
+			g_vm->_events->eventStore(evtype_Hyperlink, this, linkval, 0);
+			_hyperRequest = false;
+			if (g_conf->_safeClicks)
+				g_vm->_events->_forceClick = true;
+		}
+	}
+}
+
+void TextGridWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
+	{
+		warning("request_line_event: window already has keyboard request");
+		return;
+	}
+
+	if ((int)maxlen > (_width - _curX))
+		maxlen = (_width - _curX);
+
+	_inBuf = buf;
+	_inMax = maxlen;
+	_inLen = 0;
+	_inCurs = 0;
+	_inOrgX = _curX;
+	_inOrgY = _curY;
+	_origAttr = _attr;
+	_attr.set(style_Input);
+
+	if (initlen > maxlen)
+		initlen = maxlen;
+
+	if (initlen) {
+		TextGridRow *ln = &_lines[_inOrgY];
+
+		for (glui32 ix = 0; ix < initlen; ix++) {
+			ln->_attrs[_inOrgX + ix].set(style_Input);
+			ln->_chars[_inOrgX + ix] = buf[ix];
+		}
+
+		_inCurs += initlen;
+		_inLen += initlen;
+		_curX = _inOrgX + _inCurs;
+		_curY = _inOrgY;
+
+		touch(_inOrgY);
+	}
+
+	if (_lineTerminatorsBase && _termCt) {
+		_lineTerminators = new glui32[_termCt + 1];
+
+		if (_lineTerminators) {
+			memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+			_lineTerminators[_termCt] = 0;
+		}
+	}
+
+	if (g_vm->gli_register_arr)
+		_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Cn");
+}
+
+void TextGridWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
+		warning("requestLineEventUni: window already has keyboard request");
+		return;
+	}
+
+	if ((int)maxlen > (_width - _curX))
+		maxlen = (_width - _curX);
+
+	_inBuf = buf;
+	_inMax = maxlen;
+	_inLen = 0;
+	_inCurs = 0;
+	_inOrgX = _curX;
+	_inOrgY = _curY;
+	_origAttr = _attr;
+	_attr.set(style_Input);
+
+	if (initlen > maxlen)
+		initlen = maxlen;
+
+	if (initlen) {
+		TextGridRow *ln = &(_lines[_inOrgY]);
+
+		for (glui32 ix = 0; ix<initlen; ix++) {
+			ln->_attrs[_inOrgX + ix].set(style_Input);
+			ln->_chars[_inOrgX + ix] = buf[ix];
+		}
+
+		_inCurs += initlen;
+		_inLen += initlen;
+		_curX = _inOrgX + _inCurs;
+		_curY = _inOrgY;
+
+		touch(_inOrgY);
+	}
+	
+	if (_lineTerminatorsBase && _termCt) {
+		_lineTerminators = new glui32[_termCt + 1];
+
+		if (_lineTerminators) {
+			memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+			_lineTerminators[_termCt] = 0;
+		}
+	}
+
+	if (g_vm->gli_register_arr)
+		_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Iu");
+}
+
+void TextGridWindow::cancelLineEvent(Event *ev) {
+	int ix;
+	void *inbuf;
+	int inmax;
+	int unicode = _lineRequestUni;
+	gidispatch_rock_t inarrayrock;
+	TextGridRow *ln = &_lines[_inOrgY];
+	Event dummyEv;
+
+	if (!ev)
+		ev = &dummyEv;
+
+	g_vm->_events->clearEvent(ev);
+
+	if (!_lineRequest && !_lineRequestUni)
+		return;
+
+
+	inbuf = _inBuf;
+	inmax = _inMax;
+	inarrayrock = _inArrayRock;
+
+	if (!unicode) {
+		for (ix = 0; ix<_inLen; ix++)
+		{
+			glui32 ch = ln->_chars[_inOrgX + ix];
+			if (ch > 0xff)
+				ch = '?';
+			((char *)inbuf)[ix] = (char)ch;
+		}
+		if (_echoStream)
+			_echoStream->echoLine((char *)_inBuf, _inLen);
+	} else {
+		for (ix = 0; ix<_inLen; ix++)
+			((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
+		if (_echoStream)
+			_echoStream->echoLineUni((glui32 *)inbuf, _inLen);
+	}
+
+	_curY = _inOrgY + 1;
+	_curX = 0;
+	_attr = _origAttr;
+
+	ev->_type = evtype_LineInput;
+	ev->_window = this;
+	ev->_val1 = _inLen;
+	ev->_val2 = 0;
+
+	_lineRequest = false;
+	_lineRequestUni = false;
+
+	if (_lineTerminators) {
+		free(_lineTerminators);
+		_lineTerminators = nullptr;
+	}
+
+	_inBuf = nullptr;
+	_inMax = 0;
+	_inOrgX = 0;
+	_inOrgY = 0;
+
+	if (g_vm->gli_unregister_arr)
+		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+void TextGridWindow::acceptReadChar(glui32 arg) {
+	glui32 key;
+
+	switch (arg)
+	{
+	case keycode_Erase:
+		key = keycode_Delete;
+		break;
+	case keycode_MouseWheelUp:
+	case keycode_MouseWheelDown:
+		return;
+	default:
+		key = arg;
+	}
+
+	if (key > 0xff && key < (0xffffffff - keycode_MAXVAL + 1))
+	{
+		if (!(_charRequestUni) || key > 0x10ffff)
+			key = keycode_Unknown;
+	}
+
+	_charRequest = false;
+	_charRequestUni = false;
+	g_vm->_events->eventStore(evtype_CharInput, this, key, 0);
+}
+
+void TextGridWindow::acceptLine(glui32 keycode) {
+	int ix;
+	void *inbuf;
+	int inmax;
+	gidispatch_rock_t inarrayrock;
+	TextGridRow *ln = &(_lines[_inOrgY]);
+	int unicode = _lineRequestUni;
+
+	if (!_inBuf)
+		return;
+
+	inbuf = _inBuf;
+	inmax = _inMax;
+	inarrayrock = _inArrayRock;
+
+	if (!unicode) {
+		for (ix = 0; ix<_inLen; ix++)
+			((char *)inbuf)[ix] = (char)ln->_chars[_inOrgX + ix];
+		if (_echoStream)
+			_echoStream->echoLine((char *)inbuf, _inLen);
+	} else {
+		for (ix = 0; ix<_inLen; ix++)
+			((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
+		if (_echoStream)
+			_echoStream->echoLineUni((glui32 *)inbuf, _inLen);
+	}
+
+	_curY = _inOrgY + 1;
+	_curX = 0;
+	_attr = _origAttr;
+
+	if (_lineTerminators)
+	{
+		glui32 val2 = keycode;
+		if (val2 == keycode_Return)
+			val2 = 0;
+		g_vm->_events->eventStore(evtype_LineInput, this, _inLen, val2);
+		free(_lineTerminators);
+		_lineTerminators = NULL;
+	} else {
+		g_vm->_events->eventStore(evtype_LineInput, this, _inLen, 0);
+	}
+	_lineRequest = false;
+	_lineRequestUni = false;
+	_inBuf = NULL;
+	_inMax = 0;
+	_inOrgX = 0;
+	_inOrgY = 0;
+
+	if (g_vm->gli_unregister_arr)
+		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+void TextGridWindow::acceptReadLine(glui32 arg) {
+	int ix;
+	TextGridRow *ln = &(_lines[_inOrgY]);
+
+	if (!_inBuf)
+		return;
+
+	if (_lineTerminators && checkTerminator(arg)) {
+		glui32 *cx;
+		for (cx = _lineTerminators; *cx; cx++) {
+			if (*cx == arg) {
+				acceptLine(arg);
+				return;
+			}
+		}
+	}
+
+	switch (arg) {
+
+		/* Delete keys, during line input. */
+
+	case keycode_Delete:
+		if (_inLen <= 0)
+			return;
+		if (_inCurs <= 0)
+			return;
+		for (ix = _inCurs; ix<_inLen; ix++)
+			ln->_chars[_inOrgX + ix - 1] = ln->_chars[_inOrgX + ix];
+		ln->_chars[_inOrgX + _inLen - 1] = ' ';
+		_inCurs--;
+		_inLen--;
+		break;
+
+	case keycode_Erase:
+		if (_inLen <= 0)
+			return;
+		if (_inCurs >= _inLen)
+			return;
+		for (ix = _inCurs; ix<_inLen - 1; ix++)
+			ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix + 1];
+		ln->_chars[_inOrgX + _inLen - 1] = ' ';
+		_inLen--;
+		break;
+
+	case keycode_Escape:
+		if (_inLen <= 0)
+			return;
+		for (ix = 0; ix<_inLen; ix++)
+			ln->_chars[_inOrgX + ix] = ' ';
+		_inLen = 0;
+		_inCurs = 0;
+		break;
+
+		/* Cursor movement keys, during line input. */
+
+	case keycode_Left:
+		if (_inCurs <= 0)
+			return;
+		_inCurs--;
+		break;
+
+	case keycode_Right:
+		if (_inCurs >= _inLen)
+			return;
+		_inCurs++;
+		break;
+
+	case keycode_Home:
+		if (_inCurs <= 0)
+			return;
+		_inCurs = 0;
+		break;
+
+	case keycode_End:
+		if (_inCurs >= _inLen)
+			return;
+		_inCurs = _inLen;
+		break;
+
+	case keycode_Return:
+		acceptLine(arg);
+		break;
+
+	default:
+		if (_inLen >= _inMax)
+			return;
+
+		if (arg < 32 || arg > 0xff)
+			return;
+
+		if (g_conf->_caps && (arg > 0x60 && arg < 0x7b))
+			arg -= 0x20;
+
+		for (ix = _inLen; ix>_inCurs; ix--)
+			ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix - 1];
+		ln->_attrs[_inOrgX + _inLen].set(style_Input);
+		ln->_chars[_inOrgX + _inCurs] = arg;
+
+		_inCurs++;
+		_inLen++;
+	}
+
+	_curX = _inOrgX + _inCurs;
+	_curY = _inOrgY;
+
+	touch(_inOrgY);
+}
+
+void TextGridWindow::redraw() {
+	TextGridRow *ln;
+	int x0, y0;
+	int x, y, w;
+	int i, a, b, k, o;
+	glui32 link;
+	int font;
+	byte *fgcolor, *bgcolor;
+
+	x0 = _bbox.left;
+	y0 = _bbox.top;
+
+	for (i = 0; i < _height; i++) {
+		ln = &_lines[i];
+		if (ln->dirty || Windows::_forceRedraw) {
+			ln->dirty = 0;
+
+			x = x0;
+			y = y0 + i * g_conf->_leading;
+
+			/* clear any stored hyperlink coordinates */
+			_windows->setHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
+
+			a = 0;
+			for (b = 0; b < _width; b++) {
+				if (ln->_attrs[a] == ln->_attrs[b]) {
+					link = ln->_attrs[a].hyper;
+					font = ln->_attrs[a].attrFont(styles);
+					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
+					bgcolor = ln->_attrs[a].attrBg(styles);
+					w = (b - a) * g_conf->_cellW;
+					_windows->drawRect(x, y, w, g_conf->_leading, bgcolor);
+					o = x;
+
+					for (k = a; k < b; k++) {
+						drawStringUni(o * GLI_SUBPIX,
+							y + g_conf->_baseLine, font, fgcolor,
+							&ln->_chars[k], 1, -1);
+						o += g_conf->_cellW;
+					}
+					if (link) {
+						_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
+							g_conf->_linkStyle, g_conf->_linkColor);
+						_windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading);
+					}
+					x += w;
+					a = b;
+				}
+			}
+			link = ln->_attrs[a].hyper;
+			font = ln->_attrs[a].attrFont(styles);
+			fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
+			bgcolor = ln->_attrs[a].attrBg(styles);
+			w = (b - a) * g_conf->_cellW;
+			w += _bbox.right - (x + w);
+			_windows->drawRect(x, y, w, g_conf->_leading, bgcolor);
+
+			o = x;
+			for (k = a; k < b; k++) {
+				drawStringUni(o * GLI_SUBPIX,
+					y + g_conf->_baseLine, font, fgcolor,
+					&ln->_chars[k], 1, -1);
+				o += g_conf->_cellW;
+			}
+			if (link) {
+				_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
+					g_conf->_linkStyle, g_conf->_linkColor);
+				_windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading);
+			}
+		}
+	}
+}
+
+/*--------------------------------------------------------------------------*/
+
+void TextGridWindow::TextGridRow::resize(size_t newSize) {
+	_chars.clear();
+	_attrs.clear();
+	_chars.resize(newSize);
+	_attrs.resize(newSize);
+	Common::fill(&_chars[0], &_chars[0] + newSize, ' ');
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
new file mode 100644
index 0000000..2c72dde
--- /dev/null
+++ b/engines/gargoyle/window_text_grid.h
@@ -0,0 +1,180 @@
+/* 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 GARGOYLE_WINDOW_TEXT_GRID_H
+#define GARGOYLE_WINDOW_TEXT_GRID_H
+
+#include "gargoyle/windows.h"
+
+namespace Gargoyle {
+
+/**
+ * Text Grid window
+ */
+class TextGridWindow : public Window {
+	/**
+	 * Structure for a row within the grid window
+	 */
+	struct TextGridRow {
+		Common::Array<uint32> _chars;
+		Common::Array<Attributes> _attrs;
+		bool dirty;
+
+		/**
+		 * Constructor
+		 */
+		TextGridRow() : dirty(false) {}
+
+		/**
+		 * Resize the row
+		 */
+		void resize(size_t newSize);
+	};
+	typedef Common::Array<TextGridRow> TextGridRows;
+private:
+	/**
+	 * Mark a given text row as modified
+	 */
+	void touch(int line);
+
+	void acceptReadChar(glui32 arg);
+
+	/**
+	 * Return or enter, during line input. Ends line input. 
+	 */
+	void acceptLine(glui32 keycode);
+
+	/**
+	 * Any regular key, during line input.
+	 */
+	void acceptReadLine(glui32 arg);
+public:
+	int _width, _height;
+	TextGridRows _lines;
+
+	int _curX, _curY;    ///< the window cursor position
+
+						 ///< for line input
+	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
+	int _inOrgX, _inOrgY;
+	int _inMax;
+	int _inCurs, _inLen;
+	Attributes _origAttr;
+	gidispatch_rock_t _inArrayRock;
+	glui32 *_lineTerminators;
+
+	WindowStyle styles[style_NUMSTYLES]; ///< style hints and settings
+public:
+	/**
+	 * Constructor
+	 */
+	TextGridWindow(Windows *windows, uint32 rock);
+
+	/**
+	 * Destructor
+	 */
+	virtual ~TextGridWindow();
+
+	/**
+	 * Rearranges the window
+	 */
+	virtual void rearrange(const Common::Rect &box) override;
+
+	/**
+	 * Get window split size within parent pair window
+	 */
+	virtual glui32 getSplit(glui32 size, bool vertical) const override;
+
+	/**
+	 * Write a character
+	 */
+	virtual void putChar(unsigned char ch) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putCharUni(uint32 ch) override;
+
+	/**
+	 * Unput a unicode character
+	 */
+	virtual bool unputCharUni(uint32 ch) override;
+
+	/**
+	 * Write a buffer
+	 */
+	virtual void putBuffer(const unsigned char *buf, size_t len) override;
+
+	/**
+	 * Write a unicode character
+	 */
+	virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+	/**
+	 * Move the cursor
+	 */
+	virtual void moveCursor(const Common::Point &newPos) override;
+
+	/**
+	 * Clear the window
+	 */
+	virtual void clear() override;
+
+	/**
+	 * Click the window
+	 */
+	virtual void click(const Common::Point &newPos) override;
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
+
+	/**
+	 * Prepare for inputing a line
+	 */
+	virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) override;
+
+	/**
+	 * Cancel an input line event
+	 */
+	virtual void cancelLineEvent(Event *ev) override;
+
+	/**
+	 * Cancel a mouse event
+	 */
+	virtual void cancelMouseEvent() override { _mouseRequest = false; }
+
+	/**
+	 * Cancel a hyperlink event
+	 */
+	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index a625040..cf9b7de 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -21,6 +21,10 @@
  */
 
 #include "gargoyle/windows.h"
+#include "gargoyle/window_graphics.h"
+#include "gargoyle/window_pair.h"
+#include "gargoyle/window_text_buffer.h"
+#include "gargoyle/window_text_grid.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/streams.h"
@@ -29,9 +33,6 @@
 
 namespace Gargoyle {
 
-#define MAGIC_WINDOW_NUM (9876)
-#define GLI_SUBPIX 8
-
 bool Windows::_overrideReverse;
 bool Windows::_overrideFgSet;
 bool Windows::_overrideBgSet;
@@ -47,41 +48,6 @@ byte Windows::_zcolor_Bright[3];
 
 /*--------------------------------------------------------------------------*/
 
-Windows::iterator &Windows::iterator::operator++() {
-	if (!_current)
-		return *this;
-
-	PairWindow *pairWin = dynamic_cast<PairWindow *>(_current);
-
-	if (pairWin) {
-		_current = !pairWin->_backward ? pairWin->_child1 : pairWin->_child2;
-	} else {
-		while (_current->_parent) {
-			pairWin = dynamic_cast<PairWindow *>(_current->_parent);
-
-			if (!pairWin->_backward) {
-				if (_current == pairWin->_child1) {
-					_current = pairWin->_child2;
-					return *this;
-				}
-			} else {
-				if (_current == pairWin->_child2) {
-					_current = pairWin->_child1;
-					return *this;
-				}
-			}
-
-			_current = pairWin;
-		}
-
-		_current = nullptr;
-	}
-
-	return *this;
-}
-
-/*--------------------------------------------------------------------------*/
-
 Windows::Windows(Graphics::Screen *screen) :
 		_screen(screen), _moreFocus(false), _windowList(nullptr),
 		_rootWin(nullptr), _focusWin(nullptr), _mask(nullptr), _claimSelect(0) {
@@ -286,9 +252,44 @@ byte *Windows::rgbShift(byte *rgb) {
 
 /*--------------------------------------------------------------------------*/
 
-Window::Window(Windows *windows, glui32 rock) : _magicnum(MAGIC_WINDOW_NUM),
-		_windows(windows), _rock(rock), _type(0), _parent(nullptr), _next(nullptr), _prev(nullptr),
-		_yAdj(0), _lineRequest(0), _lineRequestUni(0), _charRequest(0), _charRequestUni(0),
+Windows::iterator &Windows::iterator::operator++() {
+	if (!_current)
+		return *this;
+
+	PairWindow *pairWin = dynamic_cast<PairWindow *>(_current);
+
+	if (pairWin) {
+		_current = !pairWin->_backward ? pairWin->_child1 : pairWin->_child2;
+	} else {
+		while (_current->_parent) {
+			pairWin = dynamic_cast<PairWindow *>(_current->_parent);
+
+			if (!pairWin->_backward) {
+				if (_current == pairWin->_child1) {
+					_current = pairWin->_child2;
+					return *this;
+				}
+			} else {
+				if (_current == pairWin->_child2) {
+					_current = pairWin->_child1;
+					return *this;
+				}
+			}
+
+			_current = pairWin;
+		}
+
+		_current = nullptr;
+	}
+
+	return *this;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Window::Window(Windows *windows, glui32 rock) : _windows(windows), _rock(rock),
+		_type(0), _parent(nullptr), _next(nullptr), _prev(nullptr), _yAdj(0),
+		_lineRequest(0), _lineRequestUni(0), _charRequest(0), _charRequestUni(0),
 		_mouseRequest(0), _hyperRequest(0), _moreRequest(0), _scrollRequest(0), _imageLoaded(0),
 		_echoLineInput(true), _lineTerminatorsBase(nullptr), _termCt(0), _echoStream(nullptr) {
 	_attr.fgset = 0;
@@ -369,1382 +370,6 @@ BlankWindow::BlankWindow(Windows *windows, uint32 rock) : Window(windows, rock)
 
 /*--------------------------------------------------------------------------*/
 
-TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
-	_type = wintype_TextGrid;
-	_width = _height = 0;
-	_curX = _curY = 0;
-	_inBuf = nullptr;
-	_inOrgX = _inOrgY = 0;
-	_inMax = 0;
-	_inCurs = _inLen = 0;
-	_inArrayRock.num = 0;
-	_lineTerminators = nullptr;
-
-	Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], styles);
-}
-
-TextGridWindow::~TextGridWindow() {
-	if (_inBuf) {
-		if (g_vm->gli_unregister_arr)
-			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
-		_inBuf = nullptr;
-	}
-
-	delete[] _lineTerminators;
-}
-
-void TextGridWindow::rearrange(const Common::Rect &box) {
-	Window::rearrange(box);
-	int newwid, newhgt;
-
-	newwid = box.width() / g_conf->_cellW;
-	newhgt = box.height() / g_conf->_cellH;
-
-	if (newwid == _width && newhgt == _height)
-		return;
-
-	_lines.resize(newhgt);
-	for (int y = 0; y < newhgt; ++y) {
-		_lines[y].resize(newwid);
-		touch(y);
-	}
-
-	_attr.clear();
-	_width = newwid;
-	_height = newhgt;
-}
-
-void TextGridWindow::touch(int line) {
-	int y = _bbox.top + line * g_conf->_leading;
-	_lines[line].dirty = true;
-	_windows->repaint(Common::Rect(_bbox.left, y, _bbox.right, y + g_conf->_leading));
-}
-
-glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
-	return vertical ? size * g_conf->_cellW + g_conf->_tMarginX * 2 :
-		size * g_conf->_cellH + g_conf->_tMarginY * 2;
-}
-
-void TextGridWindow::putChar(unsigned char ch) {
-
-}
-
-void TextGridWindow::putCharUni(uint32 ch) {
-	TextGridRow *ln;
-
-	// Canonicalize the cursor position. That is, the cursor may have been
-	// left outside the window area; wrap it if necessary.
-	if (_curX < 0) {
-		_curX = 0;
-	} else if (_curX >= _width) {
-		_curX = 0;
-		_curY++;
-	}
-	if (_curY < 0)
-		_curY = 0;
-	else if (_curY >= _height)
-		return; /* outside the window */
-
-	if (ch == '\n') {
-		/* a newline just moves the cursor. */
-		_curY++;
-		_curX = 0;
-		return;
-	}
-
-	touch(_curY);
-
-	ln = &(_lines[_curY]);
-	ln->_chars[_curX] = ch;
-	ln->_attrs[_curX] = _attr;
-
-	_curX++;
-	// We can leave the cursor outside the window, since it will be
-	// canonicalized next time a character is printed.
-}
-
-bool TextGridWindow::unputCharUni(uint32 ch) {
-	TextGridRow *ln;
-	int oldx = _curX, oldy = _curY;
-
-	/* Move the cursor back. */
-	if (_curX >= _width)
-		_curX = _width - 1;
-	else
-		_curX--;
-
-	/* Canonicalize the cursor position. That is, the cursor may have been
-	left outside the window area; wrap it if necessary. */
-	if (_curX < 0) {
-		_curX = _width - 1;
-		_curY--;
-	}
-	if (_curY < 0)
-		_curY = 0;
-	else if (_curY >= _height)
-		return false; // outside the window
-
-	if (ch == '\n') {
-		// a newline just moves the cursor.
-		if (_curX == _width - 1)
-			return 1; // deleted a newline
-		_curX = oldx;
-		_curY = oldy;
-		return 0;    // it wasn't there */
-	}
-
-	ln = &(_lines[_curY]);
-	if (ln->_chars[_curX] == ch) {
-		ln->_chars[_curX] = ' ';
-		ln->_attrs[_curX].clear();
-		touch(_curY);
-		return true; // deleted the char
-	} else {
-		_curX = oldx;
-		_curY = oldy;
-		return false; // it wasn't there
-	}
-}
-
-void TextGridWindow::putBuffer(const unsigned char *buf, size_t len) {
-	// TODO
-}
-
-void TextGridWindow::putBufferUni(const uint32 *buf, size_t len) {
-	// TODO
-}
-
-void TextGridWindow::moveCursor(const Common::Point &pos) {
-	// If the values are negative, they're really huge positive numbers --
-	// remember that they were cast from glui32. So set them huge and
-	// let canonicalization take its course.
-	_curX = (pos.x < 0) ? 32767 : pos.x;
-	_curY = (pos.y < 0) ? 32767 : pos.y;
-}
-
-void TextGridWindow::clear() {
-	_attr.fgset = Windows::_overrideFgSet;
-	_attr.bgset = Windows::_overrideBgSet;
-	_attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
-	_attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
-	_attr.reverse = false;
-
-	for (int k = 0; k < _height; k++) {
-		TextGridRow &ln = _lines[k];
-		touch(k);
-		for (uint j = 0; j < ln._attrs.size(); ++j) {
-			ln._chars[j] = ' ';
-			ln._attrs[j].clear();
-		}
-	}
-
-	_curX = 0;
-	_curY = 0;
-}
-
-void TextGridWindow::click(const Common::Point &newPos) {
-	int x = newPos.x - _bbox.left;
-	int y = newPos.y - _bbox.top;
-
-	if (_lineRequest || _charRequest || _lineRequestUni || _charRequestUni
-			|| _moreRequest || _scrollRequest)
-		_windows->setFocus(this);
-
-	if (_mouseRequest) {
-		g_vm->_events->eventStore(evtype_MouseInput, this, x / g_conf->_cellW, y / g_conf->_leading);
-		_mouseRequest = false;
-		if (g_conf->_safeClicks)
-			g_vm->_events->_forceClick = true;
-	}
-
-	if (_hyperRequest) {
-		glui32 linkval = _windows->getHyperlink(newPos);
-		if (linkval)
-		{
-			g_vm->_events->eventStore(evtype_Hyperlink, this, linkval, 0);
-			_hyperRequest = false;
-			if (g_conf->_safeClicks)
-				g_vm->_events->_forceClick = true;
-		}
-	}
-}
-
-void TextGridWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
-	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
-	{
-		warning("request_line_event: window already has keyboard request");
-		return;
-	}
-
-	if ((int)maxlen > (_width - _curX))
-		maxlen = (_width - _curX);
-
-	_inBuf = buf;
-	_inMax = maxlen;
-	_inLen = 0;
-	_inCurs = 0;
-	_inOrgX = _curX;
-	_inOrgY = _curY;
-	_origAttr = _attr;
-	_attr.set(style_Input);
-
-	if (initlen > maxlen)
-		initlen = maxlen;
-
-	if (initlen) {
-		TextGridRow *ln = &_lines[_inOrgY];
-
-		for (glui32 ix = 0; ix < initlen; ix++) {
-			ln->_attrs[_inOrgX + ix].set(style_Input);
-			ln->_chars[_inOrgX + ix] = buf[ix];
-		}
-
-		_inCurs += initlen;
-		_inLen += initlen;
-		_curX = _inOrgX + _inCurs;
-		_curY = _inOrgY;
-
-		touch(_inOrgY);
-	}
-
-	if (_lineTerminatorsBase && _termCt) {
-		_lineTerminators = new glui32[_termCt + 1];
-
-		if (_lineTerminators) {
-			memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
-			_lineTerminators[_termCt] = 0;
-		}
-	}
-
-	if (g_vm->gli_register_arr)
-		_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Cn");
-}
-
-void TextGridWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
-	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
-		warning("requestLineEventUni: window already has keyboard request");
-		return;
-	}
-
-	if ((int)maxlen > (_width - _curX))
-		maxlen = (_width - _curX);
-
-	_inBuf = buf;
-	_inMax = maxlen;
-	_inLen = 0;
-	_inCurs = 0;
-	_inOrgX = _curX;
-	_inOrgY = _curY;
-	_origAttr = _attr;
-	_attr.set(style_Input);
-
-	if (initlen > maxlen)
-		initlen = maxlen;
-
-	if (initlen) {
-		TextGridRow *ln = &(_lines[_inOrgY]);
-
-		for (glui32 ix = 0; ix<initlen; ix++) {
-			ln->_attrs[_inOrgX + ix].set(style_Input);
-			ln->_chars[_inOrgX + ix] = buf[ix];
-		}
-
-		_inCurs += initlen;
-		_inLen += initlen;
-		_curX = _inOrgX + _inCurs;
-		_curY = _inOrgY;
-
-		touch(_inOrgY);
-	}
-	
-	if (_lineTerminatorsBase && _termCt) {
-		_lineTerminators = new glui32[_termCt + 1];
-
-		if (_lineTerminators) {
-			memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
-			_lineTerminators[_termCt] = 0;
-		}
-	}
-
-	if (g_vm->gli_register_arr)
-		_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Iu");
-}
-
-void TextGridWindow::cancelLineEvent(Event *ev) {
-	int ix;
-	void *inbuf;
-	int inmax;
-	int unicode = _lineRequestUni;
-	gidispatch_rock_t inarrayrock;
-	TextGridRow *ln = &_lines[_inOrgY];
-	Event dummyEv;
-
-	if (!ev)
-		ev = &dummyEv;
-
-	g_vm->_events->clearEvent(ev);
-
-	if (!_lineRequest && !_lineRequestUni)
-		return;
-
-
-	inbuf = _inBuf;
-	inmax = _inMax;
-	inarrayrock = _inArrayRock;
-
-	if (!unicode) {
-		for (ix = 0; ix<_inLen; ix++)
-		{
-			glui32 ch = ln->_chars[_inOrgX + ix];
-			if (ch > 0xff)
-				ch = '?';
-			((char *)inbuf)[ix] = (char)ch;
-		}
-		if (_echoStream)
-			_echoStream->echoLine((char *)_inBuf, _inLen);
-	} else {
-		for (ix = 0; ix<_inLen; ix++)
-			((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
-		if (_echoStream)
-			_echoStream->echoLineUni((glui32 *)inbuf, _inLen);
-	}
-
-	_curY = _inOrgY + 1;
-	_curX = 0;
-	_attr = _origAttr;
-
-	ev->_type = evtype_LineInput;
-	ev->_window = this;
-	ev->_val1 = _inLen;
-	ev->_val2 = 0;
-
-	_lineRequest = false;
-	_lineRequestUni = false;
-
-	if (_lineTerminators) {
-		free(_lineTerminators);
-		_lineTerminators = nullptr;
-	}
-
-	_inBuf = nullptr;
-	_inMax = 0;
-	_inOrgX = 0;
-	_inOrgY = 0;
-
-	if (g_vm->gli_unregister_arr)
-		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
-}
-
-void TextGridWindow::acceptReadChar(glui32 arg) {
-	glui32 key;
-
-	switch (arg)
-	{
-	case keycode_Erase:
-		key = keycode_Delete;
-		break;
-	case keycode_MouseWheelUp:
-	case keycode_MouseWheelDown:
-		return;
-	default:
-		key = arg;
-	}
-
-	if (key > 0xff && key < (0xffffffff - keycode_MAXVAL + 1))
-	{
-		if (!(_charRequestUni) || key > 0x10ffff)
-			key = keycode_Unknown;
-	}
-
-	_charRequest = false;
-	_charRequestUni = false;
-	g_vm->_events->eventStore(evtype_CharInput, this, key, 0);
-}
-
-void TextGridWindow::acceptLine(glui32 keycode) {
-	int ix;
-	void *inbuf;
-	int inmax;
-	gidispatch_rock_t inarrayrock;
-	TextGridRow *ln = &(_lines[_inOrgY]);
-	int unicode = _lineRequestUni;
-
-	if (!_inBuf)
-		return;
-
-	inbuf = _inBuf;
-	inmax = _inMax;
-	inarrayrock = _inArrayRock;
-
-	if (!unicode) {
-		for (ix = 0; ix<_inLen; ix++)
-			((char *)inbuf)[ix] = (char)ln->_chars[_inOrgX + ix];
-		if (_echoStream)
-			_echoStream->echoLine((char *)inbuf, _inLen);
-	} else {
-		for (ix = 0; ix<_inLen; ix++)
-			((glui32 *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
-		if (_echoStream)
-			_echoStream->echoLineUni((glui32 *)inbuf, _inLen);
-	}
-
-	_curY = _inOrgY + 1;
-	_curX = 0;
-	_attr = _origAttr;
-
-	if (_lineTerminators)
-	{
-		glui32 val2 = keycode;
-		if (val2 == keycode_Return)
-			val2 = 0;
-		g_vm->_events->eventStore(evtype_LineInput, this, _inLen, val2);
-		free(_lineTerminators);
-		_lineTerminators = NULL;
-	} else {
-		g_vm->_events->eventStore(evtype_LineInput, this, _inLen, 0);
-	}
-	_lineRequest = false;
-	_lineRequestUni = false;
-	_inBuf = NULL;
-	_inMax = 0;
-	_inOrgX = 0;
-	_inOrgY = 0;
-
-	if (g_vm->gli_unregister_arr)
-		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
-}
-
-void TextGridWindow::acceptReadLine(glui32 arg) {
-	int ix;
-	TextGridRow *ln = &(_lines[_inOrgY]);
-
-	if (!_inBuf)
-		return;
-
-	if (_lineTerminators && checkTerminator(arg)) {
-		glui32 *cx;
-		for (cx = _lineTerminators; *cx; cx++) {
-			if (*cx == arg) {
-				acceptLine(arg);
-				return;
-			}
-		}
-	}
-
-	switch (arg) {
-
-		/* Delete keys, during line input. */
-
-	case keycode_Delete:
-		if (_inLen <= 0)
-			return;
-		if (_inCurs <= 0)
-			return;
-		for (ix = _inCurs; ix<_inLen; ix++)
-			ln->_chars[_inOrgX + ix - 1] = ln->_chars[_inOrgX + ix];
-		ln->_chars[_inOrgX + _inLen - 1] = ' ';
-		_inCurs--;
-		_inLen--;
-		break;
-
-	case keycode_Erase:
-		if (_inLen <= 0)
-			return;
-		if (_inCurs >= _inLen)
-			return;
-		for (ix = _inCurs; ix<_inLen - 1; ix++)
-			ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix + 1];
-		ln->_chars[_inOrgX + _inLen - 1] = ' ';
-		_inLen--;
-		break;
-
-	case keycode_Escape:
-		if (_inLen <= 0)
-			return;
-		for (ix = 0; ix<_inLen; ix++)
-			ln->_chars[_inOrgX + ix] = ' ';
-		_inLen = 0;
-		_inCurs = 0;
-		break;
-
-		/* Cursor movement keys, during line input. */
-
-	case keycode_Left:
-		if (_inCurs <= 0)
-			return;
-		_inCurs--;
-		break;
-
-	case keycode_Right:
-		if (_inCurs >= _inLen)
-			return;
-		_inCurs++;
-		break;
-
-	case keycode_Home:
-		if (_inCurs <= 0)
-			return;
-		_inCurs = 0;
-		break;
-
-	case keycode_End:
-		if (_inCurs >= _inLen)
-			return;
-		_inCurs = _inLen;
-		break;
-
-	case keycode_Return:
-		acceptLine(arg);
-		break;
-
-	default:
-		if (_inLen >= _inMax)
-			return;
-
-		if (arg < 32 || arg > 0xff)
-			return;
-
-		if (g_conf->_caps && (arg > 0x60 && arg < 0x7b))
-			arg -= 0x20;
-
-		for (ix = _inLen; ix>_inCurs; ix--)
-			ln->_chars[_inOrgX + ix] = ln->_chars[_inOrgX + ix - 1];
-		ln->_attrs[_inOrgX + _inLen].set(style_Input);
-		ln->_chars[_inOrgX + _inCurs] = arg;
-
-		_inCurs++;
-		_inLen++;
-	}
-
-	_curX = _inOrgX + _inCurs;
-	_curY = _inOrgY;
-
-	touch(_inOrgY);
-}
-
-void TextGridWindow::redraw() {
-	TextGridRow *ln;
-	int x0, y0;
-	int x, y, w;
-	int i, a, b, k, o;
-	glui32 link;
-	int font;
-	byte *fgcolor, *bgcolor;
-
-	x0 = _bbox.left;
-	y0 = _bbox.top;
-
-	for (i = 0; i < _height; i++) {
-		ln = &_lines[i];
-		if (ln->dirty || Windows::_forceRedraw) {
-			ln->dirty = 0;
-
-			x = x0;
-			y = y0 + i * g_conf->_leading;
-
-			/* clear any stored hyperlink coordinates */
-			_windows->setHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
-
-			a = 0;
-			for (b = 0; b < _width; b++) {
-				if (ln->_attrs[a] == ln->_attrs[b]) {
-					link = ln->_attrs[a].hyper;
-					font = ln->_attrs[a].attrFont(styles);
-					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
-					bgcolor = ln->_attrs[a].attrBg(styles);
-					w = (b - a) * g_conf->_cellW;
-					_windows->drawRect(x, y, w, g_conf->_leading, bgcolor);
-					o = x;
-
-					for (k = a; k < b; k++) {
-						drawStringUni(o * GLI_SUBPIX,
-							y + g_conf->_baseLine, font, fgcolor,
-							&ln->_chars[k], 1, -1);
-						o += g_conf->_cellW;
-					}
-					if (link) {
-						_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
-							g_conf->_linkStyle, g_conf->_linkColor);
-						_windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading);
-					}
-					x += w;
-					a = b;
-				}
-			}
-			link = ln->_attrs[a].hyper;
-			font = ln->_attrs[a].attrFont(styles);
-			fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
-			bgcolor = ln->_attrs[a].attrBg(styles);
-			w = (b - a) * g_conf->_cellW;
-			w += _bbox.right - (x + w);
-			_windows->drawRect(x, y, w, g_conf->_leading, bgcolor);
-
-			o = x;
-			for (k = a; k < b; k++) {
-				drawStringUni(o * GLI_SUBPIX,
-					y + g_conf->_baseLine, font, fgcolor,
-					&ln->_chars[k], 1, -1);
-				o += g_conf->_cellW;
-			}
-			if (link) {
-				_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
-					g_conf->_linkStyle, g_conf->_linkColor);
-				_windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading);
-			}
-		}
-	}
-}
-
-/*--------------------------------------------------------------------------*/
-
-void TextGridWindow::TextGridRow::resize(size_t newSize) {
-	_chars.clear();
-	_attrs.clear();
-	_chars.resize(newSize);
-	_attrs.resize(newSize);
-	Common::fill(&_chars[0], &_chars[0] + newSize, ' ');
-}
-
-/*--------------------------------------------------------------------------*/
-
-TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windows, rock),
-		_historyPos(0), _historyFirst(0), _historyPresent(0), _lastSeen(0), _scrollPos(0),
-		_scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1), _inBuf(nullptr),
-		_lineTerminators(nullptr), _echoLineInput(true), _ladjw(0), _radjw(0), _ladjn(0),
-		_radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr),
-		_spaced(0), _dashed(0), _copyBuf(0), _copyPos(0) {
-	_type = wintype_TextBuffer;
-	Common::fill(&_history[0], &_history[HISTORYLEN], nullptr);
-
-	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], styles);
-}
-
-TextBufferWindow::~TextBufferWindow() {
-	if (_inBuf) {
-		if (g_vm->gli_unregister_arr)
-			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
-		_inBuf = nullptr;
-	}
-
-	delete[] _copyBuf;
-	delete[] _lineTerminators;
-
-	for (int i = 0; i < _scrollBack; i++) {
-		if (_lines[i].lpic)
-			_lines[i].lpic->decrement();
-		if (_lines[i].rpic)
-			_lines[i].rpic->decrement();
-	}
-}
-
-void TextBufferWindow::rearrange(const Common::Rect &box) {
-	Window::rearrange(box);
-	int newwid, newhgt;
-	int rnd;
-
-	newwid = (box.width() - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) / g_conf->_cellW;
-	newhgt = (box.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
-
-	/* align text with bottom */
-	rnd = newhgt * g_conf->_cellH + g_conf->_tMarginY * 2;
-	_yAdj = (box.height() - rnd);
-	_bbox.top += (box.height() - rnd);
-
-	if (newwid != _width) {
-		_width = newwid;
-		reflow();
-	}
-
-	if (newhgt != _height) {
-		/* scroll up if we obscure new lines */
-		if (_lastSeen >= newhgt - 1)
-			_scrollPos += (_height - newhgt);
-
-		_height = newhgt;
-
-		/* keep window within 'valid' lines */
-		if (_scrollPos > _scrollMax - _height + 1)
-			_scrollPos = _scrollMax - _height + 1;
-		if (_scrollPos < 0)
-			_scrollPos = 0;
-		touchScroll();
-
-		/* allocate copy buffer */
-		if (_copyBuf)
-			delete[] _copyBuf;
-		_copyBuf = new glui32[_height * TBLINELEN];
-
-		for (int i = 0; i < (_height * TBLINELEN); i++)
-			_copyBuf[i] = 0;
-
-		_copyPos = 0;
-	}
-}
-
-void TextBufferWindow::reflow() {
-	int inputbyte = -1;
-	Attributes curattr, oldattr;
-	int i, k, p, s;
-	int x;
-
-	if (_height < 4 || _width < 20)
-		return;
-
-	_lines[0].len = _numChars;
-
-	/* allocate temp buffers */
-	Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN];
-	glui32 *charbuf = new glui32[SCROLLBACK * TBLINELEN];
-	int *alignbuf = new int[SCROLLBACK];
-	Picture **pictbuf = new Picture *[SCROLLBACK];
-	glui32 *hyperbuf = new glui32[SCROLLBACK];
-	int *offsetbuf = new int[SCROLLBACK];
-
-	if (!attrbuf || !charbuf || !alignbuf || !pictbuf || !hyperbuf || !offsetbuf) {
-		delete[] attrbuf;
-		delete[] charbuf;
-		delete[] alignbuf;
-		delete[] pictbuf;
-		delete[] hyperbuf;
-		delete[] offsetbuf;
-		return;
-	}
-
-	/* copy text to temp buffers */
-
-	oldattr = _attr;
-	curattr.clear();
-
-	x = 0;
-	p = 0;
-	s = _scrollMax < SCROLLBACK ? _scrollMax : SCROLLBACK - 1;
-
-	for (k = s; k >= 0; k--) {
-		if (k == 0 && _lineRequest)
-			inputbyte = p + _inFence;
-
-		if (_lines[k].lpic) {
-			offsetbuf[x] = p;
-			alignbuf[x] = imagealign_MarginLeft;
-			pictbuf[x] = _lines[k].lpic;
-
-			if (pictbuf[x]) pictbuf[x]->increment();
-			hyperbuf[x] = _lines[k].lhyper;
-			x++;
-		}
-
-		if (_lines[k].rpic) {
-			offsetbuf[x] = p;
-			alignbuf[x] = imagealign_MarginRight;
-			pictbuf[x] = _lines[k].rpic;
-			if (pictbuf[x]) pictbuf[x]->increment();
-			hyperbuf[x] = _lines[k].rhyper;
-			x++;
-		}
-
-		for (i = 0; i < _lines[k].len; i++) {
-			attrbuf[p] = curattr = _lines[k].attr[i];
-			charbuf[p] = _lines[k].chars[i];
-			p++;
-		}
-
-		if (_lines[k].newline) {
-			attrbuf[p] = curattr;
-			charbuf[p] = '\n';
-			p++;
-		}
-	}
-
-	offsetbuf[x] = -1;
-
-	/* clear window */
-
-	clear();
-
-	/* and dump text back */
-
-	x = 0;
-	for (i = 0; i < p; i++) {
-		if (i == inputbyte)
-			break;
-		_attr = attrbuf[i];
-
-		if (offsetbuf[x] == i) {
-			putPicture(pictbuf[x], alignbuf[x], hyperbuf[x]);
-			x++;
-		}
-
-		putCharUni(charbuf[i]);
-	}
-
-	/* terribly sorry about this... */
-	_lastSeen = 0;
-	_scrollPos = 0;
-
-	if (inputbyte != -1) {
-		_inFence = _numChars;
-		putTextUni(charbuf + inputbyte, p - inputbyte, _numChars, 0);
-		_inCurs = _numChars;
-	}
-
-	// free temp buffers
-	delete[] attrbuf;
-	delete[] charbuf;
-	delete[] alignbuf;
-	delete[] pictbuf;
-	delete[] hyperbuf;
-	delete[] offsetbuf;
-
-	_attr = oldattr;
-
-	touchScroll();
-}
-
-void TextBufferWindow::touchScroll() {
-	_windows->clearSelection();
-	_windows->repaint(_bbox);
-
-	for (int i = 0; i < _scrollMax; i++)
-		_lines[i].dirty = true;
-}
-
-bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
-	if (align == imagealign_MarginRight)
-	{
-		if (_lines[0].rpic || _numChars)
-			return false;
-
-		_radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
-		_radjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
-		_lines[0].rpic = pic;
-		_lines[0].rm = _radjw;
-		_lines[0].rhyper = linkval;
-	} else {
-		if (align != imagealign_MarginLeft && _numChars)
-			putCharUni('\n');
-
-		if (_lines[0].lpic || _numChars)
-			return false;
-
-		_ladjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
-		_ladjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
-		_lines[0].lpic = pic;
-		_lines[0].lm = _ladjw;
-		_lines[0].lhyper = linkval;
-
-		if (align != imagealign_MarginLeft)
-			flowBreak();
-	}
-
-	return true;
-}
-
-void TextBufferWindow::flowBreak() {
-	// TODO
-}
-
-void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldlen) {
-	// TODO
-}
-
-void TextBufferWindow::touch(int line) {
-	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
-	_lines[line].dirty = 1;
-	_windows->clearSelection();
-	_windows->repaint(Common::Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
-}
-
-glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
-	return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
-}
-
-void TextBufferWindow::putChar(unsigned char ch) {
-}
-
-void TextBufferWindow::putCharUni(glui32 ch) {
-	/*
-	glui32 bchars[TBLINELEN];
-	Attributes battrs[TBLINELEN];
-	int pw;
-	int bpoint;
-	int saved;
-	int i;
-	int linelen;
-	unsigned char *color;
-
-	gli_tts_speak(&ch, 1);
-
-	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - gli_scroll_width) * GLI_SUBPIX;
-	pw = pw - 2 * SLOP - radjw - ladjw;
-
-	color = Windows::_overrideBgSet ? gli_window_color : bgcolor;
-
-	// oops ... overflow
-	if (numchars + 1 >= TBLINELEN)
-		scrolloneline(dwin, 0);
-
-	if (ch == '\n') {
-		scrolloneline(dwin, 1);
-		return;
-	}
-
-	if (gli_conf_quotes) {
-		// fails for 'tis a wonderful day in the '80s
-		if (gli_conf_quotes > 1 && ch == '\'')
-		{
-			if (numchars == 0 || leftquote(_chars[numchars - 1]))
-				ch = UNI_LSQUO;
-		}
-
-		if (ch == '`')
-			ch = UNI_LSQUO;
-
-		if (ch == '\'')
-			ch = UNI_RSQUO;
-
-		if (ch == '"')
-		{
-			if (numchars == 0 || leftquote(_chars[numchars - 1]))
-				ch = UNI_LDQUO;
-			else
-				ch = UNI_RDQUO;
-		}
-	}
-
-	if (gli_conf_dashes && attr.style != style_Preformatted)
-	{
-		if (ch == '-')
-		{
-			dashed++;
-			if (dashed == 2)
-			{
-				numchars--;
-				if (gli_conf_dashes == 2)
-					ch = UNI_NDASH;
-				else
-					ch = UNI_MDASH;
-			}
-			if (dashed == 3)
-			{
-				numchars--;
-				ch = UNI_MDASH;
-				dashed = 0;
-			}
-		}
-		else
-			dashed = 0;
-	}
-
-	if (gli_conf_spaces && attr.style != style_Preformatted
-		&& styles[attr.style].bg == color
-		&& !styles[attr.style].reverse)
-	{
-		// turn (period space space) into (period space)
-		if (gli_conf_spaces == 1)
-		{
-			if (ch == '.')
-				spaced = 1;
-			else if (ch == ' ' && spaced == 1)
-				spaced = 2;
-			else if (ch == ' ' && spaced == 2)
-			{
-				spaced = 0;
-				return;
-			}
-			else
-				spaced = 0;
-		}
-
-		// Turn (per sp x) into (per sp sp x)
-		if (gli_conf_spaces == 2)
-		{
-			if (ch == '.')
-				spaced = 1;
-			else if (ch == ' ' && spaced == 1)
-				spaced = 2;
-			else if (ch != ' ' && spaced == 2)
-			{
-				spaced = 0;
-				win_textbuffer_putchar_uni(win, ' ');
-			}
-			else
-				spaced = 0;
-		}
-	}
-
-	_chars[numchars] = ch;
-	attrs[numchars] = attr;
-	numchars++;
-
-	// kill spaces at the end for line width calculation
-	linelen = numchars;
-	while (linelen > 1 && _chars[linelen - 1] == ' '
-		&& styles[attrs[linelen - 1].style].bg == color
-		&& !styles[attrs[linelen - 1].style].reverse)
-		linelen--;
-
-	if (calcwidth(dwin, _chars, attrs, 0, linelen, -1) >= pw)
-	{
-		bpoint = numchars;
-
-		for (i = numchars - 1; i > 0; i--)
-			if (_chars[i] == ' ')
-			{
-				bpoint = i + 1; // skip space
-				break;
-			}
-
-		saved = numchars - bpoint;
-
-		memcpy(bchars, _chars + bpoint, saved * 4);
-		memcpy(battrs, attrs + bpoint, saved * sizeof(attr_t));
-		numchars = bpoint;
-
-		scrolloneline(dwin, 0);
-
-		memcpy(_chars, bchars, saved * 4);
-		memcpy(attrs, battrs, saved * sizeof(attr_t));
-		numchars = saved;
-	}
-
-	touch(0);
-	*/
-}
-
-bool TextBufferWindow::unputCharUni(uint32 ch) {
-	// TODO
-	return false;
-}
-
-void TextBufferWindow::putBuffer(const unsigned char *buf, size_t len) {
-	// TODO
-}
-
-void TextBufferWindow::putBufferUni(const uint32 *buf, size_t len) {
-	// TODO
-}
-
-void TextBufferWindow::moveCursor(const Common::Point &newPos) {
-	// TODO
-}
-
-void TextBufferWindow::clear() {
-	int i;
-
-	_attr.fgset = Windows::_overrideFgSet;
-	_attr.bgset = Windows::_overrideBgSet;
-	_attr.fgcolor = Windows::_overrideFgSet ? Windows::_overrideFgVal : 0;
-	_attr.bgcolor = Windows::_overrideBgSet ? Windows::_overrideBgVal : 0;
-	_attr.reverse = false;
-
-	_ladjw = _radjw = 0;
-	_ladjn = _radjn = 0;
-
-	_spaced = 0;
-	_dashed = 0;
-
-	_numChars = 0;
-
-	for (i = 0; i < _scrollBack; i++) {
-		_lines[i].len = 0;
-
-		if (_lines[i].lpic) _lines[i].lpic->decrement();
-		_lines[i].lpic = nullptr;
-		if (_lines[i].rpic) _lines[i].rpic->decrement();
-		_lines[i].rpic = nullptr;
-
-		_lines[i].lhyper = 0;
-		_lines[i].rhyper = 0;
-		_lines[i].lm = 0;
-		_lines[i].rm = 0;
-		_lines[i].newline = 0;
-		_lines[i].dirty = true;
-		_lines[i].repaint = false;
-	}
-
-	_lastSeen = 0;
-	_scrollPos = 0;
-	_scrollMax = 0;
-
-	for (i = 0; i < _height; i++)
-		touch(i);
-}
-
-void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
-	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
-	{
-		warning("request_line_event: window already has keyboard request");
-		return;
-	}
-
-	// TODO
-}
-
-void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
-	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
-	{
-		warning("request_line_event_uni: window already has keyboard request");
-		return;
-	}
-
-	// TODO
-}
-
-void TextBufferWindow::cancelLineEvent(Event *ev) {
-	gidispatch_rock_t inarrayrock;
-	int ix;
-	int len;
-	void *inbuf;
-	int inmax;
-	int unicode = _lineRequestUni;
-	Event dummyEv;
-
-	if (!ev)
-		ev = &dummyEv;
-
-	g_vm->_events->clearEvent(ev);
-
-	if (!_lineRequest && !_lineRequestUni)
-		return;
-
-	if (!_inBuf)
-		return;
-
-	inbuf = _inBuf;
-	inmax = _inMax;
-	inarrayrock = _inArrayRock;
-
-	len = _numChars - _inFence;
-	if (_echoStream)
-		_echoStream->echoLineUni(_chars + _inFence, len);
-
-	if (len > inmax)
-		len = inmax;
-
-	if (!unicode) {
-		for (ix = 0; ix<len; ix++) {
-			glui32 ch = _chars[_inFence + ix];
-			if (ch > 0xff)
-				ch = '?';
-			((char *)inbuf)[ix] = (char)ch;
-		}
-	}
-	else {
-		for (ix = 0; ix<len; ix++)
-			((glui32 *)inbuf)[ix] = _chars[_inFence + ix];
-	}
-
-	_attr = _origAttr;
-
-	ev->_type = evtype_LineInput;
-	ev->_window = this;
-	ev->_val1 = len;
-	ev->_val2 = 0;
-
-	_lineRequest = false;
-	_lineRequestUni = false;
-	if (_lineTerminators) {
-		free(_lineTerminators);
-		_lineTerminators = nullptr;
-	}
-	_inBuf = nullptr;
-	_inMax = 0;
-
-	if (_echoLineInput) {
-		putCharUni('\n');
-	}
-	else {
-		_numChars = _inFence;
-		touch(0);
-	}
-
-	if (g_vm->gli_unregister_arr)
-		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
-}
-
-void TextBufferWindow::redraw() {
-	// TODO
-}
-
-/*--------------------------------------------------------------------------*/
-
-TextBufferWindow::TextBufferRow::TextBufferRow() : len(0), newline(0), dirty(false), repaint(false),
-		lpic(nullptr), rpic(nullptr), lhyper(0), rhyper(0), lm(0), rm(0) {
-}
-
-void TextBufferWindow::TextBufferRow::resize(size_t newSize) {
-	chars.clear();
-	attr.clear();
-	chars.resize(newSize);
-	attr.resize(newSize);
-	Common::fill(&chars[0], &chars[0] + newSize, ' ');
-}
-
-/*--------------------------------------------------------------------------*/
-
-GraphicsWindow::GraphicsWindow(Windows *windows, uint32 rock) : Window(windows, rock),
-_w(0), _h(0), _dirty(false), _surface(nullptr) {
-	_type = wintype_Graphics;
-	Common::copy(&_bgColor[0], &_bgColor[3], _bgnd);
-}
-
-GraphicsWindow::~GraphicsWindow() {
-	delete _surface;
-}
-
-void GraphicsWindow::rearrange(const Common::Rect &box) {
-	int newwid, newhgt;
-	int bothwid, bothhgt;
-	int oldw, oldh;
-	Graphics::ManagedSurface *newSurface;
-
-	_bbox = box;
-
-	newwid = box.width();
-	newhgt = box.height();
-	oldw = _w;
-	oldh = _h;
-
-	if (newwid <= 0 || newhgt <= 0) {
-		_w = 0;
-		_h = 0;
-		delete _surface;
-		_surface = NULL;
-		return;
-	}
-
-	bothwid = _w;
-	if (newwid < bothwid)
-		bothwid = newwid;
-	bothhgt = _h;
-	if (newhgt < bothhgt)
-		bothhgt = newhgt;
-
-	newSurface = new Graphics::ManagedSurface(newwid, newhgt,
-		Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0));
-
-	// If the new surface is equal or bigger than the old one, copy it over
-	if (_surface && bothwid && bothhgt)
-		newSurface->blitFrom(*_surface);
-
-	delete _surface;
-	_surface = newSurface;
-	_w = newwid;
-	_h = newhgt;
-
-	touch();
-}
-
-void GraphicsWindow::touch() {
-	_dirty = true;
-	_windows->repaint(_bbox);
-}
-
-void GraphicsWindow::redraw() {
-	// TODO
-}
-
-/*--------------------------------------------------------------------------*/
-
-PairWindow::PairWindow(Windows *windows, glui32 method, Window *key, glui32 size) :
-		Window(windows, 0),
-		_dir(method & winmethod_DirMask),
-		_division(method & winmethod_DivisionMask),
-		_wBorder((method & winmethod_BorderMask) == winmethod_Border),
-		_vertical(_dir == winmethod_Left || _dir == winmethod_Right),
-		_backward(_dir == winmethod_Left || _dir == winmethod_Above),
-		_key(key), _size(size), _keyDamage(0), _child1(nullptr), _child2(nullptr) {
-	_type = wintype_Pair;
-}
-
-void PairWindow::rearrange(const Common::Rect &box) {
-	Common::Rect box1, box2;
-	int min, diff, split, splitwid, max;
-	Window *ch1, *ch2;
-
-	_bbox = box;
-
-	if (_vertical) {
-		min = _bbox.left;
-		max = _bbox.right;
-	} else {
-		min = _bbox.top;
-		max = _bbox.bottom;
-	}
-	diff = max - min;
-
-	// We now figure split.
-	if (_vertical)
-		splitwid = g_conf->_wPaddingX; // want border?
-	else
-		splitwid = g_conf->_wPaddingY; // want border?
-
-	switch (_division) {
-	case winmethod_Proportional:
-		split = (diff * _size) / 100;
-		break;
-
-	case winmethod_Fixed:
-		split = !_key ? 0 : _key->getSplit(_size, _vertical);
-		break;
-
-	default:
-		split = diff / 2;
-		break;
-	}
-
-	if (!_backward)
-		split = max - split - splitwid;
-	else
-		split = min + split;
-
-	if (min >= max) {
-		split = min;
-	} else {
-		if (split < min)
-			split = min;
-		else if (split > max - splitwid)
-			split = max - splitwid;
-	}
-
-	if (_vertical) {
-		box1.left = _bbox.left;
-		box1.right = split;
-		box2.left = split + splitwid;
-		box2.right = _bbox.right;
-		box1.top = _bbox.top;
-		box1.bottom = _bbox.bottom;
-		box2.top = _bbox.top;
-		box2.bottom = _bbox.bottom;
-	} else {
-		box1.top = _bbox.top;
-		box1.bottom = split;
-		box2.top = split + splitwid;
-		box2.bottom = _bbox.bottom;
-		box1.left = _bbox.left;
-		box1.right = _bbox.right;
-		box2.left = _bbox.left;
-		box2.right = _bbox.right;
-	}
-
-	if (!_backward) {
-		ch1 = _child1;
-		ch2 = _child2;
-	} else {
-		ch1 = _child2;
-		ch2 = _child1;
-	}
-
-	ch1->rearrange(box1);
-	ch2->rearrange(box2);
-}
-
-void PairWindow::redraw() {
-	// TODO
-}
-
-/*--------------------------------------------------------------------------*/
-
 void Attributes::clear() {
 	fgset = 0;
 	bgset = 0;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 9a09641..a5bc2f4 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -31,7 +31,6 @@
 #include "gargoyle/events.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/fonts.h"
-#include "gargoyle/picture.h"
 #include "gargoyle/streams.h"
 #include "gargoyle/window_mask.h"
 
@@ -43,6 +42,8 @@ class PairWindow;
 #define HISTORYLEN 100
 #define SCROLLBACK 512
 #define TBLINELEN 300
+#define GLI_SUBPIX 8
+
 
 /**
  * Main windows manager
@@ -256,7 +257,6 @@ struct Attributes {
 class Window : public Draw {
 public:
 	Windows *_windows;
-	glui32 _magicnum;
 	glui32 _rock;
 	glui32 _type;
 
@@ -393,409 +393,6 @@ public:
 	BlankWindow(Windows *windows, uint32 rock);
 };
 
-/**
- * Text Grid window
- */
-class TextGridWindow : public Window {
-	/**
-	 * Structure for a row within the grid window
-	 */
-	struct TextGridRow {
-		Common::Array<uint32> _chars;
-		Common::Array<Attributes> _attrs;
-		bool dirty;
-
-		/**
-		 * Constructor
-		 */
-		TextGridRow() : dirty(false) {}
-
-		/**
-		 * Resize the row
-		 */
-		void resize(size_t newSize);
-	};
-	typedef Common::Array<TextGridRow> TextGridRows;
-private:
-	/**
-	 * Mark a given text row as modified
-	 */
-	void touch(int line);
-
-	void acceptReadChar(glui32 arg);
-
-	/**
-	 * Return or enter, during line input. Ends line input. 
-	 */
-	void acceptLine(glui32 keycode);
-
-	/**
-	 * Any regular key, during line input.
-	 */
-	void acceptReadLine(glui32 arg);
-public:
-	int _width, _height;
-	TextGridRows _lines;
-
-	int _curX, _curY;    ///< the window cursor position
-
-						 ///< for line input
-	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
-	int _inOrgX, _inOrgY;
-	int _inMax;
-	int _inCurs, _inLen;
-	Attributes _origAttr;
-	gidispatch_rock_t _inArrayRock;
-	glui32 *_lineTerminators;
-
-	WindowStyle styles[style_NUMSTYLES]; ///< style hints and settings
-public:
-	/**
-	 * Constructor
-	 */
-	TextGridWindow(Windows *windows, uint32 rock);
-
-	/**
-	 * Destructor
-	 */
-	virtual ~TextGridWindow();
-
-	/**
-	 * Rearranges the window
-	 */
-	virtual void rearrange(const Common::Rect &box) override;
-
-	/**
-	 * Get window split size within parent pair window
-	 */
-	virtual glui32 getSplit(glui32 size, bool vertical) const override;
-
-	/**
-	 * Write a character
-	 */
-	virtual void putChar(unsigned char ch) override;
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void putCharUni(uint32 ch) override;
-
-	/**
-	 * Unput a unicode character
-	 */
-	virtual bool unputCharUni(uint32 ch) override;
-
-	/**
-	 * Write a buffer
-	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) override;
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void putBufferUni(const uint32 *buf, size_t len) override;
-
-	/**
-	 * Move the cursor
-	 */
-	virtual void moveCursor(const Common::Point &newPos) override;
-
-	/**
-	 * Clear the window
-	 */
-	virtual void clear() override;
-
-	/**
-	 * Click the window
-	 */
-	virtual void click(const Common::Point &newPos) override;
-
-	/**
-	 * Prepare for inputing a line
-	 */
-	virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
-
-	/**
-	 * Prepare for inputing a line
-	 */
-	virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) override;
-
-	/**
-	 * Cancel an input line event
-	 */
-	virtual void cancelLineEvent(Event *ev) override;
-
-	/**
-	 * Cancel a mouse event
-	 */
-	virtual void cancelMouseEvent() override { _mouseRequest = false; }
-
-	/**
-	 * Cancel a hyperlink event
-	 */
-	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
-
-	/**
-	 * Redraw the window
-	 */
-	virtual void redraw() override;
-};
-
-/**
- * Text Buffer window
- */
-class TextBufferWindow : public Window {
-	/**
-	 * Structure for a row within the window
-	 */
-	struct TextBufferRow {
-		Common::Array<uint32> chars;
-		Common::Array<Attributes> attr;
-		int len, newline;
-		bool dirty, repaint;
-		Picture *lpic, *rpic;
-		glui32 lhyper, rhyper;
-		int lm, rm;
-
-		/**
-		 * Constructor
-		 */
-		TextBufferRow();
-
-		/**
-		 * Resize the row
-		 */
-		void resize(size_t newSize);
-	};
-	typedef Common::Array<TextBufferRow> TextBufferRows;
-private:
-	void reflow();
-	void touchScroll();
-	bool putPicture(Picture *pic, glui32 align, glui32 linkval);
-	void putTextUni(const glui32 *buf, int len, int pos, int oldlen);
-	void flowBreak();
-
-	/**
-	 * Mark a given text row as modified
-	 */
-	void touch(int line);
-public:
-	int _width, _height;
-	int _spaced;
-	int _dashed;
-
-	TextBufferRows _lines;
-	int _scrollBack;
-
-	int _numChars;        ///< number of chars in last line: lines[0]
-	glui32 *_chars;       ///< alias to lines[0].chars
-	Attributes *_attrs;  ///< alias to lines[0].attrs
-
-    ///< adjust margins temporarily for images
-	int _ladjw;
-	int _ladjn;
-	int _radjw;
-	int _radjn;
-
-	/* Command history. */
-	glui32 *_history[HISTORYLEN];
-	int _historyPos;
-	int _historyFirst, _historyPresent;
-
-	/* for paging */
-	int _lastSeen;
-	int _scrollPos;
-	int _scrollMax;
-
-	/* for line input */
-	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
-	int _inMax;
-	long _inFence;
-	long _inCurs;
-	Attributes _origAttr;
-	gidispatch_rock_t _inArrayRock;
-
-	glui32 _echoLineInput;
-	glui32 *_lineTerminators;
-
-	/* style hints and settings */
-	WindowStyle styles[style_NUMSTYLES];
-
-	/* for copy selection */
-	glui32 *_copyBuf;
-	int _copyPos;
-public:
-	/**
-	 * Constructor
-	 */
-	TextBufferWindow(Windows *windows, uint32 rock);
-
-	/**
-	 * Destructor
-	 */
-	virtual ~TextBufferWindow();
-
-	/**
-	 * Rearranges the window
-	 */
-	virtual void rearrange(const Common::Rect &box) override;
-
-	/**
-	 * Get window split size within parent pair window
-	 */
-	virtual glui32 getSplit(glui32 size, bool vertical) const override;
-
-	/**
-	 * Write a character
-	 */
-	virtual void putChar(unsigned char ch) override;
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void putCharUni(uint32 ch) override;
-
-	/**
-	 * Unput a unicode character
-	 */
-	virtual bool unputCharUni(uint32 ch) override;
-
-	/**
-	 * Write a buffer
-	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) override;
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void putBufferUni(const uint32 *buf, size_t len) override;
-
-	/**
-	 * Move the cursor
-	 */
-	virtual void moveCursor(const Common::Point &newPos) override;
-
-	/**
-	 * Clear the window
-	 */
-	virtual void clear() override;
-
-	/**
-	 * Prepare for inputing a line
-	 */
-	virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
-
-	/**
-	 * Prepare for inputing a line
-	 */
-	virtual void requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) override;
-
-	/**
-	 * Cancel an input line event
-	 */
-	virtual void cancelLineEvent(Event *ev) override;
-
-	/**
-	 * Cancel a hyperlink event
-	 */
-	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
-
-	/**
-	 * Redraw the window
-	 */
-	virtual void redraw() override;
-};
-
-/**
- * Graphics window
- */
-class GraphicsWindow : public Window {
-private:
-	void touch();
-public:
-	unsigned char _bgnd[3];
-	bool _dirty;
-	glui32 _w, _h;
-	Graphics::ManagedSurface *_surface;
-public:
-	/**
-	 * Constructor
-	 */
-	GraphicsWindow(Windows *windows, uint32 rock);
-
-	/**
-	 * Destructor
-	 */
-	virtual ~GraphicsWindow();
-
-	/**
-	 * Rearranges the window
-	 */
-	virtual void rearrange(const Common::Rect &box) override;
-
-	/**
-	 * Get window split size within parent pair window
-	 */
-	virtual glui32 getSplit(glui32 size, bool vertical) const override {
-		return size;
-	}
-
-	/**
-	 * Cancel a mouse event
-	 */
-	virtual void cancelMouseEvent() override { _mouseRequest = false; }
-
-	/**
-	 * Cancel a hyperlink event
-	 */
-	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
-
-	/**
-	 * Redraw the window
-	 */
-	virtual void redraw() override;
-
-	/**
-	 * Get the window dimensions
-	 */
-	void getSize(glui32 *w, glui32 *h) {
-		*w = _w;
-		*h = _h;
-	}
-};
-
-/**
- * Pair window
- */
-class PairWindow : public Window {
-public:
-	Window *_child1, *_child2;
-
-	/* split info... */
-	glui32 _dir;               ///< winmethod_Left, Right, Above, or Below
-	bool _vertical, _backward; ///< flags
-	glui32 _division;          ///< winmethod_Fixed or winmethod_Proportional
-	Window *_key;              ///< NULL or a leaf-descendant (not a Pair)
-	int _keyDamage;            ///< used as scratch space in window closing
-	glui32 _size;              ///< size value
-	glui32 _wBorder;           ///< winMethod_Border, NoBorder
-public:
-	/**
-	 * Constructor
-	 */
-	PairWindow(Windows *windows, glui32 method, Window *key, glui32 size);
-
-	/**
-	 * Rearranges the window
-	 */
-	virtual void rearrange(const Common::Rect &box) override;
-
-	/**
-	 * Redraw the window
-	 */
-	virtual void redraw() override;
-};
-
 } // End of namespace Gargoyle
 
 #endif


Commit: ede0323c08fc7d5853736d42c7f5910c18ff1fd1
    https://github.com/scummvm/scummvm/commit/ede0323c08fc7d5853736d42c7f5910c18ff1fd1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Windows don't have a non-unicode putChar method

Changed paths:
    engines/gargoyle/streams.cpp
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 7dec179..7499991 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -79,7 +79,7 @@ void WindowStream::putChar(unsigned char ch) {
 		}
 	}
 
-	_window->putChar(ch);
+	_window->putCharUni(ch);
 	if (_window->_echoStream)
 		_window->_echoStream->putChar(ch);
 }
@@ -118,7 +118,7 @@ void WindowStream::putBuffer(const char *buf, size_t len) {
 	}
 
 	for (size_t lx = 0; lx < len; lx++, buf++)
-		_window->putChar(*buf);
+		_window->putCharUni(*buf);
 	if (_window->_echoStream)
 		_window->_echoStream->putBuffer(buf, len);
 
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 1e85a24..9b1268e 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -277,9 +277,6 @@ glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
 	return (vertical) ? size * g_conf->_cellW : size * g_conf->_cellH;
 }
 
-void TextBufferWindow::putChar(unsigned char ch) {
-}
-
 void TextBufferWindow::putCharUni(glui32 ch) {
 	/*
 	glui32 bchars[TBLINELEN];
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index f7427d7..0e6ee2c 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -133,11 +133,6 @@ public:
 	virtual glui32 getSplit(glui32 size, bool vertical) const override;
 
 	/**
-	 * Write a character
-	 */
-	virtual void putChar(unsigned char ch) override;
-
-	/**
 	 * Write a unicode character
 	 */
 	virtual void putCharUni(uint32 ch) override;
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 41444ed..92bdcf5 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -82,10 +82,6 @@ glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
 		size * g_conf->_cellH + g_conf->_tMarginY * 2;
 }
 
-void TextGridWindow::putChar(unsigned char ch) {
-
-}
-
 void TextGridWindow::putCharUni(uint32 ch) {
 	TextGridRow *ln;
 
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 2c72dde..8ebaf82 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -105,11 +105,6 @@ public:
 	virtual glui32 getSplit(glui32 size, bool vertical) const override;
 
 	/**
-	 * Write a character
-	 */
-	virtual void putChar(unsigned char ch) override;
-
-	/**
 	 * Write a unicode character
 	 */
 	virtual void putCharUni(uint32 ch) override;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index a5bc2f4..60f6bb4 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -313,11 +313,6 @@ public:
 	/**
 	 * Write a character
 	 */
-	virtual void putChar(unsigned char ch) {}
-
-	/**
-	 * Write a unicode character
-	 */
 	virtual void putCharUni(uint32 ch) {}
 
 	/**


Commit: cb2b7d439fb9cb89f2758d103ba2ea710e4f0a72
    https://github.com/scummvm/scummvm/commit/cb2b7d439fb9cb89f2758d103ba2ea710e4f0a72
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Remove more Window methods that aren't actually present

Changed paths:
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 9b1268e..b916fc4 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -432,14 +432,6 @@ bool TextBufferWindow::unputCharUni(uint32 ch) {
 	return false;
 }
 
-void TextBufferWindow::putBuffer(const unsigned char *buf, size_t len) {
-	// TODO
-}
-
-void TextBufferWindow::putBufferUni(const uint32 *buf, size_t len) {
-	// TODO
-}
-
 void TextBufferWindow::moveCursor(const Common::Point &newPos) {
 	// TODO
 }
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index 0e6ee2c..ebba2b4 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -143,16 +143,6 @@ public:
 	virtual bool unputCharUni(uint32 ch) override;
 
 	/**
-	 * Write a buffer
-	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) override;
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void putBufferUni(const uint32 *buf, size_t len) override;
-
-	/**
 	 * Move the cursor
 	 */
 	virtual void moveCursor(const Common::Point &newPos) override;
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 92bdcf5..17bb222 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -159,14 +159,6 @@ bool TextGridWindow::unputCharUni(uint32 ch) {
 	}
 }
 
-void TextGridWindow::putBuffer(const unsigned char *buf, size_t len) {
-	// TODO
-}
-
-void TextGridWindow::putBufferUni(const uint32 *buf, size_t len) {
-	// TODO
-}
-
 void TextGridWindow::moveCursor(const Common::Point &pos) {
 	// If the values are negative, they're really huge positive numbers --
 	// remember that they were cast from glui32. So set them huge and
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 8ebaf82..0cf6d5e 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -115,16 +115,6 @@ public:
 	virtual bool unputCharUni(uint32 ch) override;
 
 	/**
-	 * Write a buffer
-	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) override;
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void putBufferUni(const uint32 *buf, size_t len) override;
-
-	/**
 	 * Move the cursor
 	 */
 	virtual void moveCursor(const Common::Point &newPos) override;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 60f6bb4..5e6374c 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -321,16 +321,6 @@ public:
 	virtual bool unputCharUni(uint32 ch) { return false; }
 
 	/**
-	 * Write a buffer
-	 */
-	virtual void putBuffer(const unsigned char *buf, size_t len) {}
-
-	/**
-	 * Write a unicode character
-	 */
-	virtual void putBufferUni(const uint32 *buf, size_t len) {}
-
-	/**
 	 * Move the cursor
 	 */
 	virtual void moveCursor(const Common::Point &newPos) {}


Commit: ba907c9aec333903acb013e9d3a25962c69b6ca6
    https://github.com/scummvm/scummvm/commit/ba907c9aec333903acb013e9d3a25962c69b6ca6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Implementing text buffer window methods

Changed paths:
  A engines/gargoyle/speech.h
    engines/gargoyle/draw.cpp
    engines/gargoyle/draw.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/picture.cpp
    engines/gargoyle/picture.h
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_graphics.h
    engines/gargoyle/window_mask.cpp
    engines/gargoyle/window_mask.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/draw.cpp b/engines/gargoyle/draw.cpp
index 86d5fb0..f16d1a5 100644
--- a/engines/gargoyle/draw.cpp
+++ b/engines/gargoyle/draw.cpp
@@ -24,9 +24,28 @@
 
 namespace Gargoyle {
 
-int Draw::drawStringUni(int x, int y, int fidx, byte *rgb, glui32 *s, int n, int spw) {
+int Draw::drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw) {
 	// TODO
 	return 0;
 }
 
+int Draw::drawStringUni(int x, int y, int fidx, const byte *rgb, const glui32 *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+int Draw::stringWidth(int fidx, const char *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+int Draw::stringWidthUni(int fidx, const glui32 *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+void Draw::drawCaret(const Common::Point &pos) {
+	// TODO
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/draw.h b/engines/gargoyle/draw.h
index 61dfe18..e0ec133 100644
--- a/engines/gargoyle/draw.h
+++ b/engines/gargoyle/draw.h
@@ -23,14 +23,22 @@
 #ifndef GARGOYLE_DRAW_H
 #define GARGOYLE_DRAW_H
 
-#include "common/events.h"
+#include "common/rect.h"
 #include "gargoyle/glk_types.h"
 
 namespace Gargoyle {
 
 class Draw {
 protected:
-	int drawStringUni(int x, int y, int fidx, byte *rgb, glui32 *s, int n, int spw);
+	int drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw);
+
+	int drawStringUni(int x, int y, int fidx, const byte *rgb, const glui32 *s, int n, int spw);
+
+	int stringWidth(int fidx, const char *s, int n, int spw);
+
+	int stringWidthUni(int fidx, const glui32 *s, int n, int spw);
+
+	void drawCaret(const Common::Point &pos);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 8620e88..37df6d1 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -31,8 +31,10 @@
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/events.h"
+#include "gargoyle/picture.h"
 #include "gargoyle/streams.h"
 #include "gargoyle/windows.h"
+#include "gargoyle/window_mask.h"
 
 namespace Gargoyle {
 
@@ -40,7 +42,8 @@ GargoyleEngine *g_vm;
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _conf(nullptr),
-		_events(nullptr), _screen(nullptr), _windows(nullptr),
+		_events(nullptr), _picList(nullptr), _screen(nullptr), _windows(nullptr),
+		_windowMask(nullptr), _copySelect(false),
 		gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
 	g_vm = this;
 }
@@ -48,9 +51,11 @@ GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gam
 GargoyleEngine::~GargoyleEngine() {
 	delete _conf;
 	delete _events;
+	delete _picList;
 	delete _screen;
 	delete _streams;
 	delete _windows;
+	delete _windowMask;
 }
 
 void GargoyleEngine::initialize() {
@@ -64,8 +69,10 @@ void GargoyleEngine::initialize() {
 	_screen = new Graphics::Screen();
 	_conf = new Conf();
 	_events = new Events();
+	_picList = new PicList();
 	_streams = new Streams();
 	_windows = new Windows(_screen);
+	_windowMask = new WindowMask();
 }
 
 Common::Error GargoyleEngine::run() {
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 1d81cb9..b99b7b4 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -36,7 +36,9 @@ namespace Gargoyle {
 
 class Conf;
 class Events;
+class PicList;
 class Windows;
+class WindowMask;
 class Streams;
 
 enum InterpreterType {
@@ -94,8 +96,11 @@ protected:
 public:
 	Conf *_conf;
 	Events *_events;
+	PicList *_picList;
 	Streams *_streams;
 	Windows *_windows;
+	WindowMask *_windowMask;
+	bool _copySelect;
 	void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock);
 	gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, const char *typecode);
 	void (*gli_unregister_arr)(void *array, glui32 len, const char *typecode, gidispatch_rock_t objrock);
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index eaebb6f..a25a413 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -53,6 +53,35 @@ class Window;
 typedef struct glk_fileref_struct *frefid_t;
 typedef struct glk_schannel_struct *schanid_t;
 
+/**
+ * Usurp C1 space for ligatures and smart typography glyphs
+ */
+enum Enc {
+	ENC_LIG_FI = 128,
+	ENC_LIG_FL = 129,
+	ENC_LSQUO = 130,
+	ENC_RSQUO = 131,
+	ENC_LDQUO = 132,
+	ENC_RDQUO = 133,
+	ENC_NDASH = 134,
+	ENC_MDASH = 135,
+	ENC_FLOWBREAK = 136
+};
+
+/**
+ * These are the Unicode versions
+ */
+enum UniChars {
+	UNI_LIG_FI = 0xFB01,
+	UNI_LIG_FL = 0xFB02,
+	UNI_LSQUO = 0x2018,
+	UNI_RSQUO = 0x2019,
+	UNI_LDQUO = 0x201c,
+	UNI_RDQUO = 0x201d,
+	UNI_NDASH = 0x2013,
+	UNI_MDASH = 0x2014
+};
+
 enum Gestalt {
 	gestalt_Version                = 0,
 	gestalt_CharInput              = 1,
diff --git a/engines/gargoyle/picture.cpp b/engines/gargoyle/picture.cpp
index 4b0b892..fd33e71 100644
--- a/engines/gargoyle/picture.cpp
+++ b/engines/gargoyle/picture.cpp
@@ -24,6 +24,16 @@
 
 namespace Gargoyle {
 
+void PicList::increment() {
+	// TODO
+}
+
+void PicList::decrement() {
+	// TODO
+}
+
+/*--------------------------------------------------------------------------*/
+
 void Picture::increment() {
 	++_refCount;
 }
@@ -35,4 +45,19 @@ void Picture::decrement() {
 	}
 }
 
+Picture *Picture::load(uint32 id) {
+	// TODO: gli_picture_load
+	return nullptr;
+}
+
+Picture *Picture::scale(int sx, int sy) {
+	// TODO: gli_picture_scale
+	return nullptr;
+}
+
+void Picture::drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1) {
+	// TODO: drawPicture
+}
+
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/picture.h b/engines/gargoyle/picture.h
index 38f776c..c9527c5 100644
--- a/engines/gargoyle/picture.h
+++ b/engines/gargoyle/picture.h
@@ -27,7 +27,17 @@
 
 namespace Gargoyle {
 
+class PicList {
+public:
+	void increment();
+
+	void decrement();
+};
+
 struct Picture : Graphics::Surface {
+public:
+	static Picture *load(uint32 id);
+public:
 	int _refCount;
 	uint32 _id;
 	bool _scaled;
@@ -46,6 +56,16 @@ struct Picture : Graphics::Surface {
 	 * Decrement reference counter
 	 */
 	void decrement();
+
+	/**
+	 * Rescale the picture to a new picture of a given size
+	 */
+	Picture *scale(int sx, int sy);
+
+	/**
+	 * Draw the picture
+	 */
+	void drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/speech.h b/engines/gargoyle/speech.h
new file mode 100644
index 0000000..1ff2cb1
--- /dev/null
+++ b/engines/gargoyle/speech.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_SPEECH_H
+#define GARGOYLE_SPEECH_H
+
+#include "common/events.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+/**
+ * Currently not implemented
+ */
+class Speech {
+protected:
+	void gli_initialize_tts(void) {}
+
+	void gli_tts_flush(void) {}
+
+	void gli_tts_purge(void) {}
+
+	void gli_tts_speak(const glui32 *buf, size_t len) {}
+
+	void gli_free_tts(void) {}
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index cc9a5dd..732686d 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -86,4 +86,9 @@ void GraphicsWindow::redraw() {
 	// TODO
 }
 
+glui32 GraphicsWindow::imageDraw(glui32 image, glui32 align, bool scaled, glui32 width, glui32 height) {
+	// TODO: win_graphics_draw_picture
+	return 0;
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h
index 77e4910..b0f53fa 100644
--- a/engines/gargoyle/window_graphics.h
+++ b/engines/gargoyle/window_graphics.h
@@ -77,6 +77,9 @@ public:
 	 */
 	virtual void redraw() override;
 
+	virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0,
+		glui32 height = 0) override;
+
 	/**
 	 * Get the window dimensions
 	 */
diff --git a/engines/gargoyle/window_mask.cpp b/engines/gargoyle/window_mask.cpp
index f0f6aa3..fe1d463 100644
--- a/engines/gargoyle/window_mask.cpp
+++ b/engines/gargoyle/window_mask.cpp
@@ -202,7 +202,7 @@ int WindowMask::checkSelection(uint x0, uint y0, uint x1, uint y1) {
 	return false;
 }
 
-int WindowMask::getSelection(uint x0, uint y0, uint x1, uint y1, uint *rx0, uint *rx1) {
+int WindowMask::getSelection(uint x0, uint y0, uint x1, uint y1, int *rx0, int *rx1) {
 	uint row, upper, lower, above, below;
 	int row_selected, found_left, found_right;
 	int from_right, from_below, is_above, is_below;
diff --git a/engines/gargoyle/window_mask.h b/engines/gargoyle/window_mask.h
index db0d7e6..9dc5826 100644
--- a/engines/gargoyle/window_mask.h
+++ b/engines/gargoyle/window_mask.h
@@ -30,13 +30,14 @@ namespace Gargoyle {
 
 class Window;
 
-struct WindowMask {
+class WindowMask {
+public:
 	size_t _hor, _ver;
 	glui32 **_links;
 	Common::Rect _select;
 
 	static int _lastX, _lastY;
-
+public:
 	/**
 	 * Constructor
 	 */
@@ -59,7 +60,7 @@ struct WindowMask {
 
 	int checkSelection(uint x0, uint y0, uint x1, uint y1);
 
-	int getSelection(uint x0, uint y0, uint x1, uint y1, uint *rx0, uint *rx1);
+	int getSelection(uint x0, uint y0, uint x1, uint y1, int *rx0, int *rx1);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index b916fc4..3df2e5c 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -23,9 +23,17 @@
 #include "gargoyle/window_text_buffer.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/string.h"
 
 namespace Gargoyle {
 
+/**
+ *
+ * How many pixels we add to left/right margins
+ */
+#define SLOP (2 * GLI_SUBPIX)
+
+
 TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windows, rock),
 		_historyPos(0), _historyFirst(0), _historyPresent(0), _lastSeen(0), _scrollPos(0),
 		_scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1), _inBuf(nullptr),
@@ -35,7 +43,7 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windo
 	_type = wintype_TextBuffer;
 	Common::fill(&_history[0], &_history[HISTORYLEN], nullptr);
 
-	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], styles);
+	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], _styles);
 }
 
 TextBufferWindow::~TextBufferWindow() {
@@ -49,10 +57,10 @@ TextBufferWindow::~TextBufferWindow() {
 	delete[] _lineTerminators;
 
 	for (int i = 0; i < _scrollBack; i++) {
-		if (_lines[i].lpic)
-			_lines[i].lpic->decrement();
-		if (_lines[i].rpic)
-			_lines[i].rpic->decrement();
+		if (_lines[i]._lPic)
+			_lines[i]._lPic->decrement();
+		if (_lines[i]._rPic)
+			_lines[i]._rPic->decrement();
 	}
 }
 
@@ -109,7 +117,7 @@ void TextBufferWindow::reflow() {
 	if (_height < 4 || _width < 20)
 		return;
 
-	_lines[0].len = _numChars;
+	_lines[0]._len = _numChars;
 
 	/* allocate temp buffers */
 	Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN];
@@ -142,32 +150,32 @@ void TextBufferWindow::reflow() {
 		if (k == 0 && _lineRequest)
 			inputbyte = p + _inFence;
 
-		if (_lines[k].lpic) {
+		if (_lines[k]._lPic) {
 			offsetbuf[x] = p;
 			alignbuf[x] = imagealign_MarginLeft;
-			pictbuf[x] = _lines[k].lpic;
+			pictbuf[x] = _lines[k]._lPic;
 
 			if (pictbuf[x]) pictbuf[x]->increment();
-			hyperbuf[x] = _lines[k].lhyper;
+			hyperbuf[x] = _lines[k]._lHyper;
 			x++;
 		}
 
-		if (_lines[k].rpic) {
+		if (_lines[k]._rPic) {
 			offsetbuf[x] = p;
 			alignbuf[x] = imagealign_MarginRight;
-			pictbuf[x] = _lines[k].rpic;
+			pictbuf[x] = _lines[k]._rPic;
 			if (pictbuf[x]) pictbuf[x]->increment();
-			hyperbuf[x] = _lines[k].rhyper;
+			hyperbuf[x] = _lines[k]._rHyper;
 			x++;
 		}
 
-		for (i = 0; i < _lines[k].len; i++) {
-			attrbuf[p] = curattr = _lines[k].attr[i];
-			charbuf[p] = _lines[k].chars[i];
+		for (i = 0; i < _lines[k]._len; i++) {
+			attrbuf[p] = curattr = _lines[k]._attrs[i];
+			charbuf[p] = _lines[k]._chars[i];
 			p++;
 		}
 
-		if (_lines[k].newline) {
+		if (_lines[k]._newLine) {
 			attrbuf[p] = curattr;
 			charbuf[p] = '\n';
 			p++;
@@ -224,32 +232,32 @@ void TextBufferWindow::touchScroll() {
 	_windows->repaint(_bbox);
 
 	for (int i = 0; i < _scrollMax; i++)
-		_lines[i].dirty = true;
+		_lines[i]._dirty = true;
 }
 
 bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
 	if (align == imagealign_MarginRight)
 	{
-		if (_lines[0].rpic || _numChars)
+		if (_lines[0]._rPic || _numChars)
 			return false;
 
 		_radjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
 		_radjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
-		_lines[0].rpic = pic;
-		_lines[0].rm = _radjw;
-		_lines[0].rhyper = linkval;
+		_lines[0]._rPic = pic;
+		_lines[0]._rm = _radjw;
+		_lines[0]._rHyper = linkval;
 	} else {
 		if (align != imagealign_MarginLeft && _numChars)
 			putCharUni('\n');
 
-		if (_lines[0].lpic || _numChars)
+		if (_lines[0]._lPic || _numChars)
 			return false;
 
 		_ladjw = (pic->w + g_conf->_tMarginX) * GLI_SUBPIX;
 		_ladjn = (pic->h + g_conf->_cellH - 1) / g_conf->_cellH;
-		_lines[0].lpic = pic;
-		_lines[0].lm = _ladjw;
-		_lines[0].lhyper = linkval;
+		_lines[0]._lPic = pic;
+		_lines[0]._lm = _ladjw;
+		_lines[0]._lHyper = linkval;
 
 		if (align != imagealign_MarginLeft)
 			flowBreak();
@@ -258,17 +266,110 @@ bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
 	return true;
 }
 
-void TextBufferWindow::flowBreak() {
-	// TODO
+glui32 TextBufferWindow::imageDraw(glui32 image, glui32 align, bool scaled, glui32 width, glui32 height) {
+	Picture *pic;
+	glui32 hyperlink;
+	int error;
+
+	pic = Picture::load(image);
+
+	if (!pic)
+		return false;
+
+	if (!_imageLoaded) {
+		g_vm->_picList->increment();
+		_imageLoaded = true;
+	}
+
+	if (scaled) {
+		Picture *tmp;
+		tmp = pic->scale(width, height);
+		pic = tmp;
+	}
+
+	hyperlink = _attr.hyper;
+
+	pic->increment();
+	error = putPicture(pic, align, hyperlink);
+
+	return error;
+}
+
+bool TextBufferWindow::flowBreak() {
+	while (_ladjn || _radjn)
+		putCharUni('\n');
+	return true;
+}
+
+void TextBufferWindow::putText(const char *buf, int len, int pos, int oldlen) {
+	int diff = len - oldlen;
+
+	if (_numChars + diff >= TBLINELEN)
+		return;
+
+	if (diff != 0 && pos + oldlen < _numChars)
+	{
+		memmove(_chars + pos + len,
+			_chars + pos + oldlen,
+			(_numChars - (pos + oldlen)) * 4);
+		memmove(_attrs + pos + len,
+			_attrs + pos + oldlen,
+			(_numChars - (pos + oldlen)) * sizeof(Attributes));
+	}
+	if (len > 0) {
+		int i;
+		for (i = 0; i < len; i++) {
+			_chars[pos + i] = buf[i];
+			_attrs[pos + i].set(style_Input);
+		}
+	}
+	_numChars += diff;
+
+	if (_inBuf) {
+		if (_inCurs >= pos + oldlen)
+			_inCurs += diff;
+		else if (_inCurs >= pos)
+			_inCurs = pos + len;
+	}
+
+	touch(0);
 }
 
 void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldlen) {
-	// TODO
+	int diff = len - oldlen;
+
+	if (_numChars + diff >= TBLINELEN)
+		return;
+
+	if (diff != 0 && pos + oldlen < _numChars) {
+		memmove(_chars + pos + len,
+			_chars + pos + oldlen,
+			(_numChars - (pos + oldlen)) * 4);
+		memmove(_attrs + pos + len,
+			_attrs + pos + oldlen,
+			(_numChars - (pos + oldlen)) * sizeof(Attributes));
+	}
+	if (len > 0) {
+		int i;
+		memmove(_chars + pos, buf, len * 4);
+		for (i = 0; i < len; i++)
+			_attrs[pos + i].set(style_Input);
+	}
+	_numChars += diff;
+
+	if (_inBuf) {
+		if (_inCurs >= pos + oldlen)
+			_inCurs += diff;
+		else if (_inCurs >= pos)
+			_inCurs = pos + len;
+	}
+
+	touch(0);
 }
 
 void TextBufferWindow::touch(int line) {
 	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
-	_lines[line].dirty = 1;
+	_lines[line]._dirty = 1;
 	_windows->clearSelection();
 	_windows->repaint(Common::Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
 }
@@ -278,7 +379,6 @@ glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
 }
 
 void TextBufferWindow::putCharUni(glui32 ch) {
-	/*
 	glui32 bchars[TBLINELEN];
 	Attributes battrs[TBLINELEN];
 	int pw;
@@ -286,29 +386,28 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 	int saved;
 	int i;
 	int linelen;
-	unsigned char *color;
+	byte *color;
 
 	gli_tts_speak(&ch, 1);
 
-	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - gli_scroll_width) * GLI_SUBPIX;
-	pw = pw - 2 * SLOP - radjw - ladjw;
+	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) * GLI_SUBPIX;
+	pw = pw - 2 * SLOP - _radjw - _ladjw;
 
-	color = Windows::_overrideBgSet ? gli_window_color : bgcolor;
+	color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
 
 	// oops ... overflow
-	if (numchars + 1 >= TBLINELEN)
-		scrolloneline(dwin, 0);
+	if (_numChars + 1 >= TBLINELEN)
+		scrollOneLine(0);
 
 	if (ch == '\n') {
-		scrolloneline(dwin, 1);
+		scrollOneLine(1);
 		return;
 	}
 
-	if (gli_conf_quotes) {
+	if (g_conf->_quotes) {
 		// fails for 'tis a wonderful day in the '80s
-		if (gli_conf_quotes > 1 && ch == '\'')
-		{
-			if (numchars == 0 || leftquote(_chars[numchars - 1]))
+		if (g_conf->_quotes > 1 && ch == '\'') {
+			if (_numChars == 0 || leftquote(_chars[_numChars - 1]))
 				ch = UNI_LSQUO;
 		}
 
@@ -318,122 +417,112 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 		if (ch == '\'')
 			ch = UNI_RSQUO;
 
-		if (ch == '"')
-		{
-			if (numchars == 0 || leftquote(_chars[numchars - 1]))
+		if (ch == '"') {
+			if (_numChars == 0 || leftquote(_chars[_numChars - 1]))
 				ch = UNI_LDQUO;
 			else
 				ch = UNI_RDQUO;
 		}
 	}
 
-	if (gli_conf_dashes && attr.style != style_Preformatted)
-	{
-		if (ch == '-')
-		{
-			dashed++;
-			if (dashed == 2)
-			{
-				numchars--;
-				if (gli_conf_dashes == 2)
+	if (g_conf->_dashes && _attr.style != style_Preformatted) {
+		if (ch == '-') {
+			_dashed++;
+			if (_dashed == 2) {
+				_numChars--;
+				if (g_conf->_dashes == 2)
 					ch = UNI_NDASH;
 				else
 					ch = UNI_MDASH;
 			}
-			if (dashed == 3)
-			{
-				numchars--;
+			if (_dashed == 3) {
+				_numChars--;
 				ch = UNI_MDASH;
-				dashed = 0;
+				_dashed = 0;
 			}
+		} else {
+			_dashed = 0;
 		}
-		else
-			dashed = 0;
 	}
 
-	if (gli_conf_spaces && attr.style != style_Preformatted
-		&& styles[attr.style].bg == color
-		&& !styles[attr.style].reverse)
+	if (g_conf->_spaces && _attr.style != style_Preformatted
+		&& _styles[_attr.style].bg == color
+		&& !_styles[_attr.style].reverse)
 	{
 		// turn (period space space) into (period space)
-		if (gli_conf_spaces == 1)
-		{
+		if (g_conf->_spaces == 1) {
 			if (ch == '.')
-				spaced = 1;
-			else if (ch == ' ' && spaced == 1)
-				spaced = 2;
-			else if (ch == ' ' && spaced == 2)
-			{
-				spaced = 0;
+				_spaced = 1;
+			else if (ch == ' ' && _spaced == 1)
+				_spaced = 2;
+			else if (ch == ' ' && _spaced == 2) {
+				_spaced = 0;
 				return;
+			} else {
+				_spaced = 0;
 			}
-			else
-				spaced = 0;
 		}
 
 		// Turn (per sp x) into (per sp sp x)
-		if (gli_conf_spaces == 2)
-		{
+		if (g_conf->_spaces == 2) {
 			if (ch == '.')
-				spaced = 1;
-			else if (ch == ' ' && spaced == 1)
-				spaced = 2;
-			else if (ch != ' ' && spaced == 2)
+				_spaced = 1;
+			else if (ch == ' ' && _spaced == 1)
+				_spaced = 2;
+			else if (ch != ' ' && _spaced == 2)
 			{
-				spaced = 0;
-				win_textbuffer_putchar_uni(win, ' ');
+				_spaced = 0;
+				putCharUni(' ');
+			} else {
+				_spaced = 0;
 			}
-			else
-				spaced = 0;
 		}
 	}
 
-	_chars[numchars] = ch;
-	attrs[numchars] = attr;
-	numchars++;
+	_chars[_numChars] = ch;
+	_attrs[_numChars] = _attr;
+	_numChars++;
 
 	// kill spaces at the end for line width calculation
-	linelen = numchars;
+	linelen = _numChars;
 	while (linelen > 1 && _chars[linelen - 1] == ' '
-		&& styles[attrs[linelen - 1].style].bg == color
-		&& !styles[attrs[linelen - 1].style].reverse)
+		&& _styles[_attrs[linelen - 1].style].bg == color
+		&& !_styles[_attrs[linelen - 1].style].reverse)
 		linelen--;
 
-	if (calcwidth(dwin, _chars, attrs, 0, linelen, -1) >= pw)
-	{
-		bpoint = numchars;
+	if (calcWidth(_chars, _attrs, 0, linelen, -1) >= pw) {
+		bpoint = _numChars;
 
-		for (i = numchars - 1; i > 0; i--)
-			if (_chars[i] == ' ')
-			{
+		for (i = _numChars - 1; i > 0; i--)
+			if (_chars[i] == ' ') {
 				bpoint = i + 1; // skip space
 				break;
 			}
 
-		saved = numchars - bpoint;
+		saved = _numChars - bpoint;
 
 		memcpy(bchars, _chars + bpoint, saved * 4);
-		memcpy(battrs, attrs + bpoint, saved * sizeof(attr_t));
-		numchars = bpoint;
+		memcpy(battrs, _attrs + bpoint, saved * sizeof(Attributes));
+		_numChars = bpoint;
 
-		scrolloneline(dwin, 0);
+		scrollOneLine(0);
 
 		memcpy(_chars, bchars, saved * 4);
-		memcpy(attrs, battrs, saved * sizeof(attr_t));
-		numchars = saved;
+		memcpy(_attrs, battrs, saved * sizeof(Attributes));
+		_numChars = saved;
 	}
 
 	touch(0);
-	*/
 }
 
 bool TextBufferWindow::unputCharUni(uint32 ch) {
-	// TODO
-	return false;
-}
+	if (_numChars > 0 && _chars[_numChars - 1] == ch) {
+		_numChars--;
+		touch(0);
+		return true;
+	}
 
-void TextBufferWindow::moveCursor(const Common::Point &newPos) {
-	// TODO
+	return false;
 }
 
 void TextBufferWindow::clear() {
@@ -454,20 +543,20 @@ void TextBufferWindow::clear() {
 	_numChars = 0;
 
 	for (i = 0; i < _scrollBack; i++) {
-		_lines[i].len = 0;
-
-		if (_lines[i].lpic) _lines[i].lpic->decrement();
-		_lines[i].lpic = nullptr;
-		if (_lines[i].rpic) _lines[i].rpic->decrement();
-		_lines[i].rpic = nullptr;
-
-		_lines[i].lhyper = 0;
-		_lines[i].rhyper = 0;
-		_lines[i].lm = 0;
-		_lines[i].rm = 0;
-		_lines[i].newline = 0;
-		_lines[i].dirty = true;
-		_lines[i].repaint = false;
+		_lines[i]._len = 0;
+
+		if (_lines[i]._lPic) _lines[i]._lPic->decrement();
+		_lines[i]._lPic = nullptr;
+		if (_lines[i]._rPic) _lines[i]._rPic->decrement();
+		_lines[i]._rPic = nullptr;
+
+		_lines[i]._lHyper = 0;
+		_lines[i]._rHyper = 0;
+		_lines[i]._lm = 0;
+		_lines[i]._rm = 0;
+		_lines[i]._newLine = 0;
+		_lines[i]._dirty = true;
+		_lines[i]._repaint = false;
 	}
 
 	_lastSeen = 0;
@@ -478,24 +567,146 @@ void TextBufferWindow::clear() {
 		touch(i);
 }
 
+void TextBufferWindow::click(const Common::Point &newPos) {
+	int gh = false;
+	int gs = false;
+
+	if (_lineRequest || _charRequest
+		|| _lineRequestUni || _charRequestUni
+		|| _moreRequest || _scrollRequest)
+		_windows->setFocus(this);
+
+	if (_hyperRequest) {
+		glui32 linkval = g_vm->_windowMask->getHyperlink(newPos);
+		if (linkval) {
+			g_vm->_events->eventStore(evtype_Hyperlink, this, linkval, 0);
+			_hyperRequest = false;
+			if (g_conf->_safeClicks)
+				g_vm->_events->_forceClick = 1;
+			gh = true;
+		}
+	}
+
+	if (newPos.x > _bbox.right - g_conf->_scrollWidth) {
+		if (newPos.y < _bbox.top + g_conf->_tMarginY + g_conf->_scrollWidth)
+			acceptScroll(keycode_Up);
+		else if (newPos.y > _bbox.bottom - g_conf->_tMarginY - g_conf->_scrollWidth)
+			acceptScroll(keycode_Down);
+		else if (newPos.y < (_bbox.top + _bbox.bottom) / 2)
+			acceptScroll(keycode_PageUp);
+		else
+			acceptScroll(keycode_PageDown);
+		gs = true;
+	}
+
+	if (!gh && !gs) {
+		g_vm->_copySelect = true;
+		g_vm->_windowMask->startSelection(newPos);
+	}
+}
+
 void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
-	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
-	{
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
 		warning("request_line_event: window already has keyboard request");
 		return;
 	}
 
-	// TODO
+	int pw;
+
+	gli_tts_flush();
+
+	/* because '>' prompt is ugly without extra space */
+	if (_numChars && _chars[_numChars - 1] == '>')
+		putCharUni(' ');
+	if (_numChars && _chars[_numChars - 1] == '?')
+		putCharUni(' ');
+
+	/* make sure we have some space left for typing... */
+	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2) * GLI_SUBPIX;
+	pw = pw - 2 * SLOP - _radjw + _ladjw;
+	if (calcWidth(_chars, _attrs, 0, _numChars, -1) >= pw * 3 / 4)
+		putCharUni('\n');
+
+	_inBuf = buf;
+	_inMax = maxlen;
+	_inFence = _numChars;
+	_inCurs = _numChars;
+	_origAttr = _attr;
+	_attr.set(style_Input);
+
+	_historyPos = _historyPresent;
+
+	if (initlen) {
+		touch(0);
+		putText(buf, initlen, _inCurs, 0);
+	}
+
+	_echoLineInput = _echoLineInputBase;
+
+	if (_lineTerminatorsBase && _termCt) {
+		_lineTerminators = new glui32[_termCt + 1];
+
+		if (_lineTerminators) {
+			memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+			_lineTerminators[_termCt] = 0;
+		}
+	}
+
+	if (g_vm->gli_register_arr)
+		_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Cn");
 }
 
 void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 initlen) {
-	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni)
-	{
+	if (_charRequest || _lineRequest || _charRequestUni || _lineRequestUni) {
 		warning("request_line_event_uni: window already has keyboard request");
 		return;
 	}
 
-	// TODO
+	int pw;
+
+	gli_tts_flush();
+
+	/* because '>' prompt is ugly without extra space */
+	if (_numChars && _chars[_numChars - 1] == '>')
+		putCharUni(' ');
+	if (_numChars && _chars[_numChars - 1] == '?')
+		putCharUni(' ');
+
+	/* make sure we have some space left for typing... */
+	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2) * GLI_SUBPIX;
+	pw = pw - 2 * SLOP - _radjw + _ladjw;
+	if (calcWidth(_chars, _attrs, 0, _numChars, -1) >= pw * 3 / 4)
+		putCharUni('\n');
+
+	//_lastSeen = 0;
+
+	_inBuf = buf;
+	_inMax = maxlen;
+	_inFence = _numChars;
+	_inCurs = _numChars;
+	_origAttr = _attr;
+	_attr.set(style_Input);
+
+	_historyPos = _historyPresent;
+
+	if (initlen) {
+		touch(0);
+		putTextUni(buf, initlen, _inCurs, 0);
+	}
+
+	_echoLineInput = _echoLineInputBase;
+
+	if (_lineTerminatorsBase && _termCt) {
+		_lineTerminators = new glui32[_termCt + 1];
+
+		if (_lineTerminators) {
+			memcpy(_lineTerminators, _lineTerminatorsBase, _termCt * sizeof(glui32));
+			_lineTerminators[_termCt] = 0;
+		}
+	}
+
+	if (g_vm->gli_register_arr)
+		_inArrayRock = (*g_vm->gli_register_arr)(buf, maxlen, "&+#!Iu");
 }
 
 void TextBufferWindow::cancelLineEvent(Event *ev) {
@@ -571,21 +782,857 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 }
 
 void TextBufferWindow::redraw() {
-	// TODO
+    TextBufferRow *ln;
+    int linelen;
+    int nsp, spw, pw;
+    int x0, y0, x1, y1;
+    int x, y, w;
+    int a, b;
+    glui32 link;
+    int font;
+    unsigned char *color;
+    int i;
+    int hx0, hx1, hy0, hy1;
+    int selbuf, selrow, selchar, sx0, sx1, selleft, selright;
+    int tx, tsc, tsw, lsc, rsc;
+
+    _lines[0]._len = _numChars;
+
+	ln = new TextBufferRow();
+    if (!ln)
+        return;
+
+    x0 = (_bbox.left + g_conf->_tMarginX) * GLI_SUBPIX;
+    x1 = (_bbox.right - g_conf->_tMarginX - g_conf->_scrollWidth) * GLI_SUBPIX;
+    y0 = _bbox.top + g_conf->_tMarginY;
+    y1 = _bbox.bottom - g_conf->_tMarginY;
+
+    pw = x1 - x0 - 2 * GLI_SUBPIX;
+
+    /* check if any part of buffer is selected */
+    selbuf = g_vm->_windowMask->checkSelection(x0/GLI_SUBPIX,y0,x1/GLI_SUBPIX,y1);
+
+    for (i = _scrollPos + _height - 1; i >= _scrollPos; i--) {
+        /* top of line */
+        y = y0 + (_height - (i - _scrollPos) - 1) * g_conf->_leading;
+
+        /* check if part of line is selected */
+        if (selbuf) {
+            selrow = g_vm->_windowMask->getSelection(x0/GLI_SUBPIX, y,
+                    x1/GLI_SUBPIX, y + g_conf->_leading,
+                    &sx0, &sx1);
+            selleft = (sx0 == x0/GLI_SUBPIX);
+            selright = (sx1 == x1/GLI_SUBPIX);
+        } else {
+            selrow = false;
+        }
+
+        /* mark selected line dirty */
+        if (selrow)
+            _lines[i]._dirty = true;
+
+        memcpy(ln, &_lines[i], sizeof(TextBufferRow));
+
+        /* skip if we can */
+        if (!ln->_dirty && !ln->_repaint && !Windows::_forceRedraw && _scrollPos == 0)
+            continue;
+
+        /* repaint previously selected lines if needed */
+        if (ln->_repaint && !Windows::_forceRedraw)
+            _windows->redrawRect(Common::Rect(x0 / GLI_SUBPIX, y,
+				x1/GLI_SUBPIX, y + g_conf->_leading));
+
+        /* keep selected line dirty and flag for repaint */
+        if (!selrow) {
+            _lines[i]._dirty = false;
+            _lines[i]._repaint = false;
+        } else {
+            _lines[i]._repaint = true;
+        }
+
+        /* leave bottom line blank for [more] prompt */
+        if (i == _scrollPos && i > 0)
+            continue;
+
+        linelen = ln->_len;
+
+        /* kill spaces at the end unless they're a different color*/
+        color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+        while (i > 0 && linelen > 1 && ln->_chars[linelen-1] == ' ' 
+            && _styles[ln->_attrs[linelen-1].style].bg == color 
+            && !_styles[ln->_attrs[linelen-1].style].reverse)
+                linelen --;
+
+        /* kill characters that would overwrite the scroll bar */
+        while (linelen > 1 && calcWidth(ln->_chars, ln->_attrs, 0, linelen, -1) >= pw)
+            linelen --;
+
+        /*
+         * count spaces and width for justification
+         */
+        if (g_conf->_justify && !ln->_newLine && i > 0) {
+            for (a = 0, nsp = 0; a < linelen; a++)
+                if (ln->_chars[a] == ' ')
+                    nsp ++;
+            w = calcWidth(ln->_chars, ln->_attrs, 0, linelen, 0);
+            if (nsp)
+                spw = (x1 - x0 - ln->_lm - ln->_rm - 2 * SLOP - w) / nsp;
+            else
+                spw = 0;
+        } else {
+            spw = -1;
+        }
+
+        /* find and highlight selected characters */
+        if (selrow && !Windows::_claimSelect) {
+            lsc = 0;
+            rsc = 0;
+            selchar = false;
+            /* optimized case for all chars selected */
+            if (selleft && selright) {
+                rsc = linelen > 0 ? linelen - 1 : 0;
+                selchar = calcWidth(ln->_chars, ln->_attrs, lsc, rsc, spw)/GLI_SUBPIX;
+            } else {
+                /* optimized case for leftmost char selected */
+                if (selleft) {
+                    tsc = linelen > 0 ? linelen - 1 : 0;
+                    selchar = calcWidth(ln->_chars, ln->_attrs, lsc, tsc, spw)/GLI_SUBPIX;
+                } else {
+                    /* find the substring contained by the selection */
+                    tx = (x0 + SLOP + ln->_lm)/GLI_SUBPIX;
+                    /* measure string widths until we find left char */
+                    for (tsc = 0; tsc < linelen; tsc++) {
+                        tsw = calcWidth(ln->_chars, ln->_attrs, 0, tsc, spw)/GLI_SUBPIX;
+                        if (tsw + tx >= sx0 ||
+                                tsw + tx + GLI_SUBPIX >= sx0 && ln->_chars[tsc] != ' ') {
+                            lsc = tsc;
+                            selchar = true;
+                            break;
+                        }
+                    }
+                }
+                if (selchar) {
+                    /* optimized case for rightmost char selected */
+                    if (selright) {
+                        rsc = linelen > 0 ? linelen - 1 : 0;
+                    } else {
+                    /* measure string widths until we find right char */
+                        for (tsc = lsc; tsc < linelen; tsc++) {
+                            tsw = calcWidth(ln->_chars, ln->_attrs, lsc, tsc, spw)/GLI_SUBPIX;
+                            if (tsw + sx0 < sx1)
+                                rsc = tsc;
+                        }
+                        if (lsc && !rsc)
+                            rsc = lsc;
+                    }
+                }
+            }
+            /* reverse colors for selected chars */
+            if (selchar) {
+                for (tsc = lsc; tsc <= rsc; tsc++) {
+                    ln->_attrs[tsc].reverse = !ln->_attrs[tsc].reverse;
+                    _copyBuf[_copyPos] = ln->_chars[tsc];
+                    _copyPos++;
+                }
+            }
+            /* add newline if we reach the end of the line */
+            if (ln->_len == 0 || ln->_len == (rsc+1)) {
+                _copyBuf[_copyPos] = '\n';
+                _copyPos++;
+            }
+        }
+
+        /* clear any stored hyperlink coordinates */
+        g_vm->_windowMask->putHyperlink(0, x0/GLI_SUBPIX, y,
+                x1/GLI_SUBPIX, y + g_conf->_leading);
+
+        /*
+         * fill in background colors
+         */
+        color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+        _windows->drawRect(x0/GLI_SUBPIX, y,
+                (x1-x0) / GLI_SUBPIX, g_conf->_leading,
+                color);
+
+        x = x0 + SLOP + ln->_lm;
+        a = 0;
+        for (b = 0; b < linelen; b++)
+        {
+            if (ln->_attrs[a] == ln->_attrs[b]) {
+                link = ln->_attrs[a].hyper;
+                font = ln->_attrs[a].attrFont(_styles);
+                color = ln->_attrs[a].attrBg(_styles);
+                w = stringWidthUni(font, ln->_chars + a, b - a, spw);
+                _windows->drawRect(x/GLI_SUBPIX, y,
+                        w/GLI_SUBPIX, g_conf->_leading,
+                        color);
+                if (link) {
+                    _windows->drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+                            w/GLI_SUBPIX + 1, g_conf->_linkStyle,
+                            g_conf->_linkColor);
+                    g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
+                            x/GLI_SUBPIX + w/GLI_SUBPIX,
+                            y + g_conf->_leading);
+                }
+                x += w;
+                a = b;
+            }
+        }
+        link = ln->_attrs[a].hyper;
+        font = ln->_attrs[a].attrFont(_styles);
+        color = ln->_attrs[a].attrBg(_styles);
+        w = stringWidthUni(font, ln->_chars + a, b - a, spw);
+        _windows->drawRect(x/GLI_SUBPIX, y, w/GLI_SUBPIX,
+                g_conf->_leading, color);
+        if (link) {
+            _windows->drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+                    w/GLI_SUBPIX + 1, g_conf->_linkStyle,
+                    g_conf->_linkColor);
+            g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
+                    x/GLI_SUBPIX + w/GLI_SUBPIX,
+                    y + g_conf->_leading);
+        }
+        x += w;
+
+        color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+        _windows->drawRect(x/GLI_SUBPIX, y,
+                x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
+                color);
+
+        /*
+         * draw caret
+         */
+
+        if (_windows->getFocusWindow() == this && i == 0 && (_lineRequest || _lineRequestUni)) {
+            w = calcWidth(_chars, _attrs, 0, _inCurs, spw);
+            if (w < pw - g_conf->_caretShape * 2 * GLI_SUBPIX)
+                drawCaret(Common::Point(x0 + SLOP + ln->_lm + w, y + g_conf->_baseLine));
+        }
+
+        /*
+         * draw text
+         */
+
+        x = x0 + SLOP + ln->_lm;
+        a = 0;
+        for (b = 0; b < linelen; b++)
+        {
+            if (ln->_attrs[a] == ln->_attrs[b]) {
+                link = ln->_attrs[a].hyper;
+                font = ln->_attrs[a].attrFont(_styles);
+                color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
+                x = drawStringUni(x, y + g_conf->_baseLine,
+                        font, color, ln->_chars + a, b - a, spw);
+                a = b;
+            }
+        }
+        link = ln->_attrs[a].hyper;
+        font = ln->_attrs[a].attrFont(_styles);
+        color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
+        drawStringUni(x, y + g_conf->_baseLine,
+                font, color, ln->_chars + a, linelen - a, spw);
+    }
+
+    /*
+     * draw more prompt
+     */
+    if (_scrollPos && _height > 1)
+    {
+        x = x0 + SLOP;
+        y = y0 + (_height - 1) * g_conf->_leading;
+
+        g_vm->_windowMask->putHyperlink(0, x0/GLI_SUBPIX, y,
+                x1/GLI_SUBPIX, y + g_conf->_leading);
+
+        color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
+        _windows->drawRect(x/GLI_SUBPIX, y,
+                x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
+                color);
+
+        w = stringWidth(g_conf->_moreFont,
+                g_conf->_morePrompt.c_str(), g_conf->_morePrompt.size(), -1);
+
+        if (g_conf->_moreAlign == 1)    /* center */
+            x = x0 + SLOP + (x1 - x0 - w - SLOP * 2) / 2;
+        if (g_conf->_moreAlign == 2)    /* right */
+            x = x1 - SLOP - w;
+
+        color = Windows::_overrideFgSet ? g_conf->_moreColor : _fgColor;
+        drawString(x, y + g_conf->_baseLine, 
+                g_conf->_moreFont, color,
+                g_conf->_morePrompt.c_str(), g_conf->_morePrompt.size(), -1);
+        y1 = y; /* don't want pictures overdrawing "[more]" */
+
+        /* try to claim the focus */
+        _moreRequest = true;
+        Windows::_moreFocus = true;
+    } else {
+        _moreRequest = false;
+        y1 = y0 + _height * g_conf->_leading;
+    }
+
+    /*
+     * draw the images
+     */
+    for (i = 0; i < _scrollBack; i++) {
+        memcpy(ln, &_lines[i], sizeof(TextBufferRow));
+
+        y = y0 + (_height - (i - _scrollPos) - 1) * g_conf->_leading;
+
+        if (ln->_lPic) {
+            if (y < y1 && y + ln->_lPic->h > y0) {
+				ln->_lPic->drawPicture(x0/GLI_SUBPIX, y, x0/GLI_SUBPIX, y0, x1/GLI_SUBPIX, y1);
+                link = ln->_lHyper;
+                hy0 = y > y0 ? y : y0;
+                hy1 = y + ln->_lPic->h < y1 ? y + ln->_lPic->h : y1;
+                hx0 = x0/GLI_SUBPIX;
+                hx1 = x0/GLI_SUBPIX + ln->_lPic->w < x1/GLI_SUBPIX
+                            ? x0/GLI_SUBPIX + ln->_lPic->w
+                            : x1/GLI_SUBPIX;
+                g_vm->_windowMask->putHyperlink(link, hx0, hy0, hx1, hy1);
+            }
+        }
+
+        if (ln->_rPic) {
+            if (y < y1 && y + ln->_rPic->h > y0) {
+				ln->_rPic->drawPicture(x1/GLI_SUBPIX - ln->_rPic->w, y,
+					x0/GLI_SUBPIX, y0, x1/GLI_SUBPIX, y1);
+                link = ln->_rHyper;
+                hy0 = y > y0 ? y : y0;
+                hy1 = y + ln->_rPic->h < y1 ? y + ln->_rPic->h : y1;
+                hx0 = x1/GLI_SUBPIX - ln->_rPic->w > x0/GLI_SUBPIX
+                            ? x1/GLI_SUBPIX - ln->_rPic->w
+                            : x0/GLI_SUBPIX;
+                hx1 = x1/GLI_SUBPIX;
+                g_vm->_windowMask->putHyperlink(link, hx0, hy0, hx1, hy1);
+            }
+        }
+    }
+
+    /*
+     * Draw the scrollbar
+     */
+
+    /* try to claim scroll keys */
+    _scrollRequest = _scrollMax > _height;
+
+    if (_scrollRequest && g_conf->_scrollWidth)
+    {
+        int t0, t1;
+        x0 = _bbox.right - g_conf->_scrollWidth;
+        x1 = _bbox.right;
+        y0 = _bbox.top + g_conf->_tMarginY;
+        y1 = _bbox.bottom - g_conf->_tMarginY;
+
+        g_vm->_windowMask->putHyperlink(0, x0, y0, x1, y1);
+
+        y0 += g_conf->_scrollWidth / 2;
+        y1 -= g_conf->_scrollWidth / 2;
+
+        // pos = thbot, pos - ht = thtop, max = wtop, 0 = wbot
+        t0 = (_scrollMax - _scrollPos) - (_height - 1);
+        t1 = (_scrollMax - _scrollPos);
+        if (_scrollMax > _height) {
+            t0 = t0 * (y1 - y0) / _scrollMax + y0;
+            t1 = t1 * (y1 - y0) / _scrollMax + y0;
+        } else {
+            t0 = t1 = y0;
+        }
+
+        _windows->drawRect(x0+1, y0, x1-x0-2, y1-y0, g_conf->_scrollBg);
+        _windows->drawRect(x0+1, t0, x1-x0-2, t1-t0, g_conf->_scrollFg);
+
+        for (i = 0; i < g_conf->_scrollWidth / 2 + 1; i++) {
+            _windows->drawRect(x0+g_conf->_scrollWidth/2-i,
+                    y0 - g_conf->_scrollWidth/2 + i,
+                    i*2, 1, g_conf->_scrollFg);
+            _windows->drawRect(x0+g_conf->_scrollWidth/2-i,
+                    y1 + g_conf->_scrollWidth/2 - i,
+                    i*2, 1, g_conf->_scrollFg);
+        }
+    }
+
+    /* send selected text to clipboard */
+    if (selbuf && _copyPos) {
+        Windows::_claimSelect = true;
+
+		copyTextToClipboard(_copyBuf, _copyPos);
+        for (i = 0; i < _copyPos; i++)
+            _copyBuf[i] = 0;
+        _copyPos = 0;
+    }
+
+    /* no more prompt means all text has been seen */
+    if (!_moreRequest)
+        _lastSeen = 0;
+
+    free(ln);
 }
 
-/*--------------------------------------------------------------------------*/
+int TextBufferWindow::acceptScroll(glui32 arg) {
+	int pageht = _height - 2;        /* 1 for prompt, 1 for overlap */
+	int startpos = _scrollPos;
+
+	switch (arg) {
+	case keycode_PageUp:
+		_scrollPos += pageht;
+		break;
+	case keycode_End:
+		_scrollPos = 0;
+		break;
+	case keycode_Up:
+		_scrollPos++;
+		break;
+	case keycode_Down:
+	case keycode_Return:
+		_scrollPos--;
+		break;
+	case keycode_MouseWheelUp:
+		_scrollPos += 3;
+		startpos = true;
+		break;
+	case keycode_MouseWheelDown:
+		_scrollPos -= 3;
+		startpos = true;
+		break;
+	case ' ':
+	case keycode_PageDown:
+		//default:
+		if (pageht)
+			_scrollPos -= pageht;
+		else
+			_scrollPos = 0;
+		break;
+	}
+
+	if (_scrollPos > _scrollMax - _height + 1)
+		_scrollPos = _scrollMax - _height + 1;
+	if (_scrollPos < 0)
+		_scrollPos = 0;
+	touchScroll();
+
+	return (startpos || _scrollPos);
+}
+
+void TextBufferWindow::acceptReadChar(glui32 arg) {
+	glui32 key;
+
+	if (_height < 2)
+		_scrollPos = 0;
+
+	if (_scrollPos
+		|| arg == keycode_PageUp
+		|| arg == keycode_MouseWheelUp) {
+		acceptScroll(arg);
+		return;
+	}
+
+	switch (arg) {
+	case keycode_Erase:
+		key = keycode_Delete;
+		break;
+	case keycode_MouseWheelUp:
+	case keycode_MouseWheelDown:
+		return;
+	default:
+		key = arg;
+	}
+
+	gli_tts_purge();
+
+	if (key > 0xff && key < (0xffffffff - keycode_MAXVAL + 1)) {
+		if (!(_charRequestUni) || key > 0x10ffff)
+			key = keycode_Unknown;
+	}
+
+	_charRequest = false;
+	_charRequestUni = false;
+	g_vm->_events->eventStore(evtype_CharInput, this, key, 0);
+}
+
+void TextBufferWindow::acceptReadLine(glui32 arg) {
+	glui32 *cx;
+	int len;
+
+	if (_height < 2)
+		_scrollPos = 0;
+
+	if (_scrollPos
+		|| arg == keycode_PageUp
+		|| arg == keycode_MouseWheelUp)
+	{
+		acceptScroll(arg);
+		return;
+	}
+
+	if (!_inBuf)
+		return;
+
+	if (_lineTerminators && checkTerminator(arg)) {
+		for (cx = _lineTerminators; *cx; cx++) {
+			if (*cx == arg) {
+				acceptLine(arg);
+				return;
+			}
+		}
+	}
+
+	switch (arg) {
+
+		/* History keys (up and down) */
+
+	case keycode_Up:
+		if (_historyPos == _historyFirst)
+			return;
+		if (_historyPos == _historyPresent) {
+			len = _numChars - _inFence;
+			if (len > 0) {
+				cx = new glui32[len + 1];
+				memcpy(cx, &(_chars[_inFence]), len * 4);
+				cx[len] = 0;
+			} else {
+				cx = nullptr;
+			}
+			if (_history[_historyPos])
+				free(_history[_historyPos]);
+			_history[_historyPos] = cx;
+		}
+		_historyPos--;
+		if (_historyPos < 0)
+			_historyPos += HISTORYLEN;
+		cx = _history[_historyPos];
+		putTextUni(cx, cx ? strlen_uni(cx) : 0, _inFence,
+			_numChars - _inFence);
+		break;
+
+	case keycode_Down:
+		if (_historyPos == _historyPresent)
+			return;
+		_historyPos++;
+		if (_historyPos >= HISTORYLEN)
+			_historyPos -= HISTORYLEN;
+		cx = _history[_historyPos];
+		putTextUni(cx, cx ? strlen_uni(cx) : 0, _inFence,
+			_numChars - _inFence);
+		break;
+
+		/* Cursor movement keys, during line input. */
+
+	case keycode_Left:
+		if (_inCurs <= _inFence)
+			return;
+		_inCurs--;
+		break;
+
+	case keycode_Right:
+		if (_inCurs >= _numChars)
+			return;
+		_inCurs++;
+		break;
+
+	case keycode_Home:
+		if (_inCurs <= _inFence)
+			return;
+		_inCurs = _inFence;
+		break;
+
+	case keycode_End:
+		if (_inCurs >= _numChars)
+			return;
+		_inCurs = _numChars;
+		break;
+
+	case keycode_SkipWordLeft:
+		while (_inCurs > _inFence && _chars[_inCurs - 1] == ' ')
+			_inCurs--;
+		while (_inCurs > _inFence && _chars[_inCurs - 1] != ' ')
+			_inCurs--;
+		break;
+
+	case keycode_SkipWordRight:
+		while (_inCurs < _numChars && _chars[_inCurs] != ' ')
+			_inCurs++;
+		while (_inCurs < _numChars && _chars[_inCurs] == ' ')
+			_inCurs++;
+		break;
+
+		/* Delete keys, during line input. */
+
+	case keycode_Delete:
+		if (_inCurs <= _inFence)
+			return;
+		putTextUni(nullptr, 0, _inCurs - 1, 1);
+		break;
+
+	case keycode_Erase:
+		if (_inCurs >= _numChars)
+			return;
+		putTextUni(nullptr, 0, _inCurs, 1);
+		break;
+
+	case keycode_Escape:
+		if (_inFence >= _numChars)
+			return;
+		putTextUni(nullptr, 0, _inFence, _numChars - _inFence);
+		break;
+
+		/* Regular keys */
+
+	case keycode_Return:
+		acceptLine(arg);
+		break;
+
+	default:
+		if (arg >= 32 && arg <= 0x10FFFF)
+		{
+			if (g_conf->_caps && (arg > 0x60 && arg < 0x7b))
+				arg -= 0x20;
+			putTextUni(&arg, 1, _inCurs, 0);
+		}
+		break;
+	}
 
-TextBufferWindow::TextBufferRow::TextBufferRow() : len(0), newline(0), dirty(false), repaint(false),
-		lpic(nullptr), rpic(nullptr), lhyper(0), rhyper(0), lm(0), rm(0) {
+	touch(0);
 }
 
-void TextBufferWindow::TextBufferRow::resize(size_t newSize) {
-	chars.clear();
-	attr.clear();
-	chars.resize(newSize);
-	attr.resize(newSize);
-	Common::fill(&chars[0], &chars[0] + newSize, ' ');
+/* Return or enter, during line input. Ends line input. */
+void TextBufferWindow::acceptLine(glui32 keycode) {
+	int ix;
+	int len, olen;
+	void *inbuf;
+	glui32 *s, *o;
+	int inmax;
+	gidispatch_rock_t inarrayrock;
+	int unicode = _lineRequestUni;
+
+	if (!_inBuf)
+		return;
+
+	inbuf = _inBuf;
+	inmax = _inMax;
+	inarrayrock = _inArrayRock;
+
+	len = _numChars - _inFence;
+	if (_echoStream)
+		_echoStream->echoLineUni(_chars + _inFence, len);
+
+	gli_tts_purge();
+	if (g_conf->_speakInput) {
+		const uint32 NEWLINE = '\n';
+		gli_tts_speak(_chars + _inFence, len);
+		gli_tts_speak((const glui32 *)&NEWLINE, 1);
+	}
+
+	/*
+	* Store in history.
+	* The history is a ring buffer, with historypresent being the index of the most recent
+	* element and historyfirst the index of the oldest element.
+	* A history entry should not repeat the string from the entry before it.
+	*/
+	if (len) {
+		s = new glui32[len + 1];
+		memcpy(s, _chars + _inFence, len * sizeof(glui32));
+		s[len] = 0;
+
+		free(_history[_historyPresent]);
+		_history[_historyPresent] = nullptr;
+
+		o = _history[(_historyPresent == 0 ? HISTORYLEN : _historyPresent) - 1];
+		olen = o ? strlen_uni(o) : 0;
+
+		if (len != olen || memcmp(s, o, olen * sizeof(glui32))) {
+			_history[_historyPresent] = s;
+
+			_historyPresent++;
+			if (_historyPresent == HISTORYLEN)
+				_historyPresent = 0;
+
+			if (_historyPresent == _historyFirst) {
+				_historyFirst++;
+				if (_historyFirst == HISTORYLEN)
+					_historyFirst = 0;
+			}
+		} else {
+			free(s);
+		}
+	}
+
+	/* Store in event buffer. */
+
+	if (len > inmax)
+		len = inmax;
+
+	if (!unicode) {
+		for (ix = 0; ix<len; ix++) {
+			glui32 ch = _chars[_inFence + ix];
+			if (ch > 0xff)
+				ch = '?';
+			((char *)inbuf)[ix] = (char)ch;
+		}
+	} else {
+		for (ix = 0; ix<len; ix++)
+			((glui32 *)inbuf)[ix] = _chars[_inFence + ix];
+	}
+
+	_attr = _origAttr;
+
+	if (_lineTerminators) {
+		glui32 val2 = keycode;
+		if (val2 == keycode_Return)
+			val2 = 0;
+		g_vm->_events->eventStore(evtype_LineInput, this, len, val2);
+		free(_lineTerminators);
+		_lineTerminators = nullptr;
+	} else {
+		g_vm->_events->eventStore(evtype_LineInput, this, len, 0);
+	}
+
+	_lineRequest = false;
+	_lineRequestUni = false;
+	_inBuf = nullptr;
+	_inMax = 0;
+
+	if (_echoLineInput) {
+		putCharUni('\n');
+	} else {
+		_numChars = _inFence;
+		touch(0);
+	}
+
+	if (g_vm->gli_unregister_arr)
+		(*g_vm->gli_unregister_arr)(inbuf, inmax, unicode ? "&+#!Iu" : "&+#!Cn", inarrayrock);
+}
+
+bool TextBufferWindow::leftquote(glui32 c) {
+	switch (c) {
+	case '(': case '[':
+
+		/* The following are Unicode characters in the "Separator, Space" category. */
+	case 0x0020: case 0x00a0: case 0x1680: case 0x2000:
+	case 0x2001: case 0x2002: case 0x2003: case 0x2004:
+	case 0x2005: case 0x2006: case 0x2007: case 0x2008:
+	case 0x2009: case 0x200a: case 0x202f: case 0x205f:
+	case 0x3000:
+		return true;
+	default:
+		return false;
+	}
+}
+
+void TextBufferWindow::scrollOneLine(bool forced) {
+	_lastSeen++;
+	_scrollMax++;
+
+	if (_scrollMax > _scrollBack - 1
+		|| _lastSeen > _scrollBack - 1)
+		scrollResize();
+
+	if (_lastSeen >= _height)
+		_scrollPos++;
+
+	if (_scrollPos > _scrollMax - _height + 1)
+		_scrollPos = _scrollMax - _height + 1;
+	if (_scrollPos < 0)
+		_scrollPos = 0;
+
+	if (forced)
+		_dashed = 0;
+	_spaced = 0;
+
+	_lines[0]._len = _numChars;
+	_lines[0]._newLine = forced;
+
+	for (int i = _scrollBack - 1; i > 0; i--) {
+		memcpy(&_lines[i], &_lines[i - 1], sizeof(TextBufferRow));
+		if (i < _height)
+			touch(i);
+	}
+
+	if (_radjn)
+		_radjn--;
+	if (_radjn == 0)
+		_radjw = 0;
+	if (_ladjn)
+		_ladjn--;
+	if (_ladjn == 0)
+		_ladjw = 0;
+
+	touch(0);
+	_lines[0]._len = 0;
+	_lines[0]._newLine = 0;
+	_lines[0]._lm = _ladjw;
+	_lines[0]._rm = _radjw;
+	_lines[0]._lPic = nullptr;
+	_lines[0]._rPic = nullptr;
+	_lines[0]._lHyper = 0;
+	_lines[0]._rHyper = 0;
+	memset(_chars, ' ', TBLINELEN * 4);
+	memset(_attrs, 0, TBLINELEN * sizeof(Attributes));
+
+	_numChars = 0;
+
+	touchScroll();
+
+}
+
+void TextBufferWindow::scrollResize() {
+	int i;
+
+	_lines.clear();
+	_lines.resize(_scrollBack + SCROLLBACK);
+
+	_chars = _lines[0]._chars;
+	_attrs = _lines[0]._attrs;
+
+	for (i = _scrollBack; i < (_scrollBack + SCROLLBACK); i++)
+	{
+		_lines[i]._dirty = 0;
+		_lines[i]._repaint = 0;
+		_lines[i]._lm = 0;
+		_lines[i]._rm = 0;
+		_lines[i]._lPic = 0;
+		_lines[i]._rPic = 0;
+		_lines[i]._lHyper = 0;
+		_lines[i]._rHyper = 0;
+		_lines[i]._len = 0;
+		_lines[i]._newLine = 0;
+		memset(_lines[i]._chars, ' ', sizeof _lines[i]._chars);
+		memset(_lines[i]._attrs, 0, sizeof _lines[i]._attrs);
+	}
+
+	_scrollBack += SCROLLBACK;
+}
+
+int TextBufferWindow::calcWidth(glui32 *chars, Attributes *attrs, int startchar,
+		int numChars, int spw) {
+	int w = 0;
+	int a, b;
+
+	a = startchar;
+	for (b = startchar; b < numChars; b++) {
+		if (attrs[a] == attrs[b]) {
+			w += stringWidthUni(attrs[a].attrFont(_styles),
+				chars + a, b - a, spw);
+			a = b;
+		}
+	}
+
+	w += stringWidthUni(attrs[a].attrFont(_styles),
+		chars + a, b - a, spw);
+
+	return w;
+}
+
+void TextBufferWindow::copyTextToClipboard(const glui32 *text, size_t len) {
+	// TODO
+}
+
+/*--------------------------------------------------------------------------*/
+
+TextBufferWindow::TextBufferRow::TextBufferRow() : _len(0), _newLine(0), _dirty(false),
+		_repaint(false), _lPic(nullptr), _rPic(nullptr), _lHyper(0), _rHyper(0),
+		_lm(0), _rm(0) {
+	Common::fill(&_chars[0], &_chars[TBLINELEN], 0);
 }
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index ebba2b4..2a1c1f7 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -25,47 +25,72 @@
 
 #include "gargoyle/windows.h"
 #include "gargoyle/picture.h"
+#include "gargoyle/speech.h"
 
 namespace Gargoyle {
 
 /**
  * Text Buffer window
  */
-class TextBufferWindow : public Window {
+class TextBufferWindow : public Window, Speech {
 	/**
 	 * Structure for a row within the window
 	 */
 	struct TextBufferRow {
-		Common::Array<uint32> chars;
-		Common::Array<Attributes> attr;
-		int len, newline;
-		bool dirty, repaint;
-		Picture *lpic, *rpic;
-		glui32 lhyper, rhyper;
-		int lm, rm;
+		glui32 _chars[TBLINELEN];
+		Attributes _attrs[TBLINELEN];
+		int _len, _newLine;
+		bool _dirty, _repaint;
+		Picture *_lPic, *_rPic;
+		glui32 _lHyper, _rHyper;
+		int _lm, _rm;
 
 		/**
 		 * Constructor
 		 */
 		TextBufferRow();
-
-		/**
-		 * Resize the row
-		 */
-		void resize(size_t newSize);
 	};
 	typedef Common::Array<TextBufferRow> TextBufferRows;
 private:
 	void reflow();
 	void touchScroll();
 	bool putPicture(Picture *pic, glui32 align, glui32 linkval);
+
+	/**
+	 * @remarks Only for input text
+	 */
+	void putText(const char *buf, int len, int pos, int oldlen);
+
+	/**
+	 * @remarks Only for input text
+	 */
 	void putTextUni(const glui32 *buf, int len, int pos, int oldlen);
-	void flowBreak();
+
+	bool flowBreak();
+
+	void acceptLine(glui32 keycode);
+
+	/**
+	 * Return true if a following quotation mark should be an opening mark,
+	 * false if it should be a closing mark. Opening quotation marks will
+	 * appear following an open parenthesis, open square bracket, or
+	 * whitespace.
+	 */
+	bool leftquote(glui32 c);
 
 	/**
 	 * Mark a given text row as modified
 	 */
 	void touch(int line);
+
+	void scrollOneLine(bool forced);
+	void scrollResize();
+	int calcWidth(glui32 *chars, Attributes *attrs, int startchar, int numchars, int spw);
+
+	/**
+	 * Copy the passed text to the clipboard
+	 */
+	void copyTextToClipboard(const glui32 *text, size_t len);
 public:
 	int _width, _height;
 	int _spaced;
@@ -106,7 +131,7 @@ public:
 	glui32 *_lineTerminators;
 
 	/* style hints and settings */
-	WindowStyle styles[style_NUMSTYLES];
+	WindowStyle _styles[style_NUMSTYLES];
 
 	/* for copy selection */
 	glui32 *_copyBuf;
@@ -143,14 +168,14 @@ public:
 	virtual bool unputCharUni(uint32 ch) override;
 
 	/**
-	 * Move the cursor
+	 * Clear the window
 	 */
-	virtual void moveCursor(const Common::Point &newPos) override;
+	virtual void clear() override;
 
 	/**
-	 * Clear the window
+	 * Click the window
 	 */
-	virtual void clear() override;
+	virtual void click(const Common::Point &newPos) override;
 
 	/**
 	 * Prepare for inputing a line
@@ -176,6 +201,15 @@ public:
 	 * Redraw the window
 	 */
 	virtual void redraw() override;
+
+	virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0,
+		glui32 height = 0) override;
+
+	virtual void acceptReadLine(glui32 arg) override;
+
+	virtual void acceptReadChar(glui32 arg) override;
+
+	int acceptScroll(glui32 arg);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 17bb222..91331d7 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -23,6 +23,7 @@
 #include "gargoyle/window_text_grid.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/window_mask.h"
 
 namespace Gargoyle {
 
@@ -203,7 +204,7 @@ void TextGridWindow::click(const Common::Point &newPos) {
 	}
 
 	if (_hyperRequest) {
-		glui32 linkval = _windows->getHyperlink(newPos);
+		glui32 linkval = g_vm->_windowMask->getHyperlink(newPos);
 		if (linkval)
 		{
 			g_vm->_events->eventStore(evtype_Hyperlink, this, linkval, 0);
@@ -588,7 +589,7 @@ void TextGridWindow::redraw() {
 			y = y0 + i * g_conf->_leading;
 
 			/* clear any stored hyperlink coordinates */
-			_windows->setHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
+			g_vm->_windowMask->putHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
 
 			a = 0;
 			for (b = 0; b < _width; b++) {
@@ -610,7 +611,7 @@ void TextGridWindow::redraw() {
 					if (link) {
 						_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
 							g_conf->_linkStyle, g_conf->_linkColor);
-						_windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading);
+						g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 					}
 					x += w;
 					a = b;
@@ -634,7 +635,7 @@ void TextGridWindow::redraw() {
 			if (link) {
 				_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
 					g_conf->_linkStyle, g_conf->_linkColor);
-				_windows->setHyperlink(link, x, y, x + w, y + g_conf->_leading);
+				g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 			}
 		}
 	}
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 0cf6d5e..2c5a0a8 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -56,17 +56,10 @@ private:
 	 */
 	void touch(int line);
 
-	void acceptReadChar(glui32 arg);
-
 	/**
 	 * Return or enter, during line input. Ends line input. 
 	 */
 	void acceptLine(glui32 keycode);
-
-	/**
-	 * Any regular key, during line input.
-	 */
-	void acceptReadLine(glui32 arg);
 public:
 	int _width, _height;
 	TextGridRows _lines;
@@ -158,6 +151,10 @@ public:
 	 * Redraw the window
 	 */
 	virtual void redraw() override;
+
+	virtual void acceptReadLine(glui32 arg) override;
+
+	virtual void acceptReadChar(glui32 arg) override;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index cf9b7de..c1c28e5 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -37,6 +37,8 @@ bool Windows::_overrideReverse;
 bool Windows::_overrideFgSet;
 bool Windows::_overrideBgSet;
 bool Windows::_forceRedraw;
+bool Windows::_claimSelect;
+bool Windows::_moreFocus;
 int Windows::_overrideFgVal;
 int Windows::_overrideBgVal;
 int Windows::_zcolor_fg;
@@ -48,14 +50,15 @@ byte Windows::_zcolor_Bright[3];
 
 /*--------------------------------------------------------------------------*/
 
-Windows::Windows(Graphics::Screen *screen) :
-		_screen(screen), _moreFocus(false), _windowList(nullptr),
-		_rootWin(nullptr), _focusWin(nullptr), _mask(nullptr), _claimSelect(0) {
+Windows::Windows(Graphics::Screen *screen) : _screen(screen), _windowList(nullptr),
+		_rootWin(nullptr), _focusWin(nullptr), _mask(nullptr) {
 	_mask = new WindowMask();
 	_overrideReverse = false;
 	_overrideFgSet = false;
 	_overrideBgSet = false;
 	_forceRedraw = true;
+	_claimSelect = false;
+	_moreFocus = false;
 	_overrideFgVal = 0;
 	_overrideBgVal = 0;
 	_zcolor_fg = _zcolor_bg = 0;
@@ -234,6 +237,10 @@ void Windows::redraw() {
 	// TODO: gli_windows_redraw
 }
 
+void Windows::redrawRect(const Common::Rect &r) {
+	// TODO: gli_redraw_rect
+}
+
 void Windows::repaint(const Common::Rect &box) {
 	// TODO
 }
@@ -291,7 +298,7 @@ Window::Window(Windows *windows, glui32 rock) : _windows(windows), _rock(rock),
 		_type(0), _parent(nullptr), _next(nullptr), _prev(nullptr), _yAdj(0),
 		_lineRequest(0), _lineRequestUni(0), _charRequest(0), _charRequestUni(0),
 		_mouseRequest(0), _hyperRequest(0), _moreRequest(0), _scrollRequest(0), _imageLoaded(0),
-		_echoLineInput(true), _lineTerminatorsBase(nullptr), _termCt(0), _echoStream(nullptr) {
+		_echoLineInputBase(true), _lineTerminatorsBase(nullptr), _termCt(0), _echoStream(nullptr) {
 	_attr.fgset = 0;
 	_attr.bgset = 0;
 	_attr.reverse = 0;
@@ -337,6 +344,10 @@ void Window::cancelLineEvent(Event *ev) {
 	g_vm->_events->clearEvent(ev);
 }
 
+void Window::moveCursor(const Common::Point &newPos) {
+	warning("moveCursor: not a TextGrid window");
+}
+
 void Window::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) {
 	warning("requestLineEvent: window does not support keyboard input");
 }
@@ -353,6 +364,14 @@ void Window::redraw() {
 	}
 }
 
+void Window::acceptReadLine(glui32 arg) {
+	warning("acceptReadLine:: window does not support keyboard input");
+}
+
+void Window::acceptReadChar(glui32 arg) {
+	warning("acceptReadChar:: window does not support keyboard input");
+}
+
 bool Window::checkTerminator(glui32 ch) {
 	if (ch == keycode_Escape)
 		return true;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 5e6374c..3f5c0bc 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -85,8 +85,6 @@ private:
 	Window * _windowList;      ///< List of all windows
 	Window *_rootWin;          ///< The topmost window
 	Window *_focusWin;         ///< The window selected by the player
-	bool _moreFocus;
-	bool _claimSelect;
 	WindowMask *_mask;
 private:
 	/**
@@ -108,6 +106,8 @@ public:
 	static bool _overrideFgSet;
 	static bool _overrideBgSet;
 	static bool _forceRedraw;
+	static bool _claimSelect;
+	static bool _moreFocus;
 	static int _overrideFgVal;
 	static int _overrideBgVal;
 	static int _zcolor_fg, _zcolor_bg;
@@ -157,6 +157,8 @@ public:
 
 	void redraw();
 
+	void redrawRect(const Common::Rect &r);
+
 	/**
 	 * Repaint an area of the windows
 	 */
@@ -176,18 +178,6 @@ public:
 	 * Returns the end point of window iteration
 	 */
 	iterator end() { return iterator(nullptr); }
-
-	/**
-	 * Gets a hyperlink
-	 */
-	glui32 getHyperlink(const Common::Point &pos) { return _mask->getHyperlink(pos); }
-
-	/**
-	 * Sets a hyperlink
-	 */
-	void setHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1) {
-		return _mask->putHyperlink(linkval, x0, y0, x1, y1);
-	}
 };
 
 /**
@@ -278,7 +268,7 @@ public:
 	int _scrollRequest;
 	int _imageLoaded;
 
-	glui32 _echoLineInput;
+	glui32 _echoLineInputBase;
 	glui32 *_lineTerminatorsBase;
 	glui32 _termCt;
 
@@ -323,7 +313,7 @@ public:
 	/**
 	 * Move the cursor
 	 */
-	virtual void moveCursor(const Common::Point &newPos) {}
+	virtual void moveCursor(const Common::Point &newPos);
 
 	/**
 	 * Clear the window
@@ -364,6 +354,15 @@ public:
 	 * Redraw the window
 	 */
 	virtual void redraw();
+
+	virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0,
+		glui32 height = 0) { return false; }
+
+	virtual void acceptReadLine(glui32 arg);
+
+	virtual void acceptReadChar(glui32 arg);
+
+	int acceptScroll(glui32 arg);
 };
 typedef Window *winid_t;
 


Commit: 4ddda5cebc3d0f11cc10746e4dcac5bd0e1a7b19
    https://github.com/scummvm/scummvm/commit/4ddda5cebc3d0f11cc10746e4dcac5bd0e1a7b19
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Windows focus / iteration

Changed paths:
    engines/gargoyle/draw.cpp
    engines/gargoyle/draw.h
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_pair.cpp
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/draw.cpp b/engines/gargoyle/draw.cpp
index f16d1a5..64c9a10 100644
--- a/engines/gargoyle/draw.cpp
+++ b/engines/gargoyle/draw.cpp
@@ -48,4 +48,8 @@ void Draw::drawCaret(const Common::Point &pos) {
 	// TODO
 }
 
+void Draw::fillArea(const byte *rgb) {
+	// TODO: gli_draw_clear
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/draw.h b/engines/gargoyle/draw.h
index e0ec133..f60d573 100644
--- a/engines/gargoyle/draw.h
+++ b/engines/gargoyle/draw.h
@@ -39,6 +39,8 @@ protected:
 	int stringWidthUni(int fidx, const glui32 *s, int n, int spw);
 
 	void drawCaret(const Common::Point &pos);
+
+	void fillArea(const byte *rgb);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index 732686d..feddf4e 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -83,6 +83,8 @@ void GraphicsWindow::touch() {
 }
 
 void GraphicsWindow::redraw() {
+	Window::redraw();
+
 	// TODO
 }
 
diff --git a/engines/gargoyle/window_pair.cpp b/engines/gargoyle/window_pair.cpp
index af3ed4c..7a11597 100644
--- a/engines/gargoyle/window_pair.cpp
+++ b/engines/gargoyle/window_pair.cpp
@@ -119,6 +119,8 @@ void PairWindow::rearrange(const Common::Rect &box) {
 }
 
 void PairWindow::redraw() {
+	Window::redraw();
+
 	// TODO
 }
 
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 3df2e5c..99bef25 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -228,7 +228,7 @@ void TextBufferWindow::reflow() {
 }
 
 void TextBufferWindow::touchScroll() {
-	_windows->clearSelection();
+	g_vm->_windowMask->clearSelection();
 	_windows->repaint(_bbox);
 
 	for (int i = 0; i < _scrollMax; i++)
@@ -370,7 +370,7 @@ void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldle
 void TextBufferWindow::touch(int line) {
 	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
 	_lines[line]._dirty = 1;
-	_windows->clearSelection();
+	g_vm->_windowMask->clearSelection();
 	_windows->repaint(Common::Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
 }
 
@@ -796,7 +796,10 @@ void TextBufferWindow::redraw() {
     int selbuf, selrow, selchar, sx0, sx1, selleft, selright;
     int tx, tsc, tsw, lsc, rsc;
 
+	Window::redraw();
+
     _lines[0]._len = _numChars;
+	sx0 = sx1 = selleft = selright = 0;
 
 	ln = new TextBufferRow();
     if (!ln)
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 91331d7..c20bafe 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -577,6 +577,8 @@ void TextGridWindow::redraw() {
 	int font;
 	byte *fgcolor, *bgcolor;
 
+	Window::redraw();
+
 	x0 = _bbox.left;
 	y0 = _bbox.top;
 
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index c1c28e5..9c8e22c 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -51,8 +51,7 @@ byte Windows::_zcolor_Bright[3];
 /*--------------------------------------------------------------------------*/
 
 Windows::Windows(Graphics::Screen *screen) : _screen(screen), _windowList(nullptr),
-		_rootWin(nullptr), _focusWin(nullptr), _mask(nullptr) {
-	_mask = new WindowMask();
+		_rootWin(nullptr), _focusWin(nullptr) {
 	_overrideReverse = false;
 	_overrideFgSet = false;
 	_overrideBgSet = false;
@@ -62,6 +61,7 @@ Windows::Windows(Graphics::Screen *screen) : _screen(screen), _windowList(nullpt
 	_overrideFgVal = 0;
 	_overrideBgVal = 0;
 	_zcolor_fg = _zcolor_bg = 0;
+	_drawSelect = false;
 
 	_zcolor_LightGrey[0] = _zcolor_LightGrey[1] = _zcolor_LightGrey[2] = 181;
 	_zcolor_Foreground[0] = _zcolor_Foreground[1] = _zcolor_Foreground[2] = 0;
@@ -69,10 +69,6 @@ Windows::Windows(Graphics::Screen *screen) : _screen(screen), _windowList(nullpt
 	_zcolor_Bright[0] = _zcolor_Bright[1] = _zcolor_Bright[2] = 0;
 }
 
-Windows::~Windows() {
-	delete _mask;
-}
-
 Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 		glui32 wintype, glui32 rock) {
 	Window *newwin, *oldparent;
@@ -229,20 +225,30 @@ void Windows::selectionChanged() {
 	redraw();
 }
 
-void Windows::clearSelection() {
-	_mask->clearSelection();
-}
-
 void Windows::redraw() {
-	// TODO: gli_windows_redraw
+	_claimSelect = false;
+
+	if (_forceRedraw) {
+		repaint(Common::Rect(0, 0, g_conf->_imageW, g_conf->_imageH));
+		fillArea(g_conf->_windowColor);
+	}
+
+	if (_rootWin)
+		_rootWin->redraw();
+
+	if (_moreFocus)
+		refocus(_focusWin);
+
+	_forceRedraw = 0;
 }
 
 void Windows::redrawRect(const Common::Rect &r) {
-	// TODO: gli_redraw_rect
+	_drawSelect = true;
+	repaint(r);
 }
 
 void Windows::repaint(const Common::Rect &box) {
-	// TODO
+	// No implementation
 }
 
 void Windows::drawRect(int x0, int y0, int w, int h, const byte *rgb) {
@@ -260,36 +266,52 @@ byte *Windows::rgbShift(byte *rgb) {
 /*--------------------------------------------------------------------------*/
 
 Windows::iterator &Windows::iterator::operator++() {
-	if (!_current)
-		return *this;
+	_current = _windows->iterateTreeOrder(_current);
+	return *this;
+}
+
+void Windows::refocus(Window *win) {
+	Window *focus = win;
+	do {
+		if (focus && focus->_moreRequest) {
+			_focusWin = focus;
+			return;
+		}
 
-	PairWindow *pairWin = dynamic_cast<PairWindow *>(_current);
+		focus = iterateTreeOrder(focus);
+	} while (focus != win);
+
+	_moreFocus = false;
+}
 
+Window *Windows::iterateTreeOrder(Window *win) {
+	if (!win)
+		return _rootWin;
+
+	PairWindow *pairWin = dynamic_cast<PairWindow *>(win);
 	if (pairWin) {
-		_current = !pairWin->_backward ? pairWin->_child1 : pairWin->_child2;
+		if (!pairWin->_backward)
+			return pairWin->_child1;
+		else
+			return pairWin->_child2;
 	} else {
-		while (_current->_parent) {
-			pairWin = dynamic_cast<PairWindow *>(_current->_parent);
+		while (win->_parent) {
+			pairWin = dynamic_cast<PairWindow *>(win->_parent);
+			assert(pairWin);
 
 			if (!pairWin->_backward) {
-				if (_current == pairWin->_child1) {
-					_current = pairWin->_child2;
-					return *this;
-				}
+				if (win == pairWin->_child1)
+					return pairWin->_child2;
 			} else {
-				if (_current == pairWin->_child2) {
-					_current = pairWin->_child1;
-					return *this;
-				}
+				if (win == pairWin->_child2)
+					return pairWin->_child1;
 			}
 
-			_current = pairWin;
+			win = pairWin;
 		}
 
-		_current = nullptr;
+		return nullptr;
 	}
-
-	return *this;
 }
 
 /*--------------------------------------------------------------------------*/
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 3f5c0bc..1f15720 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -48,17 +48,18 @@ class PairWindow;
 /**
  * Main windows manager
  */
-class Windows {
+class Windows : public Draw {
 	friend class Window;
 public:
 	class iterator {
 	private:
+		Windows *_windows;
 		Window *_current;
 	public:
 		/**
 		 * Constructor
 		 */
-		iterator(Window *start) : _current(start) {}
+		iterator(Windows *windows, Window *start) : _windows(windows), _current(start) {}
 
 		/**
 		 * Dereference
@@ -80,12 +81,13 @@ public:
 		 */
 		bool operator!=(const iterator &i) { return _current != i._current; }
 	};
+	friend class iterator;
 private:
 	Graphics::Screen *_screen;
 	Window * _windowList;      ///< List of all windows
 	Window *_rootWin;          ///< The topmost window
 	Window *_focusWin;         ///< The window selected by the player
-	WindowMask *_mask;
+	bool _drawSelect;
 private:
 	/**
 	 * Create a new window
@@ -101,6 +103,10 @@ private:
 	 * Rearrange windows
 	 */
 	void rearrange();
+
+	void refocus(Window *win);
+
+	Window *iterateTreeOrder(Window *win);
 public:
 	static bool _overrideReverse;
 	static bool _overrideFgSet;
@@ -124,11 +130,6 @@ public:
 	Windows(Graphics::Screen *screen);
 
 	/**
-	 * Destructor
-	 */
-	~Windows();
-
-	/**
 	 * Open a new window
 	 */
 	Window *windowOpen(Window *splitwin, glui32 method, glui32 size,
@@ -149,8 +150,6 @@ public:
 	 */
 	void setFocus(Window *win) { _focusWin = win; }
 
-	void clearSelection();
-
 	void selectionChanged();
 
 	void clearClaimSelect() { _claimSelect = false; }
@@ -172,12 +171,12 @@ public:
 	/**
 	 * Get an iterator that will move over the tree
 	 */
-	iterator begin() { return iterator(_windowList); }
+	iterator begin() { return iterator(this, _windowList); }
 
 	/**
 	 * Returns the end point of window iteration
 	 */
-	iterator end() { return iterator(nullptr); }
+	iterator end() { return iterator(this, nullptr); }
 };
 
 /**


Commit: 6939fabbfb9ce63c10f1fcd8cd3c0b7f66ff9bb9
    https://github.com/scummvm/scummvm/commit/6939fabbfb9ce63c10f1fcd8cd3c0b7f66ff9bb9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fleshed out graphics window

Changed paths:
    engines/gargoyle/draw.cpp
    engines/gargoyle/draw.h
    engines/gargoyle/gargoyle.h
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_graphics.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/draw.cpp b/engines/gargoyle/draw.cpp
index 64c9a10..59acb96 100644
--- a/engines/gargoyle/draw.cpp
+++ b/engines/gargoyle/draw.cpp
@@ -52,4 +52,8 @@ void Draw::fillArea(const byte *rgb) {
 	// TODO: gli_draw_clear
 }
 
+void Draw::drawRect(int x0, int y0, int w, int h, const byte *rgb) {
+	// TODO: gli_draw_rect
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/draw.h b/engines/gargoyle/draw.h
index f60d573..7b74f45 100644
--- a/engines/gargoyle/draw.h
+++ b/engines/gargoyle/draw.h
@@ -41,6 +41,8 @@ protected:
 	void drawCaret(const Common::Point &pos);
 
 	void fillArea(const byte *rgb);
+
+	void drawRect(int x0, int y0, int w, int h, const byte *rgb);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index b99b7b4..a8af85a 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -77,7 +77,6 @@ private:
 	void initialize();
 protected:
 	const GargoyleGameDescription *_gameDescription;
-	Graphics::Screen *_screen;
 	Common::RandomSource _random;
 	int _loadSaveSlot;
 
@@ -97,6 +96,7 @@ public:
 	Conf *_conf;
 	Events *_events;
 	PicList *_picList;
+	Graphics::Screen *_screen;
 	Streams *_streams;
 	Windows *_windows;
 	WindowMask *_windowMask;
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index feddf4e..bb002bd 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "gargoyle/window_graphics.h"
+#include "gargoyle/gargoyle.h"
 
 namespace Gargoyle {
 
@@ -85,12 +86,164 @@ void GraphicsWindow::touch() {
 void GraphicsWindow::redraw() {
 	Window::redraw();
 
-	// TODO
+	if (_dirty || Windows::_forceRedraw) {
+		_dirty = 0;
+
+		if (_surface)
+			g_vm->_screen->blitFrom(*_surface, Common::Point(_bbox.left, _bbox.top));
+	}
+}
+
+glui32 GraphicsWindow::drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int scale,
+		glui32 imagewidth, glui32 imageheight) {
+	Picture *pic = Picture::load(image);
+	glui32 hyperlink = _attr.hyper;
+
+	if (!pic)
+		return false;
+
+	if (!_imageLoaded) {
+		g_vm->_picList->increment();
+		_imageLoaded = true;
+	}
+
+	if (!scale) {
+		imagewidth = pic->w;
+		imageheight = pic->h;
+	}
+
+	drawPicture(pic, xpos, ypos, imagewidth, imageheight, hyperlink);
+	touch();
+
+	return true;
 }
 
-glui32 GraphicsWindow::imageDraw(glui32 image, glui32 align, bool scaled, glui32 width, glui32 height) {
-	// TODO: win_graphics_draw_picture
-	return 0;
+void GraphicsWindow::eraseRect(int whole, glsi32 x0, glsi32 y0, glui32 width, glui32 height) {
+	int x1 = x0 + width;
+	int y1 = y0 + height;
+	int x, y;
+	int hx0, hx1, hy0, hy1;
+
+	if (whole) {
+		x0 = 0;
+		y0 = 0;
+		x1 = _w;
+		y1 = _h;
+	}
+
+	if (x0 < 0) x0 = 0;
+	if (y0 < 0) y0 = 0;
+	if (x1 < 0) x1 = 0;
+	if (y1 < 0) y1 = 0;
+	if ((glui32)x0 >= _w) x0 = _w;
+	if ((glui32)y0 >= _h) y0 = _h;
+	if ((glui32)x1 >= _w) x1 = _w;
+	if ((glui32)y1 >= _h) y1 = _h;
+
+	hx0 = _bbox.left + x0;
+	hx1 = _bbox.left + x1;
+	hy0 = _bbox.top + y0;
+	hy1 = _bbox.top + y1;
+
+	/* zero out hyperlinks for these coordinates */
+	g_vm->_windowMask->putHyperlink(0, hx0, hy0, hx1, hy1);
+
+	_surface->fillRect(Common::Rect(x0, y0, x1, y1), MKTAG(_bgnd[0], _bgnd[1], _bgnd[2], 0));
+	touch();
+}
+
+void GraphicsWindow::fillRect(glui32 color, glsi32 x0, glsi32 y0, glui32 width, glui32 height) {
+	unsigned char col[3];
+	int x1 = x0 + width;
+	int y1 = y0 + height;
+	int x, y;
+	int hx0, hx1, hy0, hy1;
+
+	col[0] = (color >> 16) & 0xff;
+	col[1] = (color >> 8) & 0xff;
+	col[2] = (color >> 0) & 0xff;
+
+	if (x0 < 0) x0 = 0;
+	if (y0 < 0) y0 = 0;
+	if (x1 < 0) x1 = 0;
+	if (y1 < 0) y1 = 0;
+	if ((glui32)x0 > _w) x0 = _w;
+	if ((glui32)y0 > _h) y0 = _h;
+	if ((glui32)x1 > _w) x1 = _w;
+	if ((glui32)y1 > _h) y1 = _h;
+
+	hx0 = _bbox.left + x0;
+	hx1 = _bbox.left + x1;
+	hy0 = _bbox.top + y0;
+	hy1 = _bbox.top + y1;
+
+	/* zero out hyperlinks for these coordinates */
+	g_vm->_windowMask->putHyperlink(0, hx0, hy0, hx1, hy1);
+
+	_surface->fillRect(Common::Rect(x0, y0, x1, y1), MKTAG(col[0], col[1], col[2], 0));
+	touch();
+}
+
+void GraphicsWindow::setBackgroundColor(glui32 color) {
+	_bgnd[0] = (color >> 16) & 0xff;
+	_bgnd[1] = (color >> 8) & 0xff;
+	_bgnd[2] = (color >> 0) & 0xff;
+}
+
+void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int height, glui32 linkval) {
+	unsigned char *sp, *dp;
+	int dx1, dy1, x1, y1, sx0, sy0, sx1, sy1;
+	int x, y, w, h;
+	int hx0, hx1, hy0, hy1;
+
+	if (width != src->w || height != src->h)
+	{
+		src = src->scale(width, height);
+		if (!src)
+			return;
+	}
+
+	sx0 = 0;
+	sy0 = 0;
+	sx1 = src->w;
+	sy1 = src->h;
+	dx1 = _w;
+	dy1 = _h;
+
+	x1 = x0 + src->w;
+	y1 = y0 + src->h;
+
+	if (x1 <= 0 || x0 >= dx1) return;
+	if (y1 <= 0 || y0 >= dy1) return;
+	if (x0 < 0) {
+		sx0 -= x0;
+		x0 = 0;
+	}
+	if (y0 < 0) {
+		sy0 -= y0;
+		y0 = 0;
+	}
+	if (x1 > dx1) {
+		sx1 += dx1 - x1;
+		x1 = dx1;
+	}
+	if (y1 > dy1) {
+		sy1 += dy1 - y1;
+		y1 = dy1;
+	}
+
+	hx0 = _bbox.left + x0;
+	hx1 = _bbox.left + x1;
+	hy0 = _bbox.top + y0;
+	hy1 = _bbox.top + y1;
+
+	/* zero out or set hyperlink for these coordinates */
+	g_vm->_windowMask->putHyperlink(linkval, hx0, hy0, hx1, hy1);
+
+	w = sx1 - sx0;
+	h = sy1 - sy0;
+
+	_surface->blitFrom(*g_vm->_screen, Common::Rect(sx0, sy0, sx0 + w, sy0 + h), Common::Point(0, 0));
 }
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h
index b0f53fa..8e97b56 100644
--- a/engines/gargoyle/window_graphics.h
+++ b/engines/gargoyle/window_graphics.h
@@ -34,6 +34,11 @@ namespace Gargoyle {
 class GraphicsWindow : public Window {
 private:
 	void touch();
+
+	void eraseRect(int whole, glsi32 x0, glsi32 y0, glui32 width, glui32 height);
+	void fillRect(glui32 color, glsi32 x0, glsi32 y0, glui32 width, glui32 height);
+	void setBackgroundColor(glui32 color);
+	void drawPicture(Picture *src, int x0, int y0, int width, int height, glui32 linkval);
 public:
 	unsigned char _bgnd[3];
 	bool _dirty;
@@ -77,8 +82,8 @@ public:
 	 */
 	virtual void redraw() override;
 
-	virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0,
-		glui32 height = 0) override;
+	glui32 drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int scale,
+		glui32 imagewidth, glui32 imageheight);
 
 	/**
 	 * Get the window dimensions
@@ -87,6 +92,14 @@ public:
 		*w = _w;
 		*h = _h;
 	}
+
+	/**
+	 * Set the window dimensions
+	 */
+	void setSize(glui32 w, glui32 h) {
+		_w = w;
+		_h = h;
+	}
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 99bef25..6df3ac4 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -266,7 +266,7 @@ bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
 	return true;
 }
 
-glui32 TextBufferWindow::imageDraw(glui32 image, glui32 align, bool scaled, glui32 width, glui32 height) {
+glui32 TextBufferWindow::drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height) {
 	Picture *pic;
 	glui32 hyperlink;
 	int error;
@@ -953,7 +953,7 @@ void TextBufferWindow::redraw() {
          * fill in background colors
          */
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        _windows->drawRect(x0/GLI_SUBPIX, y,
+        drawRect(x0/GLI_SUBPIX, y,
                 (x1-x0) / GLI_SUBPIX, g_conf->_leading,
                 color);
 
@@ -966,11 +966,11 @@ void TextBufferWindow::redraw() {
                 font = ln->_attrs[a].attrFont(_styles);
                 color = ln->_attrs[a].attrBg(_styles);
                 w = stringWidthUni(font, ln->_chars + a, b - a, spw);
-                _windows->drawRect(x/GLI_SUBPIX, y,
+                drawRect(x/GLI_SUBPIX, y,
                         w/GLI_SUBPIX, g_conf->_leading,
                         color);
                 if (link) {
-                    _windows->drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+                    drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
                             w/GLI_SUBPIX + 1, g_conf->_linkStyle,
                             g_conf->_linkColor);
                     g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
@@ -985,10 +985,10 @@ void TextBufferWindow::redraw() {
         font = ln->_attrs[a].attrFont(_styles);
         color = ln->_attrs[a].attrBg(_styles);
         w = stringWidthUni(font, ln->_chars + a, b - a, spw);
-        _windows->drawRect(x/GLI_SUBPIX, y, w/GLI_SUBPIX,
+        drawRect(x/GLI_SUBPIX, y, w/GLI_SUBPIX,
                 g_conf->_leading, color);
         if (link) {
-            _windows->drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+            drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
                     w/GLI_SUBPIX + 1, g_conf->_linkStyle,
                     g_conf->_linkColor);
             g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
@@ -998,7 +998,7 @@ void TextBufferWindow::redraw() {
         x += w;
 
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        _windows->drawRect(x/GLI_SUBPIX, y,
+        drawRect(x/GLI_SUBPIX, y,
                 x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
                 color);
 
@@ -1048,7 +1048,7 @@ void TextBufferWindow::redraw() {
                 x1/GLI_SUBPIX, y + g_conf->_leading);
 
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        _windows->drawRect(x/GLI_SUBPIX, y,
+        drawRect(x/GLI_SUBPIX, y,
                 x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
                 color);
 
@@ -1142,14 +1142,14 @@ void TextBufferWindow::redraw() {
             t0 = t1 = y0;
         }
 
-        _windows->drawRect(x0+1, y0, x1-x0-2, y1-y0, g_conf->_scrollBg);
-        _windows->drawRect(x0+1, t0, x1-x0-2, t1-t0, g_conf->_scrollFg);
+        drawRect(x0+1, y0, x1-x0-2, y1-y0, g_conf->_scrollBg);
+        drawRect(x0+1, t0, x1-x0-2, t1-t0, g_conf->_scrollFg);
 
         for (i = 0; i < g_conf->_scrollWidth / 2 + 1; i++) {
-            _windows->drawRect(x0+g_conf->_scrollWidth/2-i,
+            drawRect(x0+g_conf->_scrollWidth/2-i,
                     y0 - g_conf->_scrollWidth/2 + i,
                     i*2, 1, g_conf->_scrollFg);
-            _windows->drawRect(x0+g_conf->_scrollWidth/2-i,
+            drawRect(x0+g_conf->_scrollWidth/2-i,
                     y1 + g_conf->_scrollWidth/2 - i,
                     i*2, 1, g_conf->_scrollFg);
         }
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index 2a1c1f7..4741877 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -202,14 +202,13 @@ public:
 	 */
 	virtual void redraw() override;
 
-	virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0,
-		glui32 height = 0) override;
-
 	virtual void acceptReadLine(glui32 arg) override;
 
 	virtual void acceptReadChar(glui32 arg) override;
 
 	int acceptScroll(glui32 arg);
+
+	glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index c20bafe..483d556 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -601,7 +601,7 @@ void TextGridWindow::redraw() {
 					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
 					bgcolor = ln->_attrs[a].attrBg(styles);
 					w = (b - a) * g_conf->_cellW;
-					_windows->drawRect(x, y, w, g_conf->_leading, bgcolor);
+					drawRect(x, y, w, g_conf->_leading, bgcolor);
 					o = x;
 
 					for (k = a; k < b; k++) {
@@ -611,7 +611,7 @@ void TextGridWindow::redraw() {
 						o += g_conf->_cellW;
 					}
 					if (link) {
-						_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
+						drawRect(x, y + g_conf->_baseLine + 1, w,
 							g_conf->_linkStyle, g_conf->_linkColor);
 						g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 					}
@@ -625,7 +625,7 @@ void TextGridWindow::redraw() {
 			bgcolor = ln->_attrs[a].attrBg(styles);
 			w = (b - a) * g_conf->_cellW;
 			w += _bbox.right - (x + w);
-			_windows->drawRect(x, y, w, g_conf->_leading, bgcolor);
+			drawRect(x, y, w, g_conf->_leading, bgcolor);
 
 			o = x;
 			for (k = a; k < b; k++) {
@@ -635,7 +635,7 @@ void TextGridWindow::redraw() {
 				o += g_conf->_cellW;
 			}
 			if (link) {
-				_windows->drawRect(x, y + g_conf->_baseLine + 1, w,
+				drawRect(x, y + g_conf->_baseLine + 1, w,
 					g_conf->_linkStyle, g_conf->_linkColor);
 				g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 			}
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 9c8e22c..0dc500e 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -251,10 +251,6 @@ void Windows::repaint(const Common::Rect &box) {
 	// No implementation
 }
 
-void Windows::drawRect(int x0, int y0, int w, int h, const byte *rgb) {
-	// TODO
-}
-
 byte *Windows::rgbShift(byte *rgb) {
 	_zcolor_Bright[0] = (rgb[0] + 0x30) < 0xff ? (rgb[0] + 0x30) : 0xff;
 	_zcolor_Bright[1] = (rgb[1] + 0x30) < 0xff ? (rgb[1] + 0x30) : 0xff;
@@ -403,6 +399,21 @@ bool Window::checkTerminator(glui32 ch) {
 		return false;
 }
 
+bool Window::imageDraw(glui32 image, glui32 align, glsi32 val1, glsi32 val2) {
+	if (!g_conf->_graphics)
+		return false;
+
+	TextBufferWindow *bufWin = dynamic_cast<TextBufferWindow *>(this);
+	GraphicsWindow *graWin = dynamic_cast<GraphicsWindow *>(this);
+
+	if (bufWin)
+		return bufWin->drawPicture(image, val1, false, 0, 0);
+	if (graWin)
+		return graWin->drawPicture(image, val1, val2, false, 0, 0);
+
+	return false;
+}
+
 /*--------------------------------------------------------------------------*/
 
 BlankWindow::BlankWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 1f15720..9092ea4 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -164,11 +164,6 @@ public:
 	void repaint(const Common::Rect &box);
 
 	/**
-	 * Draw an area of the windows
-	 */
-	void drawRect(int x0, int y0, int w, int h, const byte *rgb);
-
-	/**
 	 * Get an iterator that will move over the tree
 	 */
 	iterator begin() { return iterator(this, _windowList); }
@@ -354,8 +349,9 @@ public:
 	 */
 	virtual void redraw();
 
-	virtual glui32 imageDraw(glui32 image, glui32 align, bool scaled, glui32 width = 0,
-		glui32 height = 0) { return false; }
+	bool imageDraw(glui32 image, glui32 align, glsi32 val1, glsi32 val2);
+
+	virtual  glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height) { return false; }
 
 	virtual void acceptReadLine(glui32 arg);
 


Commit: fdfb1a55028400c976a7b0aa2df2e8cee0428b7d
    https://github.com/scummvm/scummvm/commit/fdfb1a55028400c976a7b0aa2df2e8cee0428b7d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Move Draw class into new Screen class

Changed paths:
  A engines/gargoyle/screen.cpp
  A engines/gargoyle/screen.h
  R engines/gargoyle/draw.cpp
  R engines/gargoyle/draw.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/module.mk
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/draw.cpp b/engines/gargoyle/draw.cpp
deleted file mode 100644
index 59acb96..0000000
--- a/engines/gargoyle/draw.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/draw.h"
-
-namespace Gargoyle {
-
-int Draw::drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw) {
-	// TODO
-	return 0;
-}
-
-int Draw::drawStringUni(int x, int y, int fidx, const byte *rgb, const glui32 *s, int n, int spw) {
-	// TODO
-	return 0;
-}
-
-int Draw::stringWidth(int fidx, const char *s, int n, int spw) {
-	// TODO
-	return 0;
-}
-
-int Draw::stringWidthUni(int fidx, const glui32 *s, int n, int spw) {
-	// TODO
-	return 0;
-}
-
-void Draw::drawCaret(const Common::Point &pos) {
-	// TODO
-}
-
-void Draw::fillArea(const byte *rgb) {
-	// TODO: gli_draw_clear
-}
-
-void Draw::drawRect(int x0, int y0, int w, int h, const byte *rgb) {
-	// TODO: gli_draw_rect
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/draw.h b/engines/gargoyle/draw.h
deleted file mode 100644
index 7b74f45..0000000
--- a/engines/gargoyle/draw.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_DRAW_H
-#define GARGOYLE_DRAW_H
-
-#include "common/rect.h"
-#include "gargoyle/glk_types.h"
-
-namespace Gargoyle {
-
-class Draw {
-protected:
-	int drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw);
-
-	int drawStringUni(int x, int y, int fidx, const byte *rgb, const glui32 *s, int n, int spw);
-
-	int stringWidth(int fidx, const char *s, int n, int spw);
-
-	int stringWidthUni(int fidx, const glui32 *s, int n, int spw);
-
-	void drawCaret(const Common::Point &pos);
-
-	void fillArea(const byte *rgb);
-
-	void drawRect(int x0, int y0, int w, int h, const byte *rgb);
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 37df6d1..1a8cc61 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -32,6 +32,7 @@
 #include "gargoyle/conf.h"
 #include "gargoyle/events.h"
 #include "gargoyle/picture.h"
+#include "gargoyle/screen.h"
 #include "gargoyle/streams.h"
 #include "gargoyle/windows.h"
 #include "gargoyle/window_mask.h"
@@ -66,7 +67,7 @@ void GargoyleEngine::initialize() {
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
 
 	initGraphics(640, 480, false);
-	_screen = new Graphics::Screen();
+	_screen = new Screen();
 	_conf = new Conf();
 	_events = new Events();
 	_picList = new PicList();
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index a8af85a..c34538b 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -29,7 +29,6 @@
 #include "common/serializer.h"
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
-#include "graphics/screen.h"
 #include "gargoyle/glk_types.h"
 
 namespace Gargoyle {
@@ -37,9 +36,10 @@ namespace Gargoyle {
 class Conf;
 class Events;
 class PicList;
+class Screen;
+class Streams;
 class Windows;
 class WindowMask;
-class Streams;
 
 enum InterpreterType {
 	INTERPRETER_SCOTT
@@ -96,7 +96,7 @@ public:
 	Conf *_conf;
 	Events *_events;
 	PicList *_picList;
-	Graphics::Screen *_screen;
+	Screen *_screen;
 	Streams *_streams;
 	Windows *_windows;
 	WindowMask *_windowMask;
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index e684ff9..e2584b4 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -3,12 +3,12 @@ MODULE := engines/gargoyle
 MODULE_OBJS := \
 	conf.o \
 	detection.o \
-	draw.o \
 	events.o \
 	fonts.o \
 	gargoyle.o \
 	glk.o \
 	picture.o \
+	screen.o \
 	streams.o \
 	string.o \
 	windows.o \
diff --git a/engines/gargoyle/screen.cpp b/engines/gargoyle/screen.cpp
new file mode 100644
index 0000000..dad3e6d
--- /dev/null
+++ b/engines/gargoyle/screen.cpp
@@ -0,0 +1,61 @@
+/* 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 "gargoyle/screen.h"
+
+namespace Gargoyle {
+
+void Screen::fill(const byte *rgb) {
+	uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+	clear(color);
+}
+
+void Screen::fillRect(uint x, uint y, uint w, uint h, const byte *rgb) {
+	uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+	Graphics::Screen::fillRect(Common::Rect(x, y, x + w, y + h), color);
+}
+
+int Screen::drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+int Screen::drawStringUni(int x, int y, int fidx, const byte *rgb, const uint32 *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+int Screen::stringWidth(int fidx, const char *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+int Screen::stringWidthUni(int fidx, const uint32 *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+void Screen::drawCaret(const Common::Point &pos) {
+	// TODO
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/screen.h b/engines/gargoyle/screen.h
new file mode 100644
index 0000000..ce9088d
--- /dev/null
+++ b/engines/gargoyle/screen.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_DRAW_H
+#define GARGOYLE_DRAW_H
+
+#include "graphics/screen.h"
+
+namespace Gargoyle {
+
+class Screen : public Graphics::Screen {
+public:
+	/**
+	 * Fills the screen with a given rgb color
+	 */
+	void fill(const byte *rgb);
+
+	/**
+	 * Fill a given area of the screen with an rgb color
+	 */
+	void fillRect(uint x, uint y, uint w, uint h, const byte *rgb);
+
+	int drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw);
+
+	int drawStringUni(int x, int y, int fidx, const byte *rgb, const uint32 *s, int n, int spw);
+
+	int stringWidth(int fidx, const char *s, int n, int spw);
+
+	int stringWidthUni(int fidx, const uint32 *s, int n, int spw);
+
+	void drawCaret(const Common::Point &pos);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index bb002bd..caf51a2 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -22,6 +22,7 @@
 
 #include "gargoyle/window_graphics.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/screen.h"
 
 namespace Gargoyle {
 
@@ -84,13 +85,14 @@ void GraphicsWindow::touch() {
 }
 
 void GraphicsWindow::redraw() {
+	Screen &screen = *g_vm->_screen;
 	Window::redraw();
 
 	if (_dirty || Windows::_forceRedraw) {
 		_dirty = 0;
 
 		if (_surface)
-			g_vm->_screen->blitFrom(*_surface, Common::Point(_bbox.left, _bbox.top));
+			screen.blitFrom(*_surface, Common::Point(_bbox.left, _bbox.top));
 	}
 }
 
@@ -121,7 +123,6 @@ glui32 GraphicsWindow::drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int s
 void GraphicsWindow::eraseRect(int whole, glsi32 x0, glsi32 y0, glui32 width, glui32 height) {
 	int x1 = x0 + width;
 	int y1 = y0 + height;
-	int x, y;
 	int hx0, hx1, hy0, hy1;
 
 	if (whole) {
@@ -156,7 +157,6 @@ void GraphicsWindow::fillRect(glui32 color, glsi32 x0, glsi32 y0, glui32 width,
 	unsigned char col[3];
 	int x1 = x0 + width;
 	int y1 = y0 + height;
-	int x, y;
 	int hx0, hx1, hy0, hy1;
 
 	col[0] = (color >> 16) & 0xff;
@@ -191,13 +191,11 @@ void GraphicsWindow::setBackgroundColor(glui32 color) {
 }
 
 void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int height, glui32 linkval) {
-	unsigned char *sp, *dp;
 	int dx1, dy1, x1, y1, sx0, sy0, sx1, sy1;
-	int x, y, w, h;
 	int hx0, hx1, hy0, hy1;
+	int w, h;
 
-	if (width != src->w || height != src->h)
-	{
+	if (width != src->w || height != src->h) {
 		src = src->scale(width, height);
 		if (!src)
 			return;
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 6df3ac4..b940fe6 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -23,6 +23,7 @@
 #include "gargoyle/window_text_buffer.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/screen.h"
 #include "gargoyle/string.h"
 
 namespace Gargoyle {
@@ -795,6 +796,7 @@ void TextBufferWindow::redraw() {
     int hx0, hx1, hy0, hy1;
     int selbuf, selrow, selchar, sx0, sx1, selleft, selright;
     int tx, tsc, tsw, lsc, rsc;
+	Screen &screen = *g_vm->_screen;
 
 	Window::redraw();
 
@@ -953,7 +955,7 @@ void TextBufferWindow::redraw() {
          * fill in background colors
          */
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        drawRect(x0/GLI_SUBPIX, y,
+        screen.fillRect(x0/GLI_SUBPIX, y,
                 (x1-x0) / GLI_SUBPIX, g_conf->_leading,
                 color);
 
@@ -965,12 +967,12 @@ void TextBufferWindow::redraw() {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = ln->_attrs[a].attrBg(_styles);
-                w = stringWidthUni(font, ln->_chars + a, b - a, spw);
-                drawRect(x/GLI_SUBPIX, y,
+                w = screen.stringWidthUni(font, ln->_chars + a, b - a, spw);
+                screen.fillRect(x/GLI_SUBPIX, y,
                         w/GLI_SUBPIX, g_conf->_leading,
                         color);
                 if (link) {
-                    drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+                    screen.fillRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
                             w/GLI_SUBPIX + 1, g_conf->_linkStyle,
                             g_conf->_linkColor);
                     g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
@@ -984,11 +986,11 @@ void TextBufferWindow::redraw() {
         link = ln->_attrs[a].hyper;
         font = ln->_attrs[a].attrFont(_styles);
         color = ln->_attrs[a].attrBg(_styles);
-        w = stringWidthUni(font, ln->_chars + a, b - a, spw);
-        drawRect(x/GLI_SUBPIX, y, w/GLI_SUBPIX,
+        w = screen.stringWidthUni(font, ln->_chars + a, b - a, spw);
+        screen.fillRect(x/GLI_SUBPIX, y, w/GLI_SUBPIX,
                 g_conf->_leading, color);
         if (link) {
-            drawRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+            screen.fillRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
                     w/GLI_SUBPIX + 1, g_conf->_linkStyle,
                     g_conf->_linkColor);
             g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
@@ -998,7 +1000,7 @@ void TextBufferWindow::redraw() {
         x += w;
 
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        drawRect(x/GLI_SUBPIX, y,
+        screen.fillRect(x/GLI_SUBPIX, y,
                 x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
                 color);
 
@@ -1009,7 +1011,7 @@ void TextBufferWindow::redraw() {
         if (_windows->getFocusWindow() == this && i == 0 && (_lineRequest || _lineRequestUni)) {
             w = calcWidth(_chars, _attrs, 0, _inCurs, spw);
             if (w < pw - g_conf->_caretShape * 2 * GLI_SUBPIX)
-                drawCaret(Common::Point(x0 + SLOP + ln->_lm + w, y + g_conf->_baseLine));
+                screen.drawCaret(Common::Point(x0 + SLOP + ln->_lm + w, y + g_conf->_baseLine));
         }
 
         /*
@@ -1024,7 +1026,7 @@ void TextBufferWindow::redraw() {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-                x = drawStringUni(x, y + g_conf->_baseLine,
+                x = screen.drawStringUni(x, y + g_conf->_baseLine,
                         font, color, ln->_chars + a, b - a, spw);
                 a = b;
             }
@@ -1032,7 +1034,7 @@ void TextBufferWindow::redraw() {
         link = ln->_attrs[a].hyper;
         font = ln->_attrs[a].attrFont(_styles);
         color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-        drawStringUni(x, y + g_conf->_baseLine,
+        screen.drawStringUni(x, y + g_conf->_baseLine,
                 font, color, ln->_chars + a, linelen - a, spw);
     }
 
@@ -1048,11 +1050,11 @@ void TextBufferWindow::redraw() {
                 x1/GLI_SUBPIX, y + g_conf->_leading);
 
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        drawRect(x/GLI_SUBPIX, y,
+        screen.fillRect(x/GLI_SUBPIX, y,
                 x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
                 color);
 
-        w = stringWidth(g_conf->_moreFont,
+        w = screen.stringWidth(g_conf->_moreFont,
                 g_conf->_morePrompt.c_str(), g_conf->_morePrompt.size(), -1);
 
         if (g_conf->_moreAlign == 1)    /* center */
@@ -1061,7 +1063,7 @@ void TextBufferWindow::redraw() {
             x = x1 - SLOP - w;
 
         color = Windows::_overrideFgSet ? g_conf->_moreColor : _fgColor;
-        drawString(x, y + g_conf->_baseLine, 
+		screen.drawString(x, y + g_conf->_baseLine,
                 g_conf->_moreFont, color,
                 g_conf->_morePrompt.c_str(), g_conf->_morePrompt.size(), -1);
         y1 = y; /* don't want pictures overdrawing "[more]" */
@@ -1142,14 +1144,14 @@ void TextBufferWindow::redraw() {
             t0 = t1 = y0;
         }
 
-        drawRect(x0+1, y0, x1-x0-2, y1-y0, g_conf->_scrollBg);
-        drawRect(x0+1, t0, x1-x0-2, t1-t0, g_conf->_scrollFg);
+        screen.fillRect(x0+1, y0, x1-x0-2, y1-y0, g_conf->_scrollBg);
+        screen.fillRect(x0+1, t0, x1-x0-2, t1-t0, g_conf->_scrollFg);
 
         for (i = 0; i < g_conf->_scrollWidth / 2 + 1; i++) {
-            drawRect(x0+g_conf->_scrollWidth/2-i,
+            screen.fillRect(x0+g_conf->_scrollWidth/2-i,
                     y0 - g_conf->_scrollWidth/2 + i,
                     i*2, 1, g_conf->_scrollFg);
-            drawRect(x0+g_conf->_scrollWidth/2-i,
+            screen.fillRect(x0+g_conf->_scrollWidth/2-i,
                     y1 + g_conf->_scrollWidth/2 - i,
                     i*2, 1, g_conf->_scrollFg);
         }
@@ -1608,19 +1610,20 @@ void TextBufferWindow::scrollResize() {
 
 int TextBufferWindow::calcWidth(glui32 *chars, Attributes *attrs, int startchar,
 		int numChars, int spw) {
+	Screen &screen = *g_vm->_screen;
 	int w = 0;
 	int a, b;
 
 	a = startchar;
 	for (b = startchar; b < numChars; b++) {
 		if (attrs[a] == attrs[b]) {
-			w += stringWidthUni(attrs[a].attrFont(_styles),
+			w += screen.stringWidthUni(attrs[a].attrFont(_styles),
 				chars + a, b - a, spw);
 			a = b;
 		}
 	}
 
-	w += stringWidthUni(attrs[a].attrFont(_styles),
+	w += screen.stringWidthUni(attrs[a].attrFont(_styles),
 		chars + a, b - a, spw);
 
 	return w;
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 483d556..bf0f39c 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -23,6 +23,7 @@
 #include "gargoyle/window_text_grid.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/screen.h"
 #include "gargoyle/window_mask.h"
 
 namespace Gargoyle {
@@ -576,6 +577,7 @@ void TextGridWindow::redraw() {
 	glui32 link;
 	int font;
 	byte *fgcolor, *bgcolor;
+	Screen &screen = *g_vm->_screen;
 
 	Window::redraw();
 
@@ -601,17 +603,17 @@ void TextGridWindow::redraw() {
 					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
 					bgcolor = ln->_attrs[a].attrBg(styles);
 					w = (b - a) * g_conf->_cellW;
-					drawRect(x, y, w, g_conf->_leading, bgcolor);
+					screen.fillRect(x, y, w, g_conf->_leading, bgcolor);
 					o = x;
 
 					for (k = a; k < b; k++) {
-						drawStringUni(o * GLI_SUBPIX,
+						screen.drawStringUni(o * GLI_SUBPIX,
 							y + g_conf->_baseLine, font, fgcolor,
 							&ln->_chars[k], 1, -1);
 						o += g_conf->_cellW;
 					}
 					if (link) {
-						drawRect(x, y + g_conf->_baseLine + 1, w,
+						screen.fillRect(x, y + g_conf->_baseLine + 1, w,
 							g_conf->_linkStyle, g_conf->_linkColor);
 						g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 					}
@@ -625,17 +627,17 @@ void TextGridWindow::redraw() {
 			bgcolor = ln->_attrs[a].attrBg(styles);
 			w = (b - a) * g_conf->_cellW;
 			w += _bbox.right - (x + w);
-			drawRect(x, y, w, g_conf->_leading, bgcolor);
+			screen.fillRect(x, y, w, g_conf->_leading, bgcolor);
 
 			o = x;
 			for (k = a; k < b; k++) {
-				drawStringUni(o * GLI_SUBPIX,
+				screen.drawStringUni(o * GLI_SUBPIX,
 					y + g_conf->_baseLine, font, fgcolor,
 					&ln->_chars[k], 1, -1);
 				o += g_conf->_cellW;
 			}
 			if (link) {
-				drawRect(x, y + g_conf->_baseLine + 1, w,
+				screen.fillRect(x, y + g_conf->_baseLine + 1, w,
 					g_conf->_linkStyle, g_conf->_linkColor);
 				g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 			}
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 0dc500e..6fb834d 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -27,6 +27,7 @@
 #include "gargoyle/window_text_grid.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/screen.h"
 #include "gargoyle/streams.h"
 #include "common/algorithm.h"
 #include "common/textconsole.h"
@@ -230,7 +231,7 @@ void Windows::redraw() {
 
 	if (_forceRedraw) {
 		repaint(Common::Rect(0, 0, g_conf->_imageW, g_conf->_imageH));
-		fillArea(g_conf->_windowColor);
+		g_vm->_screen->fill(g_conf->_windowColor);
 	}
 
 	if (_rootWin)
@@ -378,7 +379,7 @@ void Window::redraw() {
 	if (Windows::_forceRedraw) {
 		unsigned char *color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
 		int y0 = _yAdj ? _bbox.top - _yAdj : _bbox.top;
-		_windows->drawRect(_bbox.left, y0, _bbox.width(), _bbox.bottom - y0, color);
+		g_vm->_screen->fillRect(_bbox.left, y0, _bbox.width(), _bbox.bottom - y0, color);
 	}
 }
 
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 9092ea4..ba9cb92 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -27,7 +27,6 @@
 #include "common/list.h"
 #include "common/rect.h"
 #include "graphics/screen.h"
-#include "gargoyle/draw.h"
 #include "gargoyle/events.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/fonts.h"
@@ -48,7 +47,7 @@ class PairWindow;
 /**
  * Main windows manager
  */
-class Windows : public Draw {
+class Windows {
 	friend class Window;
 public:
 	class iterator {
@@ -238,7 +237,7 @@ struct Attributes {
 /**
  * Window definition
  */
-class Window : public Draw {
+class Window {
 public:
 	Windows *_windows;
 	glui32 _rock;


Commit: 3d8626081ce6a03d533009e07699b397f70b834b
    https://github.com/scummvm/scummvm/commit/3d8626081ce6a03d533009e07699b397f70b834b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added window closing, some structures cleanup

Changed paths:
    engines/gargoyle/events.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk_types.h
    engines/gargoyle/streams.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 4ee9470..939e691 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -93,15 +93,16 @@ enum Keycode {
  * Event structure
  */
 struct Event {
-	EvType _type;
-	Window *_window;
-	uint32 _val1, _val2;
+	EvType type;
+	Window *window;
+	uint32 val1, val2;
 
 	/**
 	 * Constructor
 	 */
-	Event() : _type(evtype_None), _window(nullptr), _val1(0), _val2(0) {}
+	Event() : type(evtype_None), window(nullptr), val1(0), val2(0) {}
 };
+typedef Event event_t;
 
 class Events {
 public:
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index ba62de1..26b8349 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -73,7 +73,13 @@ winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size, glui32 w
 }
 
 void Glk::glk_window_close(winid_t win, stream_result_t *result) {
-	// TODO
+	_windows->_forceRedraw = true;
+
+	if (!win) {
+		warning("glk_window_close: invalid ref");
+	} else {
+		_windows->windowClose(win, result);
+	}
 }
 
 void Glk::glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) {
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index a25a413..81d05ac 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -220,19 +220,6 @@ enum ImageAlign {
 
 #endif /* GLK_MODULE_IMAGE */
 
-struct event_struct {
-	glui32 type;
-	Window *win;
-	glui32 val1, val2;
-};
-typedef event_struct event_t;
-
-struct stream_result_struct {
-	glui32 readcount;
-	glui32 writecount;
-};
-typedef stream_result_struct stream_result_t;
-
 #ifdef GLK_MODULE_DATETIME
 
 struct glktimeval_struct {
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 82d6465..51ab484 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -36,6 +36,7 @@ struct StreamResult {
 	uint32 _readCount;
 	uint32 _writeCount;
 };
+typedef StreamResult stream_result_t;
 
 /**
  * Base class for streams
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index b940fe6..a32a64f 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -756,10 +756,10 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 
 	_attr = _origAttr;
 
-	ev->_type = evtype_LineInput;
-	ev->_window = this;
-	ev->_val1 = len;
-	ev->_val2 = 0;
+	ev->type = evtype_LineInput;
+	ev->window = this;
+	ev->val1 = len;
+	ev->val2 = 0;
 
 	_lineRequest = false;
 	_lineRequestUni = false;
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index bf0f39c..d9550bf 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -360,10 +360,10 @@ void TextGridWindow::cancelLineEvent(Event *ev) {
 	_curX = 0;
 	_attr = _origAttr;
 
-	ev->_type = evtype_LineInput;
-	ev->_window = this;
-	ev->_val1 = _inLen;
-	ev->_val2 = 0;
+	ev->type = evtype_LineInput;
+	ev->window = this;
+	ev->val1 = _inLen;
+	ev->val2 = 0;
 
 	_lineRequest = false;
 	_lineRequestUni = false;
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 6fb834d..42db6f7 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -73,7 +73,7 @@ Windows::Windows(Graphics::Screen *screen) : _screen(screen), _windowList(nullpt
 Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 		glui32 wintype, glui32 rock) {
 	Window *newwin, *oldparent;
-	PairWindow *pairwin;
+	PairWindow *pairWin;
 	glui32 val;
 
 	_forceRedraw = true;
@@ -125,24 +125,24 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 	if (!splitwin) {
 		_rootWin = newwin;
 	} else {
-		// create pairwin, with newwin as the key
-		pairwin = newPairWindow(method, newwin, size);
-		pairwin->_child1 = splitwin;
-		pairwin->_child2 = newwin;
+		// create pairWin, with newwin as the key
+		pairWin = newPairWindow(method, newwin, size);
+		pairWin->_child1 = splitwin;
+		pairWin->_child2 = newwin;
 
-		splitwin->_parent = pairwin;
-		newwin->_parent = pairwin;
-		pairwin->_parent = oldparent;
+		splitwin->_parent = pairWin;
+		newwin->_parent = pairWin;
+		pairWin->_parent = oldparent;
 
 		if (oldparent) {
 			PairWindow *parentWin = dynamic_cast<PairWindow *>(oldparent);
 			assert(parentWin);
 			if (parentWin->_child1 == splitwin)
-				parentWin->_child1 = pairwin;
+				parentWin->_child1 = pairWin;
 			else
-				parentWin->_child2 = pairwin;
+				parentWin->_child2 = pairWin;
 		} else {
-			_rootWin = pairwin;
+			_rootWin = pairWin;
 		}
 	}
 
@@ -151,6 +151,62 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 	return newwin;
 }
 
+void Windows::windowClose(Window *win, StreamResult *result) {
+	if (win == _rootWin || win->_parent == nullptr) {
+		// Close the root window, which means all windows.
+		_rootWin = nullptr;
+
+		// Begin (simpler) closation
+		win->_stream->fillResult(result);
+		win->close(true);
+	} else {
+		// Have to jigger parent
+		Window *sibWin;
+		PairWindow *pairWin = dynamic_cast<PairWindow *>(win->_parent);
+		PairWindow *grandparWin;
+
+		if (win == pairWin->_child1) {
+			sibWin = pairWin->_child2;
+		} else if (win == pairWin->_child2) {
+			sibWin = pairWin->_child1;
+		} else {
+			warning("windowClose: window tree is corrupted");
+			return;
+		}
+
+		grandparWin = dynamic_cast<PairWindow *>(pairWin->_parent);
+		if (!grandparWin) {
+			_rootWin = sibWin;
+			sibWin->_parent = nullptr;
+		} else {
+			if (grandparWin->_child1 == pairWin)
+				grandparWin->_child1 = sibWin;
+			else
+				grandparWin->_child2 = sibWin;
+			sibWin->_parent = grandparWin;
+		}
+
+		// Begin closation
+		win->_stream->fillResult(result);
+
+		// Close the child window (and descendants), so that key-deletion can
+		// crawl up the tree to the root window.
+		win->close(true);
+
+		// This probably isn't necessary, but the child *is* gone, so just in case.
+		if (win == pairWin->_child1)
+			pairWin->_child1 = nullptr;
+		else if (win == pairWin->_child2)
+			pairWin->_child2 = nullptr;
+
+		// Now we can delete the parent pair.
+		pairWin->close(false);
+
+		// Sort out the arrangements
+		rearrange();
+	}
+}
+
 Window *Windows::newWindow(glui32 type, glui32 rock) {
 	Window *win;
 
@@ -355,6 +411,30 @@ Window::~Window() {
 		next->_prev = prev;
 }
 
+void Window::close(bool recurse) {
+	if (_windows->getFocusWindow() == this)
+		// Focused window is being removed
+		_windows->setFocus(nullptr);
+
+	for (Window *wx = _parent; wx; wx = wx->_parent) {
+		PairWindow *pairWin = dynamic_cast<PairWindow *>(wx);
+
+		if (pairWin && pairWin->_key == this) {
+			pairWin->_key = nullptr;
+			pairWin->_keyDamage = true;
+		}
+	}
+
+	PairWindow *pairWin = dynamic_cast<PairWindow *>(this);
+	if (pairWin) {
+		pairWin->_child1->close(recurse);
+		pairWin->_child2->close(recurse);
+	}
+
+	// Finally, delete the window
+	delete this;
+}
+
 void Window::cancelLineEvent(Event *ev) {
 	Event dummyEv;
 	if (!ev)
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index ba9cb92..f52bbdd 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -135,6 +135,11 @@ public:
 		glui32 wintype, glui32 rock);
 
 	/**
+	 * Close an existing window
+	 */
+	void windowClose(Window *win, StreamResult *result = nullptr);
+
+	/**
 	 * Return the root window
 	 */
 	Window *getRoot() const { return _rootWin; }
@@ -284,6 +289,11 @@ public:
 	virtual ~Window();
 
 	/**
+	 * Close and delete the window
+	 */
+	void close(bool recurse = true);
+
+	/**
 	 * Rearranges the window
 	 */
 	virtual void rearrange(const Common::Rect &box) { _bbox = box; }


Commit: 49b8281ce3590642673db3fca67f7c94d5f710c1
    https://github.com/scummvm/scummvm/commit/49b8281ce3590642673db3fca67f7c94d5f710c1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Window rearrangement methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/window_pair.cpp
    engines/gargoyle/window_pair.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 26b8349..4dc5e53 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -25,6 +25,7 @@
 #include "gargoyle/streams.h"
 #include "gargoyle/string.h"
 #include "gargoyle/windows.h"
+#include "gargoyle/window_pair.h"
 
 namespace Gargoyle {
 
@@ -86,14 +87,21 @@ void Glk::glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
 	// TODO
 }
 
-void Glk::glk_window_set_arrangement(winid_t win, glui32 method,
-	glui32 size, winid_t keywin) {
-	// TODO
+void Glk::glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin) {
+	if (!win) {
+		warning("window_set_arrangement: invalid ref");
+	} else {
+		win->setArrangement(method, size, keywin);
+	}
 }
 
-void Glk::glk_window_get_arrangement(winid_t win, glui32 *methodptr,
-	glui32 *sizeptr, winid_t *keywinptr) {
-	// TODO
+void Glk::glk_window_get_arrangement(winid_t win, glui32 *method,
+		glui32 *size, winid_t *keyWin) {
+	if (!win) {
+		warning("window_get_arrangement: invalid ref");
+	} else {
+		win->getArrangement(method, size, keyWin);
+	}
 }
 
 winid_t Glk::glk_window_iterate(winid_t win, glui32 *rockptr) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 0029ecf..5a822f8 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -75,8 +75,8 @@ public:
 		glui32 *heightptr);
 	void glk_window_set_arrangement(winid_t win, glui32 method,
 		glui32 size, winid_t keywin);
-	void glk_window_get_arrangement(winid_t win, glui32 *methodptr,
-		glui32 *sizeptr, winid_t *keywinptr);
+	void glk_window_get_arrangement(winid_t win, glui32 *method,
+		glui32 *size, winid_t *keywin);
 	winid_t glk_window_iterate(winid_t win, glui32 *rockptr);
 	glui32 glk_window_get_rock(winid_t win);
 	glui32 glk_window_get_type(winid_t win);
diff --git a/engines/gargoyle/window_pair.cpp b/engines/gargoyle/window_pair.cpp
index 7a11597..b8d593e 100644
--- a/engines/gargoyle/window_pair.cpp
+++ b/engines/gargoyle/window_pair.cpp
@@ -124,4 +124,85 @@ void PairWindow::redraw() {
 	// TODO
 }
 
+void PairWindow::getArrangement(glui32 *method, glui32 *size, Window **keyWin) {
+	glui32 val = _dir | _division;
+	if (!_wBorder)
+		val |= winmethod_NoBorder;
+
+	if (size)
+		*size = _size;
+	if (keyWin) {
+		if (_key)
+			*keyWin = _key;
+		else
+			*keyWin = nullptr;
+	}
+
+	if (method)
+		*method = val;
+}
+
+void PairWindow::setArrangement(glui32 method, glui32 size, Window *keyWin) {
+	glui32 newDir;
+	bool newVertical, newBackward;
+
+	if (_key) {
+		Window *wx;
+		PairWindow *pairWin = dynamic_cast<PairWindow *>(_key);
+
+		if (pairWin) {
+			warning("setArrangement: keywin cannot be a Pair");
+			return;
+		}
+
+		for (wx = _key; wx; wx = wx->_parent) {
+			if (wx == this)
+				break;
+		}
+		if (wx == nullptr) {
+			warning("setArrangement: keywin must be a descendant");
+			return;
+		}
+	}
+
+	newDir = method & winmethod_DirMask;
+	newVertical = (newDir == winmethod_Left || newDir == winmethod_Right);
+	newBackward = (newDir == winmethod_Left || newDir == winmethod_Above);
+	if (!keyWin)
+		keyWin = _key;
+
+	if ((newVertical && !_vertical) || (!newVertical && _vertical)) {
+		if (!_vertical)
+			warning("setArrangement: split must stay horizontal");
+		else
+			warning("setArrangement: split must stay vertical");
+		return;
+	}
+
+	if (keyWin && dynamic_cast<BlankWindow *>(keyWin)
+			&& (method & winmethod_DivisionMask) == winmethod_Fixed) {
+		warning("setArrangement: a Blank window cannot have a fixed size");
+		return;
+	}
+
+	if ((newBackward && !_backward) || (!newBackward && _backward)) {
+		// switch the children
+		Window *tmpWin = _child1;
+		_child1 = _child2;
+		_child2 = tmpWin;
+	}
+
+	// set up everything else
+	_dir = newDir;
+	_division = method & winmethod_DivisionMask;
+	_key = keyWin;
+	_size = size;
+	_wBorder = ((method & winmethod_BorderMask) == winmethod_Border);
+
+	_vertical = (_dir == winmethod_Left || _dir == winmethod_Right);
+	_backward = (_dir == winmethod_Left || _dir == winmethod_Above);
+
+	_windows->rearrange();
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_pair.h b/engines/gargoyle/window_pair.h
index b25b324..8e80f79 100644
--- a/engines/gargoyle/window_pair.h
+++ b/engines/gargoyle/window_pair.h
@@ -57,6 +57,10 @@ public:
 	 * Redraw the window
 	 */
 	virtual void redraw() override;
+
+	virtual void getArrangement(glui32 *method, glui32 *size, Window **keyWin) override;
+
+	virtual void setArrangement(glui32 method, glui32 size, Window *keyWin) override;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 42db6f7..53feed2 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -471,6 +471,14 @@ void Window::acceptReadChar(glui32 arg) {
 	warning("acceptReadChar:: window does not support keyboard input");
 }
 
+void Window::getArrangement(glui32 *method, glui32 *size, Window **keyWin) {
+	warning("getArrangement: not a Pair window");
+}
+
+void Window::setArrangement(glui32 method, glui32 size, Window *keyWin) {
+	warning("setArrangement: not a Pair window");
+}
+
 bool Window::checkTerminator(glui32 ch) {
 	if (ch == keycode_Escape)
 		return true;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index f52bbdd..1e4dfd8 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -98,11 +98,6 @@ private:
 	 */
 	PairWindow *newPairWindow(glui32 method, Window *key, glui32 size);
 
-	/**
-	 * Rearrange windows
-	 */
-	void rearrange();
-
 	void refocus(Window *win);
 
 	Window *iterateTreeOrder(Window *win);
@@ -158,6 +153,11 @@ public:
 
 	void clearClaimSelect() { _claimSelect = false; }
 
+	/**
+	 * Rearrange windows
+	 */
+	void rearrange();
+
 	void redraw();
 
 	void redrawRect(const Common::Rect &r);
@@ -366,6 +366,10 @@ public:
 
 	virtual void acceptReadChar(glui32 arg);
 
+	virtual void getArrangement(glui32 *method, glui32 *size, Window **keyWin);
+
+	virtual void setArrangement(glui32 method, glui32 size, Window *keyWin);
+
 	int acceptScroll(glui32 arg);
 };
 typedef Window *winid_t;


Commit: 594f63f4752dcc3f2924ee34bc3b3fe33f895704
    https://github.com/scummvm/scummvm/commit/594f63f4752dcc3f2924ee34bc3b3fe33f895704
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added window getSize methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/window_graphics.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 4dc5e53..4e5c465 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -83,8 +83,12 @@ void Glk::glk_window_close(winid_t win, stream_result_t *result) {
 	}
 }
 
-void Glk::glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) {
-	// TODO
+void Glk::glk_window_get_size(winid_t win, glui32 *width, glui32 *height) {
+	if (!win) {
+		warning("window_get_size: invalid ref");
+	} else {
+		win->getSize(width, height);
+	}
 }
 
 void Glk::glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 5a822f8..4b300f5 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -71,12 +71,11 @@ public:
 		glui32 wintype, glui32 rock = 0) const;
 
 	void glk_window_close(winid_t win, stream_result_t *result);
-	void glk_window_get_size(winid_t win, glui32 *widthptr,
-		glui32 *heightptr);
+	void glk_window_get_size(winid_t win, glui32 *width, glui32 *height);
 	void glk_window_set_arrangement(winid_t win, glui32 method,
-		glui32 size, winid_t keywin);
+		glui32 size, winid_t keyWin);
 	void glk_window_get_arrangement(winid_t win, glui32 *method,
-		glui32 *size, winid_t *keywin);
+		glui32 *size, winid_t *keyWin);
 	winid_t glk_window_iterate(winid_t win, glui32 *rockptr);
 	glui32 glk_window_get_rock(winid_t win);
 	glui32 glk_window_get_type(winid_t win);
diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h
index 8e97b56..5d08125 100644
--- a/engines/gargoyle/window_graphics.h
+++ b/engines/gargoyle/window_graphics.h
@@ -82,24 +82,10 @@ public:
 	 */
 	virtual void redraw() override;
 
+	virtual void getSize(glui32 *width, glui32 *height) override;
+
 	glui32 drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int scale,
 		glui32 imagewidth, glui32 imageheight);
-
-	/**
-	 * Get the window dimensions
-	 */
-	void getSize(glui32 *w, glui32 *h) {
-		*w = _w;
-		*h = _h;
-	}
-
-	/**
-	 * Set the window dimensions
-	 */
-	void setSize(glui32 w, glui32 h) {
-		_w = w;
-		_h = h;
-	}
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index a32a64f..4368883 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -1633,6 +1633,13 @@ void TextBufferWindow::copyTextToClipboard(const glui32 *text, size_t len) {
 	// TODO
 }
 
+void TextBufferWindow::getSize(glui32 *width, glui32 *height) {
+	if (width)
+		*width = (_bbox.width() - g_conf->_tMarginX * 2) / g_conf->_cellW;
+	if (height)
+		*height = (_bbox.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
+}
+
 /*--------------------------------------------------------------------------*/
 
 TextBufferWindow::TextBufferRow::TextBufferRow() : _len(0), _newLine(0), _dirty(false),
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index 4741877..0c59cd2 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -206,6 +206,8 @@ public:
 
 	virtual void acceptReadChar(glui32 arg) override;
 
+	virtual void getSize(glui32 *width, glui32 *height) override;
+
 	int acceptScroll(glui32 arg);
 
 	glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height);
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index d9550bf..b249223 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -645,6 +645,13 @@ void TextGridWindow::redraw() {
 	}
 }
 
+void TextGridWindow::getSize(glui32 *width, glui32 *height) {
+	if (width)
+		*width = _bbox.width() / g_conf->_cellW;
+	if (height)
+		*height = _bbox.height() / g_conf->_cellH;
+}
+
 /*--------------------------------------------------------------------------*/
 
 void TextGridWindow::TextGridRow::resize(size_t newSize) {
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 2c5a0a8..2b8448c 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -155,6 +155,8 @@ public:
 	virtual void acceptReadLine(glui32 arg) override;
 
 	virtual void acceptReadChar(glui32 arg) override;
+
+	virtual void getSize(glui32 *width, glui32 *height) override;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 53feed2..400689c 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -503,6 +503,13 @@ bool Window::imageDraw(glui32 image, glui32 align, glsi32 val1, glsi32 val2) {
 	return false;
 }
 
+void Window::getSize(glui32 *width, glui32 *height) {
+	if (width)
+		*width = 0;
+	if (height)
+		*height = 0;
+}
+
 /*--------------------------------------------------------------------------*/
 
 BlankWindow::BlankWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 1e4dfd8..5a4c022 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -360,6 +360,8 @@ public:
 
 	bool imageDraw(glui32 image, glui32 align, glsi32 val1, glsi32 val2);
 
+	int acceptScroll(glui32 arg);
+
 	virtual  glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height) { return false; }
 
 	virtual void acceptReadLine(glui32 arg);
@@ -370,7 +372,7 @@ public:
 
 	virtual void setArrangement(glui32 method, glui32 size, Window *keyWin);
 
-	int acceptScroll(glui32 arg);
+	virtual void getSize(glui32 *width, glui32 *height);
 };
 typedef Window *winid_t;
 


Commit: 72022042ee303fde02eef2264d430d56526768ed
    https://github.com/scummvm/scummvm/commit/72022042ee303fde02eef2264d430d56526768ed
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Adding glk window methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/window_graphics.cpp


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 4e5c465..1e6dfa3 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -108,28 +108,63 @@ void Glk::glk_window_get_arrangement(winid_t win, glui32 *method,
 	}
 }
 
-winid_t Glk::glk_window_iterate(winid_t win, glui32 *rockptr) {
-	// TODO
+winid_t Glk::glk_window_iterate(winid_t win, glui32 *rock) {
+	win = win ? win->_next : _windows->getRoot();
+
+	if (win) {
+		if (rock)
+			*rock = win->_rock;
+		return win;
+	}
+
+	if (rock)
+		*rock = 0;
+
 	return nullptr;
 }
 
 glui32 Glk::glk_window_get_rock(winid_t win) {
-	// TODO
-	return 0;
+	if (!win) {
+		warning("window_get_rock: invalid ref.");
+		return 0;
+	}
+
+	return win->_rock;
 }
 
 glui32 Glk::glk_window_get_type(winid_t win) {
-	// TODO
-	return 0;
+	if (!win) {
+		warning("window_get_parent: invalid ref");
+		return 0;
+	}
+
+	return win->_type;
 }
 
 winid_t Glk::glk_window_get_parent(winid_t win) {
-	// TODO
-	return nullptr;
+	if (!win) {
+		warning("window_get_parent: invalid ref");
+		return 0;
+	}
+
+	return win->_parent;
 }
 
 winid_t Glk::glk_window_get_sibling(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("window_get_sibling: invalid ref");
+		return nullptr;
+	}
+
+	PairWindow *parentWin = dynamic_cast<PairWindow *>(win->_parent);
+	if (!parentWin)
+		return nullptr;
+
+	if (parentWin->_child1 == win)
+		return parentWin->_child2;
+	else if (parentWin->_child2 == win)
+		return parentWin->_child1;
+
 	return nullptr;
 }
 
@@ -142,16 +177,29 @@ void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
 }
 
 strid_t Glk::glk_window_get_stream(winid_t win) {
+	if (!win) {
+		warning("window_get_stream: invalid ref");
+		return nullptr;
+	}
+
 	return win->_stream;
 }
 
 void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
-	// TODO
+	if (!win) {
+		warning("window_set_echo_stream: invalid window id");
+	} else {
+		win->_echoStream = str;
+	}
 }
 
 strid_t Glk::glk_window_get_echo_stream(winid_t win) {
-	// TODO
-	return nullptr;
+	if (!win) {
+		warning("window_get_echo_stream: invalid ref");
+		return nullptr;
+	}
+
+	return win->_echoStream;
 }
 
 void Glk::glk_set_window(winid_t win) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 4b300f5..0e97600 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -76,7 +76,7 @@ public:
 		glui32 size, winid_t keyWin);
 	void glk_window_get_arrangement(winid_t win, glui32 *method,
 		glui32 *size, winid_t *keyWin);
-	winid_t glk_window_iterate(winid_t win, glui32 *rockptr);
+	winid_t glk_window_iterate(winid_t win, glui32 *rock);
 	glui32 glk_window_get_rock(winid_t win);
 	glui32 glk_window_get_type(winid_t win);
 	winid_t glk_window_get_parent(winid_t win);
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index caf51a2..3af2555 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -244,4 +244,9 @@ void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int h
 	_surface->blitFrom(*g_vm->_screen, Common::Rect(sx0, sy0, sx0 + w, sy0 + h), Common::Point(0, 0));
 }
 
+void GraphicsWindow::getSize(glui32 *width, glui32 *height) {
+	*width = _bbox.width();
+	*height = _bbox.height();
+}
+
 } // End of namespace Gargoyle


Commit: 4266f3af130ddeddbed5da9d6c7aa4826ad94d96
    https://github.com/scummvm/scummvm/commit/4266f3af130ddeddbed5da9d6c7aa4826ad94d96
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Adding more window glk methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 1e6dfa3..43ba646 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -387,11 +387,25 @@ void Glk::glk_request_timer_events(glui32 millisecs) {
 }
 
 void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
-	// TODO
+	if (!win) {
+		warning("request_line_event: invalid ref");
+	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+			|| win->_lineRequestUni) {
+		warning("request_line_event: window already has keyboard request");
+	} else {
+		win->requestLineEvent(buf, maxlen, initlen);
+	}
 }
 
 void Glk::glk_request_char_event(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("request_char_event: invalid ref");
+	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+			|| win->_lineRequestUni) {
+		warning("request_char_event: window already has keyboard request");
+	} else {
+		win->requestCharEvent();
+	}
 }
 
 void Glk::glk_request_mouse_event(winid_t win) {
@@ -413,7 +427,11 @@ void Glk::glk_cancel_mouse_event(winid_t win) {
 #ifdef GLK_MODULE_LINE_ECHO
 
 void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
-	// TODO
+	if (!win) {
+		warning("set_echo_line_event: invalid ref");
+	} else {
+		win->setEchoLineEvent(val);
+	}
 }
 
 #endif /* GLK_MODULE_LINE_ECHO */
@@ -421,7 +439,11 @@ void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
 #ifdef GLK_MODULE_LINE_TERMINATORS
 
 void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
-	// TODO
+	if (!win) {
+		warning("set_terminators_line_event: invalid ref");
+	} else {
+		win->setTerminatorsLineEvent(keycodes, count);
+	}
 }
 
 #endif /* GLK_MODULE_LINE_TERMINATORS */
@@ -505,12 +527,25 @@ strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmo
 }
 
 void Glk::glk_request_char_event_uni(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("request_char_event_uni: invalid ref");
+	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+			|| win->_lineRequestUni) {
+		warning("request_char_event_uni: window already has keyboard request");
+	} else {
+		win->requestCharEvent();
+	}
 }
 
-void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf,
-	glui32 maxlen, glui32 initlen) {
-	// TODO
+void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen) {
+	if (!win) {
+		warning("request_line_event_uni: invalid ref");
+	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+			|| win->_lineRequestUni) {
+		warning("request_line_event_uni: window already has keyboard request");
+	} else {
+		win->requestLineEventUni(buf, maxlen, initlen);
+	}
 }
 
 #endif /* GLK_MODULE_UNICODE */
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index 0c59cd2..094d6d0 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -208,6 +208,12 @@ public:
 
 	virtual void getSize(glui32 *width, glui32 *height) override;
 
+	virtual void requestCharEvent() override { _charRequest = true; }
+
+	virtual void requestCharEventUni() override { _charRequestUni = true; }
+
+	virtual void setEchoLineEvent(glui32 val) override { _echoLineInput = val != 0; }
+
 	int acceptScroll(glui32 arg);
 
 	glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height);
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 2b8448c..17fa96f 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -157,6 +157,10 @@ public:
 	virtual void acceptReadChar(glui32 arg) override;
 
 	virtual void getSize(glui32 *width, glui32 *height) override;
+
+	virtual void requestCharEvent() override { _charRequest = true; }
+
+	virtual void requestCharEventUni() override { _charRequestUni = true; }
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 400689c..7c5b259 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -479,6 +479,34 @@ void Window::setArrangement(glui32 method, glui32 size, Window *keyWin) {
 	warning("setArrangement: not a Pair window");
 }
 
+void Window::requestCharEvent() {
+	warning("requestCharEvent: window does not support keyboard input");
+}
+
+void Window::requestCharEventUni() {
+	warning("requestCharEventUni: window does not support keyboard input");
+}
+
+void Window::setTerminatorsLineEvent(glui32 *keycodes, glui32 count) {
+	if (dynamic_cast<TextBufferWindow *>(this) || dynamic_cast<TextGridWindow *>(this)) {
+		delete _lineTerminatorsBase;
+		_lineTerminatorsBase = nullptr;
+
+		if (!keycodes || count == 0) {
+			_termCt = 0;
+		} else {
+			_lineTerminatorsBase = new glui32[count + 1];
+			if (_lineTerminatorsBase) {
+				memcpy(_lineTerminatorsBase, keycodes, count * sizeof(glui32));
+				_lineTerminatorsBase[count] = 0;
+				_termCt = count;
+			}
+		}
+	} else {
+		warning("setTerminatorsLineEvent: window does not support keyboard input");
+	}
+}
+
 bool Window::checkTerminator(glui32 ch) {
 	if (ch == keycode_Escape)
 		return true;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 5a4c022..3c419e5 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -362,6 +362,8 @@ public:
 
 	int acceptScroll(glui32 arg);
 
+	void setTerminatorsLineEvent(glui32 *keycodes, glui32 count);
+
 	virtual  glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height) { return false; }
 
 	virtual void acceptReadLine(glui32 arg);
@@ -373,6 +375,12 @@ public:
 	virtual void setArrangement(glui32 method, glui32 size, Window *keyWin);
 
 	virtual void getSize(glui32 *width, glui32 *height);
+
+	virtual void requestCharEvent();
+
+	virtual void requestCharEventUni();
+
+	virtual void setEchoLineEvent(glui32 val) {}
 };
 typedef Window *winid_t;
 


Commit: e14424c2b944ae9a9c5ce34158bd5e7e361ee994
    https://github.com/scummvm/scummvm/commit/e14424c2b944ae9a9c5ce34158bd5e7e361ee994
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Adding more window glk methods

Changed paths:
    engines/gargoyle/events.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_graphics.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 939e691..f49d9ff 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -101,6 +101,15 @@ struct Event {
 	 * Constructor
 	 */
 	Event() : type(evtype_None), window(nullptr), val1(0), val2(0) {}
+
+	/**
+	 * Clear
+	 */
+	void clear() {
+		type = evtype_None;
+		window = nullptr;
+		val1 = val2 = 0;
+	}
 };
 typedef Event event_t;
 
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 43ba646..c309f72 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -409,19 +409,35 @@ void Glk::glk_request_char_event(winid_t win) {
 }
 
 void Glk::glk_request_mouse_event(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("request_mouse_event: invalid ref");
+	} else {
+		win->requestMouseEvent();
+	}
 }
 
 void Glk::glk_cancel_line_event(winid_t win, event_t *event) {
-	// TODO
+	if (!win) {
+		warning("cancel_line_event: invalid ref");
+	} else {
+		win->cancelLineEvent(event);
+	}
 }
 
 void Glk::glk_cancel_char_event(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("glk_cancel_char_event: invalid ref");
+	} else {
+		win->cancelCharEvent();
+	}
 }
 
 void Glk::glk_cancel_mouse_event(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("cancel_mouse_event: invalid ref");
+	} else {
+		win->cancelMouseEvent();
+	}
 }
 
 #ifdef GLK_MODULE_LINE_ECHO
@@ -685,7 +701,11 @@ void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
 }
 
 void Glk::glk_request_hyperlink_event(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("request_hyperlink_event: invalid ref");
+	} else {
+		win->requestHyperlinkEvent();
+	}
 }
 
 void Glk::glk_cancel_hyperlink_event(winid_t win) {
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index 3af2555..0ec2bee 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -244,7 +244,7 @@ void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int h
 	_surface->blitFrom(*g_vm->_screen, Common::Rect(sx0, sy0, sx0 + w, sy0 + h), Common::Point(0, 0));
 }
 
-void GraphicsWindow::getSize(glui32 *width, glui32 *height) {
+void GraphicsWindow::getSize(glui32 *width, glui32 *height) const {
 	*width = _bbox.width();
 	*height = _bbox.height();
 }
diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h
index 5d08125..166b03a 100644
--- a/engines/gargoyle/window_graphics.h
+++ b/engines/gargoyle/window_graphics.h
@@ -55,6 +55,9 @@ public:
 	 */
 	virtual ~GraphicsWindow();
 
+	glui32 drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int scale,
+		glui32 imagewidth, glui32 imageheight);
+
 	/**
 	 * Rearranges the window
 	 */
@@ -77,15 +80,16 @@ public:
 	 */
 	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
 
+	virtual void requestMouseEvent() override { _mouseRequest = true; }
+
+	virtual void requestHyperlinkEvent() override { _hyperRequest = true; }
+
 	/**
 	 * Redraw the window
 	 */
 	virtual void redraw() override;
 
-	virtual void getSize(glui32 *width, glui32 *height) override;
-
-	glui32 drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int scale,
-		glui32 imagewidth, glui32 imageheight);
+	virtual void getSize(glui32 *width, glui32 *height) const override;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 4368883..c428dac 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -1633,7 +1633,7 @@ void TextBufferWindow::copyTextToClipboard(const glui32 *text, size_t len) {
 	// TODO
 }
 
-void TextBufferWindow::getSize(glui32 *width, glui32 *height) {
+void TextBufferWindow::getSize(glui32 *width, glui32 *height) const {
 	if (width)
 		*width = (_bbox.width() - g_conf->_tMarginX * 2) / g_conf->_cellW;
 	if (height)
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index 094d6d0..774bd66 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -147,6 +147,10 @@ public:
 	 */
 	virtual ~TextBufferWindow();
 
+	int acceptScroll(glui32 arg);
+
+	glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height);
+
 	/**
 	 * Rearranges the window
 	 */
@@ -206,7 +210,7 @@ public:
 
 	virtual void acceptReadChar(glui32 arg) override;
 
-	virtual void getSize(glui32 *width, glui32 *height) override;
+	virtual void getSize(glui32 *width, glui32 *height) const override;
 
 	virtual void requestCharEvent() override { _charRequest = true; }
 
@@ -214,9 +218,9 @@ public:
 
 	virtual void setEchoLineEvent(glui32 val) override { _echoLineInput = val != 0; }
 
-	int acceptScroll(glui32 arg);
+	virtual void requestHyperlinkEvent() override { _hyperRequest = true; }
 
-	glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height);
+	virtual void cancelCharEvent() override { _charRequest = _charRequestUni = false; }
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index b249223..01c3193 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -645,7 +645,7 @@ void TextGridWindow::redraw() {
 	}
 }
 
-void TextGridWindow::getSize(glui32 *width, glui32 *height) {
+void TextGridWindow::getSize(glui32 *width, glui32 *height) const {
 	if (width)
 		*width = _bbox.width() / g_conf->_cellW;
 	if (height)
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 17fa96f..9ff588b 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -123,6 +123,24 @@ public:
 	virtual void click(const Common::Point &newPos) override;
 
 	/**
+	 * Cancel a hyperlink event
+	 */
+	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
+
+	/**
+	 * Redraw the window
+	 */
+	virtual void redraw() override;
+
+	virtual void acceptReadLine(glui32 arg) override;
+
+	virtual void acceptReadChar(glui32 arg) override;
+
+	virtual void getSize(glui32 *width, glui32 *height) const override;
+
+	virtual void requestCharEvent() override { _charRequest = true; }
+
+	/**
 	 * Prepare for inputing a line
 	 */
 	virtual void requestLineEvent(char *buf, glui32 maxlen, glui32 initlen) override;
@@ -142,25 +160,13 @@ public:
 	 */
 	virtual void cancelMouseEvent() override { _mouseRequest = false; }
 
-	/**
-	 * Cancel a hyperlink event
-	 */
-	virtual void cancelHyperlinkEvent() override { _hyperRequest = false; }
-
-	/**
-	 * Redraw the window
-	 */
-	virtual void redraw() override;
-
-	virtual void acceptReadLine(glui32 arg) override;
-
-	virtual void acceptReadChar(glui32 arg) override;
+	virtual void requestCharEventUni() override { _charRequestUni = true; }
 
-	virtual void getSize(glui32 *width, glui32 *height) override;
+	virtual void requestMouseEvent() override { _mouseRequest = true; }
 
-	virtual void requestCharEvent() override { _charRequest = true; }
+	virtual void requestHyperlinkEvent() override { _hyperRequest = true; }
 
-	virtual void requestCharEventUni() override { _charRequestUni = true; }
+	virtual void cancelCharEvent() override { _charRequest = _charRequestUni = false; }
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 7c5b259..cc23e47 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -531,7 +531,7 @@ bool Window::imageDraw(glui32 image, glui32 align, glsi32 val1, glsi32 val2) {
 	return false;
 }
 
-void Window::getSize(glui32 *width, glui32 *height) {
+void Window::getSize(glui32 *width, glui32 *height) const {
 	if (width)
 		*width = 0;
 	if (height)
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 3c419e5..b7d701f 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -344,6 +344,11 @@ public:
 	virtual void cancelLineEvent(Event *ev);
 
 	/**
+	 * Cancel a character event
+	 */
+	virtual void cancelCharEvent() {}
+
+	/**
 	 * Cancel a mouse event
 	 */
 	virtual void cancelMouseEvent() {}
@@ -374,13 +379,17 @@ public:
 
 	virtual void setArrangement(glui32 method, glui32 size, Window *keyWin);
 
-	virtual void getSize(glui32 *width, glui32 *height);
+	virtual void getSize(glui32 *width, glui32 *height) const;
 
 	virtual void requestCharEvent();
 
 	virtual void requestCharEventUni();
 
 	virtual void setEchoLineEvent(glui32 val) {}
+
+	virtual void requestMouseEvent() {}
+
+	virtual void requestHyperlinkEvent() {}
 };
 typedef Window *winid_t;
 


Commit: b231dee01a92f263c95311d68657f01b003141ca
    https://github.com/scummvm/scummvm/commit/b231dee01a92f263c95311d68657f01b003141ca
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: More window glk methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_graphics.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index c309f72..8f9df23 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -21,7 +21,9 @@
  */
 
 #include "gargoyle/glk.h"
+#include "gargoyle/conf.h"
 #include "gargoyle/events.h"
+#include "gargoyle/picture.h"
 #include "gargoyle/streams.h"
 #include "gargoyle/string.h"
 #include "gargoyle/windows.h"
@@ -169,11 +171,27 @@ winid_t Glk::glk_window_get_sibling(winid_t win) {
 }
 
 void Glk::glk_window_clear(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("window_clear: invalid ref");
+	} else if (win->_lineRequest || win->_lineRequestUni) {
+		if (g_conf->_safeClicks && _events->_forceClick) {
+			glk_cancel_line_event(win, NULL);
+			_events->_forceClick = false;
+
+			win->clear();
+		} else {
+			warning("window_clear: window has pending line request");
+			return;
+		}
+	}
 }
 
 void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
-	// TODO
+	if (!win) {
+		warning("window_move_cursor: invalid ref");
+	} else {
+		win->moveCursor(Common::Point(xpos, ypos));
+	}
 }
 
 strid_t Glk::glk_window_get_stream(winid_t win) {
@@ -583,8 +601,14 @@ glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numch
 #ifdef GLK_MODULE_IMAGE
 
 glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
-	// TODO
-	return 0;
+/*
+	if (!win) {
+		warning("image_draw: invalid ref");
+	} else if (g_conf->_graphics) {
+		win->imageDraw(image, val1, val2, );
+	}
+	*/
+	return false;
 }
 
 glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image,
@@ -594,22 +618,44 @@ glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image,
 }
 
 glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
-	// TODO
-	return 0;
+	if (!g_conf->_graphics)
+		return false;
+
+	Picture *pic = Picture::load(image);
+	if (!pic)
+		return false;
+
+	if (width)
+		*width = pic->w;
+	if (height)
+		*height = pic->h;
+
+	return true;
 }
 
 void Glk::glk_window_flow_break(winid_t win) {
-	// TODO
+	if (!win) {
+		warning("window_erase_rect: invalid ref");
+	} else {
+		win->flowBreak();
+	}
 }
 
-void Glk::glk_window_erase_rect(winid_t win,
-	glsi32 left, glsi32 top, glui32 width, glui32 height) {
-	// TODO
+void Glk::glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height) {
+	if (!win) {
+		warning("window_erase_rect: invalid ref");
+	} else {
+		win->eraseRect(false, Common::Rect(left, top, left + width, top + height));
+	}
 }
 
-void Glk::glk_window_fill_rect(winid_t win, glui32 color,
-	glsi32 left, glsi32 top, glui32 width, glui32 height) {
-	// TODO
+void Glk::glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top,
+		glui32 width, glui32 height) {
+	if (!win) {
+		warning("window_fill_rect: invalid ref");
+	} else {
+		win->eraseRect(color, Common::Rect(left, top, left + width, top + height));
+	}
 }
 
 void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index 0ec2bee..0ea5876 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -120,9 +120,8 @@ glui32 GraphicsWindow::drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int s
 	return true;
 }
 
-void GraphicsWindow::eraseRect(int whole, glsi32 x0, glsi32 y0, glui32 width, glui32 height) {
-	int x1 = x0 + width;
-	int y1 = y0 + height;
+void GraphicsWindow::eraseRect(bool whole, const Common::Rect &box) {
+	int x0 = box.left, y0 = box.top, x1 = box.right, y1 = box.bottom;
 	int hx0, hx1, hy0, hy1;
 
 	if (whole) {
@@ -153,10 +152,9 @@ void GraphicsWindow::eraseRect(int whole, glsi32 x0, glsi32 y0, glui32 width, gl
 	touch();
 }
 
-void GraphicsWindow::fillRect(glui32 color, glsi32 x0, glsi32 y0, glui32 width, glui32 height) {
+void GraphicsWindow::fillRect(glui32 color, const Common::Rect &box) {
 	unsigned char col[3];
-	int x1 = x0 + width;
-	int y1 = y0 + height;
+	int x0 = box.left, y0 = box.top, x1 = box.right, y1 = box.bottom;
 	int hx0, hx1, hy0, hy1;
 
 	col[0] = (color >> 16) & 0xff;
@@ -184,12 +182,6 @@ void GraphicsWindow::fillRect(glui32 color, glsi32 x0, glsi32 y0, glui32 width,
 	touch();
 }
 
-void GraphicsWindow::setBackgroundColor(glui32 color) {
-	_bgnd[0] = (color >> 16) & 0xff;
-	_bgnd[1] = (color >> 8) & 0xff;
-	_bgnd[2] = (color >> 0) & 0xff;
-}
-
 void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int height, glui32 linkval) {
 	int dx1, dy1, x1, y1, sx0, sy0, sx1, sy1;
 	int hx0, hx1, hy0, hy1;
@@ -249,4 +241,10 @@ void GraphicsWindow::getSize(glui32 *width, glui32 *height) const {
 	*height = _bbox.height();
 }
 
+void GraphicsWindow::setBackgroundColor(glui32 color) {
+	_bgnd[0] = (color >> 16) & 0xff;
+	_bgnd[1] = (color >> 8) & 0xff;
+	_bgnd[2] = (color >> 0) & 0xff;
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h
index 166b03a..8c2724e 100644
--- a/engines/gargoyle/window_graphics.h
+++ b/engines/gargoyle/window_graphics.h
@@ -35,9 +35,6 @@ class GraphicsWindow : public Window {
 private:
 	void touch();
 
-	void eraseRect(int whole, glsi32 x0, glsi32 y0, glui32 width, glui32 height);
-	void fillRect(glui32 color, glsi32 x0, glsi32 y0, glui32 width, glui32 height);
-	void setBackgroundColor(glui32 color);
 	void drawPicture(Picture *src, int x0, int y0, int width, int height, glui32 linkval);
 public:
 	unsigned char _bgnd[3];
@@ -89,7 +86,13 @@ public:
 	 */
 	virtual void redraw() override;
 
+	virtual void eraseRect(bool whole, const Common::Rect &box) override;
+
+	virtual void fillRect(glui32 color, const Common::Rect &box) override;
+
 	virtual void getSize(glui32 *width, glui32 *height) const override;
+
+	virtual void setBackgroundColor(glui32 color) override;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index c428dac..05705cc 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -296,12 +296,6 @@ glui32 TextBufferWindow::drawPicture(glui32 image, glui32 align, glui32 scaled,
 	return error;
 }
 
-bool TextBufferWindow::flowBreak() {
-	while (_ladjn || _radjn)
-		putCharUni('\n');
-	return true;
-}
-
 void TextBufferWindow::putText(const char *buf, int len, int pos, int oldlen) {
 	int diff = len - oldlen;
 
@@ -1640,6 +1634,11 @@ void TextBufferWindow::getSize(glui32 *width, glui32 *height) const {
 		*height = (_bbox.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
 }
 
+void TextBufferWindow::flowBreak() {
+	while (_ladjn || _radjn)
+		putCharUni('\n');
+}
+
 /*--------------------------------------------------------------------------*/
 
 TextBufferWindow::TextBufferRow::TextBufferRow() : _len(0), _newLine(0), _dirty(false),
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index 774bd66..be9ffcb 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -66,8 +66,6 @@ private:
 	 */
 	void putTextUni(const glui32 *buf, int len, int pos, int oldlen);
 
-	bool flowBreak();
-
 	void acceptLine(glui32 keycode);
 
 	/**
@@ -221,6 +219,8 @@ public:
 	virtual void requestHyperlinkEvent() override { _hyperRequest = true; }
 
 	virtual void cancelCharEvent() override { _charRequest = _charRequestUni = false; }
+
+	virtual void flowBreak() override;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index cc23e47..2f2008c 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -487,6 +487,22 @@ void Window::requestCharEventUni() {
 	warning("requestCharEventUni: window does not support keyboard input");
 }
 
+void Window::flowBreak() {
+	warning("flowBreak: not a text buffer window");
+}
+
+void Window::eraseRect(bool whole, const Common::Rect &box) {
+	warning("eraseRect: not a graphics window");
+}
+
+void Window::fillRect(glui32 color, const Common::Rect &box) {
+	warning("fillRect: not a graphics window");
+}
+
+void Window::setBackgroundColor(glui32 color) {
+	warning("setBackgroundColor: not a graphics window");
+}
+
 void Window::setTerminatorsLineEvent(glui32 *keycodes, glui32 count) {
 	if (dynamic_cast<TextBufferWindow *>(this) || dynamic_cast<TextGridWindow *>(this)) {
 		delete _lineTerminatorsBase;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index b7d701f..03ab1b0 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -390,6 +390,14 @@ public:
 	virtual void requestMouseEvent() {}
 
 	virtual void requestHyperlinkEvent() {}
+
+	virtual void flowBreak();
+
+	virtual void eraseRect(bool whole, const Common::Rect &box);
+
+	virtual void fillRect(glui32 color, const Common::Rect &box);
+
+	virtual void setBackgroundColor(glui32 color);
 };
 typedef Window *winid_t;
 


Commit: d1282498542b87ee0d807e5d4a2a09232a8dc677
    https://github.com/scummvm/scummvm/commit/d1282498542b87ee0d807e5d4a2a09232a8dc677
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Miscellaneous glk methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 8f9df23..d81ddf5 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -27,16 +27,39 @@
 #include "gargoyle/streams.h"
 #include "gargoyle/string.h"
 #include "gargoyle/windows.h"
+#include "gargoyle/window_graphics.h"
+#include "gargoyle/window_text_buffer.h"
 #include "gargoyle/window_pair.h"
 
+
 namespace Gargoyle {
 
 Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
-	GargoyleEngine(syst, gameDesc), _gliFirstEvent(false) {
+		GargoyleEngine(syst, gameDesc), _gliFirstEvent(false) {
+	// Set uppercase/lowercase tables
+	int ix, res;
+	for (ix = 0; ix < 256; ix++) {
+		_charToupperTable[ix] = ix;
+		_charTolowerTable[ix] = ix;
+	}
+
+	for (ix = 0; ix < 256; ix++) {
+		if (ix >= 'A' && ix <= 'Z')
+			res = ix + ('a' - 'A');
+		else if (ix >= 0xC0 && ix <= 0xDE && ix != 0xD7)
+			res = ix + 0x20;
+		else
+			res = 0;
+
+		if (res) {
+			_charTolowerTable[ix] = res;
+			_charToupperTable[res] = ix;
+		}
+	}
 }
 
 void Glk::glk_exit(void) {
-	// TODO
+	quitGame();
 }
 
 void Glk::glk_set_interrupt_handler(void(*func)(void)) {
@@ -44,27 +67,109 @@ void Glk::glk_set_interrupt_handler(void(*func)(void)) {
 }
 
 void Glk::glk_tick(void) {
-	// TODO
+	// Nothing needed
 }
 
-glui32 Glk::glk_gestalt(glui32 sel, glui32 val) {
-	// TODO
-	return 0;
+glui32 Glk::glk_gestalt(glui32 id, glui32 val) {
+	return glk_gestalt_ext(id, val, nullptr, 0);
 }
 
-glui32 Glk::glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) {
-	// TODO
-	return 0;
+glui32 Glk::glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen) {
+	switch (id) {
+	case gestalt_Version:
+		return 0x00000703;
+
+	case gestalt_LineInput:
+		if (val >= 32 && val < 0x10ffff)
+			return true;
+		else
+			return false;
+
+	case gestalt_CharInput:
+		if (val >= 32 && val < 0x10ffff)
+			return true;
+		else if (val == keycode_Return)
+			return true;
+		else
+			return false;
+
+	case gestalt_CharOutput:
+		if (val >= 32 && val < 0x10ffff) {
+			if (arr && arrlen >= 1)
+				arr[0] = 1;
+			return gestalt_CharOutput_ExactPrint;
+		} else {
+			// cheaply, we don't do any translation of printed characters,
+			// so the output is always one character even if it's wrong.
+			if (arr && arrlen >= 1)
+				arr[0] = 1;
+			return gestalt_CharOutput_CannotPrint;
+		}
+
+	case gestalt_MouseInput:
+		if (val == wintype_TextGrid)
+			return true;
+		if (val == wintype_Graphics)
+			return true;
+		return false;
+
+	case gestalt_Timer:
+		return true;
+
+	case gestalt_Graphics:
+	case gestalt_GraphicsTransparency:
+		return g_conf->_graphics;
+
+	case gestalt_DrawImage:
+		if (val == wintype_TextBuffer)
+			return g_conf->_graphics;
+		if (val == wintype_Graphics)
+			return g_conf->_graphics;
+		return false;
+
+	case gestalt_Sound:
+	case gestalt_SoundVolume:
+	case gestalt_SoundMusic:
+	case gestalt_SoundNotify:
+		return g_conf->_sound;
+
+	case gestalt_Sound2:
+		return false;
+
+	case gestalt_Unicode:
+		return true;
+	case gestalt_UnicodeNorm:
+		return true;
+
+	case gestalt_Hyperlinks:
+		return true;
+	case gestalt_HyperlinkInput:
+		return true;
+
+	case gestalt_LineInputEcho:
+		return true;
+	case gestalt_LineTerminators:
+		return true;
+	case gestalt_LineTerminatorKey:
+		return Window::checkTerminator(val);
+
+	case gestalt_DateTime:
+		return true;
+
+	case gestalt_GarglkText:
+		return true;
+
+	default:
+		return false;
+	}
 }
 
 unsigned char Glk::glk_char_to_lower(unsigned char ch) {
-	// TODO
-	return '\0';
+	return _charTolowerTable[ch];
 }
 
 unsigned char Glk::glk_char_to_upper(unsigned char ch) {
-	// TODO
-	return '\0';
+	return _charToupperTable[ch];
 }
 
 winid_t Glk::glk_window_get_root(void) const {
@@ -224,8 +329,7 @@ void Glk::glk_set_window(winid_t win) {
 	_streams->setCurrent(win ? win->_stream : nullptr);
 }
 
-strid_t Glk::glk_stream_open_file(frefid_t fileref, FileMode fmode,
-	glui32 rock) {
+strid_t Glk::glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock) {
 	// TODO
 	return nullptr;
 }
@@ -601,20 +705,36 @@ glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numch
 #ifdef GLK_MODULE_IMAGE
 
 glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
-/*
 	if (!win) {
 		warning("image_draw: invalid ref");
 	} else if (g_conf->_graphics) {
-		win->imageDraw(image, val1, val2, );
+		TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
+		GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
+
+		if (textWin)
+			textWin->drawPicture(image, val1, false, 0, 0);
+		else if (gfxWin)
+			gfxWin->drawPicture(image, val1, val2, false, 0, 0);
 	}
-	*/
+
 	return false;
 }
 
-glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image,
-	glsi32 val1, glsi32 val2, glui32 width, glui32 height) {
-	// TODO
-	return 0;
+glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2,
+		glui32 width, glui32 height) {
+	if (!win) {
+		warning("image_draw_scaled: invalid ref");
+	} else if (g_conf->_graphics) {
+		TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
+		GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
+
+		if (textWin)
+			textWin->drawPicture(image, val1, true, width, height);
+		else if (gfxWin)
+			gfxWin->drawPicture(image, val1, val2, true, width, height);
+	}
+
+	return false;
 }
 
 glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
@@ -659,7 +779,11 @@ void Glk::glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 to
 }
 
 void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
-	// TODO
+	if (!win) {
+		warning("window_set_background_color: invalid ref");
+	} else {
+		win->setBackgroundColor(color);
+	}
 }
 
 #endif /* GLK_MODULE_IMAGE */
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 0e97600..e1923b7 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -36,6 +36,8 @@ namespace Gargoyle {
 class Glk : public GargoyleEngine {
 private:
 	bool _gliFirstEvent;
+	unsigned char _charTolowerTable[256];
+	unsigned char _charToupperTable[256];
 private:
 	/**
 	 * Pick first window which might want input. This is called after every keystroke.
@@ -53,8 +55,8 @@ public:
 	void glk_set_interrupt_handler(void(*func)(void));
 	void glk_tick(void);
 
-	glui32 glk_gestalt(glui32 sel, glui32 val);
-	glui32 glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen);
+	glui32 glk_gestalt(glui32 id, glui32 val);
+	glui32 glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen);
 
 	unsigned char glk_char_to_lower(unsigned char ch);
 	unsigned char glk_char_to_upper(unsigned char ch);
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 03ab1b0..2c03c15 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -275,8 +275,8 @@ public:
 	byte _fgColor[3];
 
 	gidispatch_rock_t _dispRock;
-protected:
-	bool checkTerminator(glui32 ch);
+public:
+	static bool checkTerminator(glui32 ch);
 public:
 	/**
 	 * Constructor
@@ -369,8 +369,6 @@ public:
 
 	void setTerminatorsLineEvent(glui32 *keycodes, glui32 count);
 
-	virtual  glui32 drawPicture(glui32 image, glui32 align, glui32 scaled, glui32 width, glui32 height) { return false; }
-
 	virtual void acceptReadLine(glui32 arg);
 
 	virtual void acceptReadChar(glui32 arg);


Commit: 485e899312d1cf092e7cb52927593be21ef0f263
    https://github.com/scummvm/scummvm/commit/485e899312d1cf092e7cb52927593be21ef0f263
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Adding glk stream methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index d81ddf5..1993375 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -335,12 +335,11 @@ strid_t Glk::glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock)
 }
 
 strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock) {
-	// TODO
-	return nullptr;
+	return _streams->addMemoryStream(buf, buflen, fmode, rock, false);
 }
 
 void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
-	// TODO
+	str->close(result);
 }
 
 strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) const {
@@ -356,22 +355,29 @@ glui32 Glk::glk_stream_get_rock(strid_t str) const {
 	return str->getRock();
 }
 
-void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) {
-	// TODO
+void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode) {
+	if (!str) {
+		warning("stream_set_position: invalid ref");
+	} else {
+		str->setPosition(pos, seekMode);
+	}
 }
 
-glui32 Glk::glk_stream_get_position(strid_t str) {
-	// TODO
-	return 0;
+glui32 Glk::glk_stream_get_position(strid_t str) const {
+	if (!str) {
+		warning("stream_get_position: invalid ref");
+		return 0;
+	} else {
+		return str->getPosition();
+	}
 }
 
 void Glk::glk_stream_set_current(strid_t str) {
-	// TODO
+	_streams->setCurrent(str);
 }
 
 strid_t Glk::glk_stream_get_current(void) {
-	// TODO
-	return nullptr;
+	return _streams->getCurrent();
 }
 
 void Glk::glk_put_char(unsigned char ch) {
@@ -387,32 +393,40 @@ void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
 }
 
 void Glk::glk_put_string(const char *s) {
-	// TODO
+	_streams->getCurrent()->putBuffer(s, strlen(s));
 }
 
 void Glk::glk_put_string_stream(strid_t str, const char *s) {
-	// TODO
+	str->putBuffer(s, strlen(s));
 }
 
 void Glk::glk_put_buffer(char *buf, glui32 len) {
-	// TODO
+	_streams->getCurrent()->putBuffer(buf, len);
 }
 
 void Glk::glk_put_buffer_stream(strid_t str, const char *buf, glui32 len) {
-	// TODO
+	str->putBuffer(buf, len);
 }
 
 void Glk::glk_set_style(glui32 styl) {
-	// TODO
+	_streams->getCurrent()->setStyle(styl);
 }
 
 void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
-	// TODO
+	if (str) {
+		str->setStyle(styl);
+	} else {
+		warning("set_style_stream: invalid ref");
+	}
 }
 
 glsi32 Glk::glk_get_char_stream(strid_t str) {
-	// TODO
-	return 0;
+	if (str) {
+		warning("get_char_stream: invalid ref");
+		return -1;
+	} else {
+		return str->getChar();
+	}
 }
 
 glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
@@ -661,7 +675,7 @@ strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 r
 }
 
 strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock) {
-	return _streams->addMemoryStream(buf, buflen, fmode, rock, false);
+	return _streams->addMemoryStream(buf, buflen, fmode, rock, true);
 }
 
 void Glk::glk_request_char_event_uni(winid_t win) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index e1923b7..3b49421 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -96,8 +96,8 @@ public:
 	void glk_stream_close(strid_t str, stream_result_t *result);
 	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const;
 	glui32 glk_stream_get_rock(strid_t str) const;
-	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode);
-	glui32 glk_stream_get_position(strid_t str);
+	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode);
+	glui32 glk_stream_get_position(strid_t str) const;
 	void glk_stream_set_current(strid_t str);
 	strid_t glk_stream_get_current(void);
 
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 81d05ac..a521548 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -46,11 +46,6 @@ class Window;
 #define GLK_MODULE_DATETIME
 #define GLK_MODULE_GARGLKTEXT
 
-/**
- * These types are opaque object identifiers. They're pointers to opaque
- * C structures, which are defined differently by each library.
- */
-typedef struct glk_fileref_struct *frefid_t;
 typedef struct glk_schannel_struct *schanid_t;
 
 /**
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 7499991..6a73c5b 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -144,6 +144,18 @@ void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
 		_window->_echoStream->putBufferUni(buf, len);
 }
 
+void WindowStream::setStyle(glui32 val) {
+	if (!_writable)
+		return;
+
+	if (val >= style_NUMSTYLES)
+		val = 0;
+
+	_window->_attr.style = val;
+	if (_window->_echoStream)
+		_window->_echoStream->setStyle(val);
+}
+
 /*--------------------------------------------------------------------------*/
 
 MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
@@ -297,6 +309,66 @@ void MemoryStream::putBufferUni(const uint32 *buf, size_t len) {
 	}
 }
 
+glui32 MemoryStream::getPosition() const {
+	if (_unicode)
+		return ((glui32 *)_bufPtr - (glui32 *)_buf);
+	else
+		return ((unsigned char *)_bufPtr - (unsigned char *)_buf);
+}
+
+void MemoryStream::setPosition(glui32 pos, glui32 seekMode) {
+	if (!_unicode)
+	{
+		if (seekMode == seekmode_Current)
+			pos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + pos;
+		else if (seekMode == seekmode_End)
+			pos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + pos;
+		else
+			/* pos = pos */;
+		if (pos < 0)
+			pos = 0;
+		if (pos > (glui32)((unsigned char *)_bufEof - (unsigned char *)_buf))
+			pos = ((unsigned char *)_bufEof - (unsigned char *)_buf);
+		_bufPtr = (unsigned char *)_buf + pos;
+	} else {
+		if (seekMode == seekmode_Current)
+			pos = ((glui32 *)_bufPtr - (glui32 *)_buf) + pos;
+		else if (seekMode == seekmode_End)
+			pos = ((glui32 *)_bufEof - (glui32 *)_buf) + pos;
+
+		if (pos < 0)
+			pos = 0;
+		if (pos > (glui32)((glui32 *)_bufEof - (glui32 *)_buf))
+			pos = ((glui32 *)_bufEof - (glui32 *)_buf);
+		_bufPtr = (glui32 *)_buf + pos;
+	}
+}
+
+glsi32 MemoryStream::getChar() {
+	if (!_readable)
+		return -1;
+
+	if (_bufPtr < _bufEnd) {
+		if (!_unicode) {
+			unsigned char ch;
+			ch = *((unsigned char *)_bufPtr);
+			_bufPtr = ((unsigned char *)_bufPtr) + 1;
+			_readCount++;
+			return ch;
+		} else {
+			glui32 ch;
+			ch = *((glui32 *)_bufPtr);
+			_bufPtr = ((glui32 *)_bufPtr) + 1;
+			_readCount++;
+			if (ch > 0xff)
+				ch = '?';
+			return ch;
+		}
+	} else {
+		return -1;
+	}
+}
+
 /*--------------------------------------------------------------------------*/
 
 FileStream::FileStream(Streams *streams, uint32 rock, bool unicode) :
@@ -409,6 +481,148 @@ void FileStream::putCharUtf8(glui32 val) {
 	}
 }
 
+glsi32 FileStream::getCharUtf8() {
+	glui32 res;
+	glui32 val0, val1, val2, val3;
+
+	if (_inFile->eos())
+		return -1;
+	val0 = _inFile->readByte();
+	if (val0 < 0x80) {
+		res = val0;
+		return res;
+	}
+
+	if ((val0 & 0xe0) == 0xc0) {
+		if (_inFile->eos()) {
+			warning("incomplete two-byte character");
+			return -1;
+		}
+
+		val1 = _inFile->readByte();
+		if ((val1 & 0xc0) != 0x80) {
+			warning("malformed two-byte character");
+			return '?';
+		}
+
+		res = (val0 & 0x1f) << 6;
+		res |= (val1 & 0x3f);
+		return res;
+	}
+
+	if ((val0 & 0xf0) == 0xe0) {
+		val1 = _inFile->readByte();
+		val2 = _inFile->readByte();
+		if (_inFile->eos()) {
+			warning("incomplete three-byte character");
+			return -1;
+		}
+		if ((val1 & 0xc0) != 0x80) {
+			warning("malformed three-byte character");
+			return '?';
+		}
+		if ((val2 & 0xc0) != 0x80) {
+			warning("malformed three-byte character");
+			return '?';
+		}
+
+		res = (((val0 & 0xf) << 12) & 0x0000f000);
+		res |= (((val1 & 0x3f) << 6) & 0x00000fc0);
+		res |= (((val2 & 0x3f)) & 0x0000003f);
+		return res;
+	}
+
+	if ((val0 & 0xf0) == 0xf0) {
+		if ((val0 & 0xf8) != 0xf0) {
+			warning("malformed four-byte character");
+			return '?';
+		}
+
+		val1 = _inFile->readByte();
+		val2 = _inFile->readByte();
+		val3 = _inFile->readByte();
+		if (_inFile->eos()) {
+			warning("incomplete four-byte character");
+			return -1;
+		}
+		if ((val1 & 0xc0) != 0x80) {
+			warning("malformed four-byte character");
+			return '?';
+		}
+		if ((val2 & 0xc0) != 0x80) {
+			warning("malformed four-byte character");
+			return '?';
+		}
+		if ((val3 & 0xc0) != 0x80) {
+			warning("malformed four-byte character");
+			return '?';
+		}
+
+		res = (((val0 & 0x7) << 18) & 0x1c0000);
+		res |= (((val1 & 0x3f) << 12) & 0x03f000);
+		res |= (((val2 & 0x3f) << 6) & 0x000fc0);
+		res |= (((val3 & 0x3f)) & 0x00003f);
+		return res;
+	}
+
+	warning("malformed character");
+	return '?';
+}
+
+glui32 FileStream::getPosition() const {
+	return _outFile->pos();
+}
+
+void FileStream::setPosition(glui32 pos, glui32 seekMode) {
+	_lastOp = 0;
+	if (_unicode)
+		pos *= 4;
+	
+	error("FileStream::setPosition - seek not yet supported");
+//	fseek(str->file, pos, ((seekmode == seekmode_Current) ? 1 :
+	//		((seekmode == seekmode_End) ? 2 : 0)));
+}
+
+glsi32 FileStream::getChar() {
+	if (!_readable)
+		return -1;
+
+	ensureOp(filemode_Read);
+	int res;
+	if (!_unicode) {
+		res = _inFile->readByte();
+	} else if (_textFile) {
+		res = getCharUtf8();
+	} else {
+		glui32 ch;
+		res = _inFile->readByte();
+		if (_inFile->eos())
+			return -1;
+		ch = (res & 0xFF);
+		res = _inFile->readByte();
+		if (_inFile->eos())
+			return -1;
+		ch = (ch << 8) | (res & 0xFF);
+		res = _inFile->readByte();
+		if (_inFile->eos())
+			return -1;
+		ch = (ch << 8) | (res & 0xFF);
+		res = _inFile->readByte();
+		if (_inFile->eos())
+			return -1;
+		ch = (ch << 8) | (res & 0xFF);
+		res = ch;
+	}
+	if (res != -1) {
+		_readCount++;
+		if (res >= 0x100)
+			return '?';
+		return (glsi32)res;
+	} else {
+		return -1;
+	}
+}
+
 /*--------------------------------------------------------------------------*/
 
 Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) {
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 51ab484..8b7da6f 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -32,6 +32,15 @@ namespace Gargoyle {
 class Window;
 class Streams;
 
+struct FileReference {
+	glui32 _rock;
+	Common::String _filename;
+	int _fileType;
+	int _textMode;
+	gidispatch_rock_t _dispRock;
+};
+typedef FileReference *frefid_t;
+
 struct StreamResult {
 	uint32 _readCount;
 	uint32 _writeCount;
@@ -117,6 +126,14 @@ public:
 		putBufferUni(buf, len);
 		putCharUni('\n');
 	}
+
+	virtual glui32 getPosition() const { return 0; }
+
+	virtual void setPosition(glui32 pos, glui32 seekMode) {}
+
+	virtual void setStyle(glui32 val) {}
+
+	virtual glsi32 getChar() { return -1; }
 };
 typedef Stream *strid_t;
 
@@ -157,6 +174,8 @@ public:
 	 * Write a unicode character
 	 */
 	virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+	virtual void setStyle(glui32 val) override;
 };
 
 /**
@@ -194,6 +213,12 @@ public:
 	 * Write a unicode character
 	 */
 	virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+	virtual glui32 getPosition() const override;
+
+	virtual void setPosition(glui32 pos, glui32 seekMode) override;
+
+	virtual glsi32 getChar() override;
 };
 
 /**
@@ -215,6 +240,11 @@ private:
 	 * Put a UTF8 character
 	 */
 	void putCharUtf8(glui32 val);
+
+	/**
+	 * Get a UTF8 character
+	 */
+	glsi32 getCharUtf8();
 public:
 	/**
 	 * Constructor
@@ -240,6 +270,12 @@ public:
 	 * Write a unicode character
 	 */
 	virtual void putBufferUni(const uint32 *buf, size_t len) override;
+
+	virtual glui32 getPosition() const override;
+
+	virtual void setPosition(glui32 pos, glui32 seekMode) override;
+
+	virtual glsi32 getChar() override;
 };
 
 /**


Commit: 734366ce8919824336a1bbe543130e9d96b1e464
    https://github.com/scummvm/scummvm/commit/734366ce8919824336a1bbe543130e9d96b1e464
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added more glk stream commands

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 1993375..2f26aee 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -655,13 +655,21 @@ void Glk::glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len)
 }
 
 glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
-	// TODO
-	return 0;
+	if (str) {
+		return str->getCharUni();
+	} else {
+		warning("get_char_stream_uni: invalid ref");
+		return -1;
+	}
 }
 
 glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	// TODO
-	return 0;
+	if (str) {
+		return str->getBufferUni(buf, len);
+	} else {
+		warning("get_buffer_stream_uni: invalid ref");
+		return 0;
+	}
 }
 
 glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 6a73c5b..42b89eb 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -369,6 +369,81 @@ glsi32 MemoryStream::getChar() {
 	}
 }
 
+glsi32 MemoryStream::getCharUni() {
+	if (!_readable)
+		return -1;
+
+	if (_bufPtr < _bufEnd) {
+		if (!_unicode) {
+			unsigned char ch;
+			ch = *((unsigned char *)_bufPtr);
+			_bufPtr = ((unsigned char *)_bufPtr) + 1;
+			_readCount++;
+			return ch;
+		} else {
+			glui32 ch;
+			ch = *((glui32 *)_bufPtr);
+			_bufPtr = ((glui32 *)_bufPtr) + 1;
+			_readCount++;
+			return ch;
+		}
+	} else {
+		return -1;
+	}
+}
+
+glui32 MemoryStream::getBufferUni(glui32 *buf, glui32 len) {
+	if (!_readable)
+		return 0;
+
+	if (_bufPtr >= _bufEnd) {
+		len = 0;
+	} else {
+		if (!_unicode) {
+			unsigned char *bp = (unsigned char *)_bufPtr;
+			if (bp + len > (unsigned char *)_bufEnd)
+			{
+				glui32 lx;
+				lx = (bp + len) - (unsigned char *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+			if (len) {
+				glui32 i;
+				for (i = 0; i < len; i++)
+					buf[i] = bp[i];
+				bp += len;
+				if (bp >(unsigned char *)_bufEof)
+					_bufEof = bp;
+			}
+			_readCount += len;
+			_bufPtr = bp;
+		} else {
+			glui32 *bp = (glui32 *)_bufPtr;
+			if (bp + len > (glui32 *)_bufEnd) {
+				glui32 lx;
+				lx = (bp + len) - (glui32 *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+			if (len) {
+				memcpy(buf, bp, len * 4);
+				bp += len;
+				if (bp >(glui32 *)_bufEof)
+					_bufEof = bp;
+			}
+			_readCount += len;
+			_bufPtr = bp;
+		}
+	}
+
+	return len;
+}
+
 /*--------------------------------------------------------------------------*/
 
 FileStream::FileStream(Streams *streams, uint32 rock, bool unicode) :
@@ -623,6 +698,103 @@ glsi32 FileStream::getChar() {
 	}
 }
 
+glsi32 FileStream::getCharUni() {
+	if (!_readable)
+		return -1;
+
+	ensureOp(filemode_Read);
+	int res;
+	if (!_unicode) {
+		res = _inFile->readByte();
+	} else if (_textFile) {
+		res = getCharUtf8();
+	} else {
+		glui32 ch;
+		res = _inFile->readByte();
+		if (res == -1)
+			return -1;
+		ch = (res & 0xFF);
+		res = _inFile->readByte();
+		if (res == -1)
+			return -1;
+		ch = (ch << 8) | (res & 0xFF);
+		res = _inFile->readByte();
+		if (res == -1)
+			return -1;
+		ch = (ch << 8) | (res & 0xFF);
+		res = _inFile->readByte();
+		if (res == -1)
+			return -1;
+		ch = (ch << 8) | (res & 0xFF);
+		res = ch;
+	}
+	if (res != -1) {
+		_readCount++;
+		return (glsi32)res;
+	} else {
+		return -1;
+	}
+}
+
+
+glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
+	if (!_readable)
+		return 0;
+
+	ensureOp(filemode_Read);
+	if (!_unicode) {
+		glui32 lx;
+		for (lx = 0; lx<len; lx++) {
+			int res;
+			glui32 ch;
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (res & 0xFF);
+			_readCount++;
+			buf[lx] = ch;
+		}
+		return lx;
+	} else if (_textFile) {
+		glui32 lx;
+		for (lx = 0; lx<len; lx++) {
+			glui32 ch;
+			ch = getCharUtf8();
+			if (ch == -1)
+				break;
+			_readCount++;
+			buf[lx] = ch;
+		}
+		return lx;
+	} else {
+		glui32 lx;
+		for (lx = 0; lx<len; lx++)
+		{
+			int res;
+			glui32 ch;
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (res & 0xFF);
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			_readCount++;
+			buf[lx] = ch;
+		}
+		return lx;
+	}
+}
+
 /*--------------------------------------------------------------------------*/
 
 Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) {
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 8b7da6f..ff0f9be 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -133,7 +133,20 @@ public:
 
 	virtual void setStyle(glui32 val) {}
 
+	/**
+	 * Get a character from the stream
+	 */
 	virtual glsi32 getChar() { return -1; }
+
+	/**
+	 * Get a unicode character from the stream
+	 */
+	virtual glsi32 getCharUni() { return -1; }
+
+	/**
+	 * Get a unicode buffer
+	 */
+	virtual glui32 getBufferUni(glui32 *buf, glui32 len) { return 0; }
 };
 typedef Stream *strid_t;
 
@@ -218,7 +231,20 @@ public:
 
 	virtual void setPosition(glui32 pos, glui32 seekMode) override;
 
+	/**
+	 * Get a character from the stream
+	 */
 	virtual glsi32 getChar() override;
+
+	/**
+	 * Get a unicode character from the stream
+	 */
+	virtual glsi32 getCharUni() override;
+
+	/**
+	 * Get a unicode buffer
+	 */
+	virtual glui32 getBufferUni(glui32 *buf, glui32 len) override;
 };
 
 /**
@@ -275,7 +301,20 @@ public:
 
 	virtual void setPosition(glui32 pos, glui32 seekMode) override;
 
+	/**
+	 * Get a character from the stream
+	 */
 	virtual glsi32 getChar() override;
+
+	/**
+	 * Get a unicode character from the stream
+	 */
+	virtual glsi32 getCharUni() override;
+
+	/**
+	 * Get a unicode buffer
+	 */
+	virtual glui32 getBufferUni(glui32 *buf, glui32 len) override;
 };
 
 /**


Commit: 4bb596792005f3a696a29fdd038809133bfb8119
    https://github.com/scummvm/scummvm/commit/4bb596792005f3a696a29fdd038809133bfb8119
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: More glk stream functions

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 2f26aee..35b8563 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -673,8 +673,12 @@ glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
 }
 
 glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	// TODO
-	return 0;
+	if (str) {
+		return str->getLineUni(buf, len);
+	} else  {
+		warning("get_line_stream_uni: invalid ref");
+		return -1;
+	}
 }
 
 strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock) {
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 42b89eb..1942ddf 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -444,6 +444,60 @@ glui32 MemoryStream::getBufferUni(glui32 *buf, glui32 len) {
 	return len;
 }
 
+glui32 MemoryStream::getLineUni(glui32 *ubuf, glui32 len) {
+	bool gotNewline;
+	int lx;
+
+	if (!_readable || len == 0)
+		return 0;
+
+	len -= 1; // for the terminal null
+	if (!_unicode) {
+		if (_bufPtr >= _bufEnd) {
+			len = 0;
+		} else {
+			if ((char *)_bufPtr + len > (char *)_bufEnd) {
+				lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
+				if (lx < (int)len)
+					len -= lx;
+				else
+					len = 0;
+			}
+		}
+		gotNewline = false;
+		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+			ubuf[lx] = ((unsigned char *)_bufPtr)[lx];
+			gotNewline = (ubuf[lx] == '\n');
+		}
+		ubuf[lx] = '\0';
+		_bufPtr = ((unsigned char *)_bufPtr) + lx;
+	} else {
+		if (_bufPtr >= _bufEnd) {
+			len = 0;
+		} else {
+			if ((glui32 *)_bufPtr + len > (glui32 *)_bufEnd) {
+				lx = ((glui32 *)_bufPtr + len) - (glui32 *)_bufEnd;
+				if (lx < (int)len)
+					len -= lx;
+				else
+					len = 0;
+			}
+		}
+		gotNewline = false;
+		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+			glui32 ch;
+			ch = ((glui32 *)_bufPtr)[lx];
+			ubuf[lx] = ch;
+			gotNewline = (ch == '\n');
+		}
+		ubuf[lx] = '\0';
+		_bufPtr = ((glui32 *)_bufPtr) + lx;
+	}
+
+	_readCount += lx;
+	return lx;
+}
+
 /*--------------------------------------------------------------------------*/
 
 FileStream::FileStream(Streams *streams, uint32 rock, bool unicode) :
@@ -795,6 +849,75 @@ glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 	}
 }
 
+glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
+	bool gotNewline;
+	int lx;
+
+	if (!_readable || len == 0)
+		return 0;
+
+	ensureOp(filemode_Read);
+	if (!_unicode) {
+		len -= 1; // for the terminal null
+		gotNewline = false;
+		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+			int res;
+			glui32 ch;
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (res & 0xFF);
+			_readCount++;
+			ubuf[lx] = ch;
+			gotNewline = (ch == '\n');
+		}
+		ubuf[lx] = '\0';
+		return lx;
+	} else if (_textFile) {
+		len -= 1; /* for the terminal null */
+		gotNewline = false;
+		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+			glui32 ch;
+			ch = getCharUtf8();
+			if (ch == -1)
+				break;
+			_readCount++;
+			ubuf[lx] = ch;
+			gotNewline = (ch == '\n');
+		}
+		ubuf[lx] = '\0';
+		return lx;
+	} else {
+		len -= 1; // for the terminal null
+		gotNewline = false;
+		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
+			int res;
+			glui32 ch;
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (res & 0xFF);
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			res = _inFile->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			_readCount++;
+			ubuf[lx] = ch;
+			gotNewline = (ch == '\n');
+		}
+		ubuf[lx] = '\0';
+		return lx;
+	}
+}
+
 /*--------------------------------------------------------------------------*/
 
 Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) {
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index ff0f9be..77ae8cb 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -147,6 +147,12 @@ public:
 	 * Get a unicode buffer
 	 */
 	virtual glui32 getBufferUni(glui32 *buf, glui32 len) { return 0; }
+
+	/**
+	 * Get a unicode line
+	 */
+	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) { return 0; }
+
 };
 typedef Stream *strid_t;
 
@@ -245,6 +251,11 @@ public:
 	 * Get a unicode buffer
 	 */
 	virtual glui32 getBufferUni(glui32 *buf, glui32 len) override;
+
+	/**
+	 * Get a unicode line
+	 */
+	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) override;
 };
 
 /**
@@ -315,6 +326,11 @@ public:
 	 * Get a unicode buffer
 	 */
 	virtual glui32 getBufferUni(glui32 *buf, glui32 len) override;
+
+	/**
+	 * Get a unicode line
+	 */
+	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) override;
 };
 
 /**


Commit: 98f1ddc968152df85790380aef73f8cc6ca50f13
    https://github.com/scummvm/scummvm/commit/98f1ddc968152df85790380aef73f8cc6ca50f13
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added unicode case methods

Changed paths:
  A engines/gargoyle/unicode.cpp
  A engines/gargoyle/unicode.h
  A engines/gargoyle/unicode_gen.cpp
  A engines/gargoyle/unicode_gen.h
  R engines/gargoyle/string.cpp
  R engines/gargoyle/string.h
    engines/gargoyle/conf.cpp
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/module.mk
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 125e9f4..e2b8d04 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -22,7 +22,7 @@
 
 #include "gargoyle/conf.h"
 #include "gargoyle/fonts.h"
-#include "gargoyle/string.h"
+#include "gargoyle/unicode.h"
 #include "gargoyle/windows.h"
 #include "common/config-manager.h"
 #include "common/system.h"
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 35b8563..c385482 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -25,7 +25,7 @@
 #include "gargoyle/events.h"
 #include "gargoyle/picture.h"
 #include "gargoyle/streams.h"
-#include "gargoyle/string.h"
+#include "gargoyle/unicode.h"
 #include "gargoyle/windows.h"
 #include "gargoyle/window_graphics.h"
 #include "gargoyle/window_text_buffer.h"
@@ -576,8 +576,6 @@ void Glk::glk_cancel_mouse_event(winid_t win) {
 	}
 }
 
-#ifdef GLK_MODULE_LINE_ECHO
-
 void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
 	if (!win) {
 		warning("set_echo_line_event: invalid ref");
@@ -586,10 +584,6 @@ void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
 	}
 }
 
-#endif /* GLK_MODULE_LINE_ECHO */
-
-#ifdef GLK_MODULE_LINE_TERMINATORS
-
 void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
 	if (!win) {
 		warning("set_terminators_line_event: invalid ref");
@@ -598,24 +592,17 @@ void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 c
 	}
 }
 
-#endif /* GLK_MODULE_LINE_TERMINATORS */
-
-#ifdef GLK_MODULE_UNICODE
-
 glui32 Glk::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	// TODO
-	return 0;
+	return bufferChangeCase(buf, len, numchars, CASE_LOWER, COND_ALL, true);
 }
 
 glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	// TODO
-	return 0;
+	return bufferChangeCase(buf, len, numchars, CASE_UPPER, COND_ALL, true);
 }
 
 glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
 	glui32 numchars, glui32 lowerrest) {
-	// TODO
-	return 0;
+	return bufferChangeCase(buf, len, numchars, CASE_TITLE, COND_LINESTART, lowerrest);
 }
 
 void Glk::glk_put_char_uni(glui32 ch) {
@@ -677,7 +664,7 @@ glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
 		return str->getLineUni(buf, len);
 	} else  {
 		warning("get_line_stream_uni: invalid ref");
-		return -1;
+		return (glui32 )-1;
 	}
 }
 
@@ -712,10 +699,6 @@ void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, gl
 	}
 }
 
-#endif /* GLK_MODULE_UNICODE */
-
-#ifdef GLK_MODULE_UNICODE_NORM
-
 glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
 	glui32 numchars) {
 	// TODO
@@ -726,10 +709,6 @@ glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numch
 	return 0;
 }
 
-#endif /* GLK_MODULE_UNICODE_NORM */
-
-#ifdef GLK_MODULE_IMAGE
-
 glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
 	if (!win) {
 		warning("image_draw: invalid ref");
@@ -812,10 +791,6 @@ void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
 	}
 }
 
-#endif /* GLK_MODULE_IMAGE */
-
-#ifdef GLK_MODULE_SOUND
-
 schanid_t Glk::glk_schannel_create(glui32 rock) {
 	// TODO
 	return nullptr;
@@ -857,8 +832,6 @@ void Glk::glk_sound_load_hint(glui32 snd, glui32 flag) {
 	// TODO
 }
 
-#ifdef GLK_MODULE_SOUND2
-
 schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
 	// TODO
 	return nullptr;
@@ -883,11 +856,6 @@ void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
 	// TODO
 }
 
-#endif /* GLK_MODULE_SOUND2 */
-#endif /* GLK_MODULE_SOUND */
-
-#ifdef GLK_MODULE_HYPERLINKS
-
 void Glk::glk_set_hyperlink(glui32 linkval) {
 	// TODO
 }
@@ -908,10 +876,6 @@ void Glk::glk_cancel_hyperlink_event(winid_t win) {
 	// TODO
 }
 
-#endif /* GLK_MODULE_HYPERLINKS */
-
-#ifdef GLK_MODULE_DATETIME
-
 void Glk::glk_current_time(glktimeval_t *time) {
 	// TODO
 }
@@ -955,8 +919,6 @@ glsi32 Glk::glk_date_to_simple_time_local(glkdate_t *date, glui32 factor) {
 	return 0;
 }
 
-#endif /* GLK_MODULE_DATETIME */
-
 /* XXX non-official Glk functions that may or may not exist */
 
 char *garglk_fileref_get_name(frefid_t fref) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 3b49421..34531c0 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -157,7 +157,9 @@ public:
 		glui32 count);
 #endif /* GLK_MODULE_LINE_TERMINATORS */
 
-#ifdef GLK_MODULE_UNICODE
+	/** \addtogroup Unicode 
+	 *  @{
+	 */
 
 	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
 		glui32 numchars);
@@ -184,7 +186,7 @@ public:
 	void glk_request_line_event_uni(winid_t win, glui32 *buf,
 		glui32 maxlen, glui32 initlen);
 
-#endif /* GLK_MODULE_UNICODE */
+	/** @}*/
 
 #ifdef GLK_MODULE_UNICODE_NORM
 
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index e2584b4..d3cf7a1 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -10,7 +10,8 @@ MODULE_OBJS := \
 	picture.o \
 	screen.o \
 	streams.o \
-	string.o \
+	unicode.o \
+	unicode_gen.o \
 	windows.o \
 	window_mask.o \
 	window_graphics.o \
diff --git a/engines/gargoyle/string.cpp b/engines/gargoyle/string.cpp
deleted file mode 100644
index 4b68978..0000000
--- a/engines/gargoyle/string.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/conf.h"
-#include "common/textconsole.h"
-
-namespace Gargoyle {
-
-size_t strlen_uni(const uint32 *s) {
-	size_t len = 0;
-	while (*s++)
-		++len;
-	return len;
-}
-
-int strToInt(const char *s) {
-	if (!*s)
-		// No string at all
-		return 0;
-	else if (toupper(s[strlen(s) - 1]) != 'H')
-		// Standard decimal string
-		return atoi(s);
-
-	// Hexadecimal string
-	uint tmp = 0;
-	int read = sscanf(s, "%xh", &tmp);
-	if (read < 1)
-		error("strToInt failed on string \"%s\"", s);
-	return (int)tmp;
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/string.h b/engines/gargoyle/string.h
deleted file mode 100644
index 2ab2925..0000000
--- a/engines/gargoyle/string.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_STRING_H
-#define GARGOYLE_STRING_H
-
-#include "gargoyle/string.h"
-#include "gargoyle/glk_types.h"
-
-namespace Gargoyle {
-
-/*
- * Get the length of a unicode string
- */
-size_t strlen_uni(const uint32 *s);
-
-/**
- * Converts a decimal or hexadecimal string into a number
- */
-int strToInt(const char *s);
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/unicode.cpp b/engines/gargoyle/unicode.cpp
new file mode 100644
index 0000000..601d8f0
--- /dev/null
+++ b/engines/gargoyle/unicode.cpp
@@ -0,0 +1,168 @@
+/* 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 "gargoyle/unicode.h"
+#include "gargoyle/unicode_gen.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+
+size_t strlen_uni(const uint32 *s) {
+	size_t len = 0;
+	while (*s++)
+		++len;
+	return len;
+}
+
+int strToInt(const char *s) {
+	if (!*s)
+		// No string at all
+		return 0;
+	else if (toupper(s[strlen(s) - 1]) != 'H')
+		// Standard decimal string
+		return atoi(s);
+
+	// Hexadecimal string
+	uint tmp = 0;
+	int read = sscanf(s, "%xh", &tmp);
+	if (read < 1)
+		error("strToInt failed on string \"%s\"", s);
+	return (int)tmp;
+}
+
+glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCase destcase,
+		BufferChangeCond cond, int changerest) {
+	glui32 ix, jx;
+	glui32 *outbuf;
+	glui32 *newoutbuf;
+	glui32 outcount;
+	int dest_block_rest = 0, dest_block_first = 0;
+	int dest_spec_rest = 0, dest_spec_first = 0;
+
+	switch (cond) {
+	case COND_ALL:
+		dest_spec_rest = destcase;
+		dest_spec_first = destcase;
+		break;
+	case COND_LINESTART:
+		if (changerest)
+			dest_spec_rest = CASE_LOWER;
+		else
+			dest_spec_rest = CASE_IDENT;
+		dest_spec_first = destcase;
+		break;
+	}
+
+	dest_block_rest = dest_spec_rest;
+	if (dest_block_rest == CASE_TITLE)
+		dest_block_rest = CASE_UPPER;
+	dest_block_first = dest_spec_first;
+	if (dest_block_first == CASE_TITLE)
+		dest_block_first = CASE_UPPER;
+
+	newoutbuf = NULL;
+	outcount = 0;
+	outbuf = buf;
+
+	for (ix = 0; ix < numchars; ix++) {
+		int target;
+		int isfirst;
+		glui32 res;
+		glui32 *special;
+		glui32 *ptr;
+		glui32 speccount;
+		glui32 ch = buf[ix];
+
+		isfirst = (ix == 0);
+
+		target = (isfirst ? dest_block_first : dest_block_rest);
+
+		if (target == CASE_IDENT) {
+			res = ch;
+		} else {
+			gli_case_block_t *block;
+
+			GET_CASE_BLOCK(ch, &block);
+			if (!block)
+				res = ch;
+			else
+				res = block[ch & 0xFF][target];
+		}
+
+		if (res != 0xFFFFFFFF || res == ch)
+		{
+			/* simple case */
+			if (outcount < len)
+				outbuf[outcount] = res;
+			outcount++;
+			continue;
+		}
+
+		target = (isfirst ? dest_spec_first : dest_spec_rest);
+
+		/* complicated cases */
+		GET_CASE_SPECIAL(ch, &special);
+		if (!special) {
+			warning("inconsistency in cgunigen.c");
+			continue;
+		}
+		ptr = &unigen_special_array[special[target]];
+		speccount = *(ptr++);
+
+		if (speccount == 1) {
+			/* simple after all */
+			if (outcount < len)
+				outbuf[outcount] = ptr[0];
+			outcount++;
+			continue;
+		}
+
+		// Now we have to allocate a new buffer, if we haven't already.
+		if (!newoutbuf) {
+			newoutbuf = new glui32[len + 1];
+			if (!newoutbuf)
+				return 0;
+			if (outcount)
+				memcpy(newoutbuf, buf, outcount * sizeof(glui32));
+			outbuf = newoutbuf;
+		}
+
+		for (jx = 0; jx<speccount; jx++) {
+			if (outcount < len)
+				outbuf[outcount] = ptr[jx];
+			outcount++;
+		}
+	}
+
+	if (newoutbuf) {
+		glui32 finallen = outcount;
+		if (finallen > len)
+			finallen = len;
+		if (finallen)
+			memcpy(buf, newoutbuf, finallen * sizeof(glui32));
+		free(newoutbuf);
+	}
+
+	return outcount;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/unicode.h b/engines/gargoyle/unicode.h
new file mode 100644
index 0000000..806a63f
--- /dev/null
+++ b/engines/gargoyle/unicode.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.
+ *
+ */
+
+#ifndef GARGOYLE_UNICODE_H
+#define GARGOYLE_UNICODE_H
+
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+typedef glui32 gli_case_block_t[2]; /* upper, lower */
+enum BufferChangeCase { CASE_UPPER = 0, CASE_LOWER = 1, CASE_TITLE = 2, CASE_IDENT = 3 };
+enum BufferChangeCond { COND_ALL = 0, COND_LINESTART = 1 };
+
+/*
+ * Get the length of a unicode string
+ */
+size_t strlen_uni(const uint32 *s);
+
+/**
+ * Apply a case change to the buffer. The len is the length of the buffer
+ * array; numchars is the number of characters originally in it. (This
+ * may be less than len.) The result will be clipped to fit len, but
+ * the return value will be the full number of characters that the
+ *converted string should have contained.
+ */
+extern glui32 bufferChangeCase(glui32 *buf, glui32 len,
+	glui32 numchars, BufferChangeCase destcase, BufferChangeCond cond, int changerest);
+
+/**
+ * Converts a decimal or hexadecimal string into a number
+ */
+int strToInt(const char *s);
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/unicode_gen.cpp b/engines/gargoyle/unicode_gen.cpp
new file mode 100644
index 0000000..a6b0bc5
--- /dev/null
+++ b/engines/gargoyle/unicode_gen.cpp
@@ -0,0 +1,11825 @@
+/* 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.
+ *
+ */
+
+/**
+ * This file is based on one that was generated by casemap.py.
+ * Derived from Unicode data files, Unicode version 4.0.1.
+ */
+
+#include "gargoyle/unicode_gen.h"
+
+namespace Gargoyle {
+
+gli_case_block_t unigen_case_block_0x0[256] = {
+	{ 0x0, 0x0 },
+	{ 0x1, 0x1 },
+	{ 0x2, 0x2 },
+	{ 0x3, 0x3 },
+	{ 0x4, 0x4 },
+	{ 0x5, 0x5 },
+	{ 0x6, 0x6 },
+	{ 0x7, 0x7 },
+	{ 0x8, 0x8 },
+	{ 0x9, 0x9 },
+	{ 0xa, 0xa },
+	{ 0xb, 0xb },
+	{ 0xc, 0xc },
+	{ 0xd, 0xd },
+	{ 0xe, 0xe },
+	{ 0xf, 0xf },
+	{ 0x10, 0x10 },
+	{ 0x11, 0x11 },
+	{ 0x12, 0x12 },
+	{ 0x13, 0x13 },
+	{ 0x14, 0x14 },
+	{ 0x15, 0x15 },
+	{ 0x16, 0x16 },
+	{ 0x17, 0x17 },
+	{ 0x18, 0x18 },
+	{ 0x19, 0x19 },
+	{ 0x1a, 0x1a },
+	{ 0x1b, 0x1b },
+	{ 0x1c, 0x1c },
+	{ 0x1d, 0x1d },
+	{ 0x1e, 0x1e },
+	{ 0x1f, 0x1f },
+	{ 0x20, 0x20 },
+	{ 0x21, 0x21 },
+	{ 0x22, 0x22 },
+	{ 0x23, 0x23 },
+	{ 0x24, 0x24 },
+	{ 0x25, 0x25 },
+	{ 0x26, 0x26 },
+	{ 0x27, 0x27 },
+	{ 0x28, 0x28 },
+	{ 0x29, 0x29 },
+	{ 0x2a, 0x2a },
+	{ 0x2b, 0x2b },
+	{ 0x2c, 0x2c },
+	{ 0x2d, 0x2d },
+	{ 0x2e, 0x2e },
+	{ 0x2f, 0x2f },
+	{ 0x30, 0x30 },
+	{ 0x31, 0x31 },
+	{ 0x32, 0x32 },
+	{ 0x33, 0x33 },
+	{ 0x34, 0x34 },
+	{ 0x35, 0x35 },
+	{ 0x36, 0x36 },
+	{ 0x37, 0x37 },
+	{ 0x38, 0x38 },
+	{ 0x39, 0x39 },
+	{ 0x3a, 0x3a },
+	{ 0x3b, 0x3b },
+	{ 0x3c, 0x3c },
+	{ 0x3d, 0x3d },
+	{ 0x3e, 0x3e },
+	{ 0x3f, 0x3f },
+	{ 0x40, 0x40 },
+	{ 0x41, 0x61 },  /* upper */
+	{ 0x42, 0x62 },  /* upper */
+	{ 0x43, 0x63 },  /* upper */
+	{ 0x44, 0x64 },  /* upper */
+	{ 0x45, 0x65 },  /* upper */
+	{ 0x46, 0x66 },  /* upper */
+	{ 0x47, 0x67 },  /* upper */
+	{ 0x48, 0x68 },  /* upper */
+	{ 0x49, 0x69 },  /* upper */
+	{ 0x4a, 0x6a },  /* upper */
+	{ 0x4b, 0x6b },  /* upper */
+	{ 0x4c, 0x6c },  /* upper */
+	{ 0x4d, 0x6d },  /* upper */
+	{ 0x4e, 0x6e },  /* upper */
+	{ 0x4f, 0x6f },  /* upper */
+	{ 0x50, 0x70 },  /* upper */
+	{ 0x51, 0x71 },  /* upper */
+	{ 0x52, 0x72 },  /* upper */
+	{ 0x53, 0x73 },  /* upper */
+	{ 0x54, 0x74 },  /* upper */
+	{ 0x55, 0x75 },  /* upper */
+	{ 0x56, 0x76 },  /* upper */
+	{ 0x57, 0x77 },  /* upper */
+	{ 0x58, 0x78 },  /* upper */
+	{ 0x59, 0x79 },  /* upper */
+	{ 0x5a, 0x7a },  /* upper */
+	{ 0x5b, 0x5b },
+	{ 0x5c, 0x5c },
+	{ 0x5d, 0x5d },
+	{ 0x5e, 0x5e },
+	{ 0x5f, 0x5f },
+	{ 0x60, 0x60 },
+	{ 0x41, 0x61 },  /* lower */
+	{ 0x42, 0x62 },  /* lower */
+	{ 0x43, 0x63 },  /* lower */
+	{ 0x44, 0x64 },  /* lower */
+	{ 0x45, 0x65 },  /* lower */
+	{ 0x46, 0x66 },  /* lower */
+	{ 0x47, 0x67 },  /* lower */
+	{ 0x48, 0x68 },  /* lower */
+	{ 0x49, 0x69 },  /* lower */
+	{ 0x4a, 0x6a },  /* lower */
+	{ 0x4b, 0x6b },  /* lower */
+	{ 0x4c, 0x6c },  /* lower */
+	{ 0x4d, 0x6d },  /* lower */
+	{ 0x4e, 0x6e },  /* lower */
+	{ 0x4f, 0x6f },  /* lower */
+	{ 0x50, 0x70 },  /* lower */
+	{ 0x51, 0x71 },  /* lower */
+	{ 0x52, 0x72 },  /* lower */
+	{ 0x53, 0x73 },  /* lower */
+	{ 0x54, 0x74 },  /* lower */
+	{ 0x55, 0x75 },  /* lower */
+	{ 0x56, 0x76 },  /* lower */
+	{ 0x57, 0x77 },  /* lower */
+	{ 0x58, 0x78 },  /* lower */
+	{ 0x59, 0x79 },  /* lower */
+	{ 0x5a, 0x7a },  /* lower */
+	{ 0x7b, 0x7b },
+	{ 0x7c, 0x7c },
+	{ 0x7d, 0x7d },
+	{ 0x7e, 0x7e },
+	{ 0x7f, 0x7f },
+	{ 0x80, 0x80 },
+	{ 0x81, 0x81 },
+	{ 0x82, 0x82 },
+	{ 0x83, 0x83 },
+	{ 0x84, 0x84 },
+	{ 0x85, 0x85 },
+	{ 0x86, 0x86 },
+	{ 0x87, 0x87 },
+	{ 0x88, 0x88 },
+	{ 0x89, 0x89 },
+	{ 0x8a, 0x8a },
+	{ 0x8b, 0x8b },
+	{ 0x8c, 0x8c },
+	{ 0x8d, 0x8d },
+	{ 0x8e, 0x8e },
+	{ 0x8f, 0x8f },
+	{ 0x90, 0x90 },
+	{ 0x91, 0x91 },
+	{ 0x92, 0x92 },
+	{ 0x93, 0x93 },
+	{ 0x94, 0x94 },
+	{ 0x95, 0x95 },
+	{ 0x96, 0x96 },
+	{ 0x97, 0x97 },
+	{ 0x98, 0x98 },
+	{ 0x99, 0x99 },
+	{ 0x9a, 0x9a },
+	{ 0x9b, 0x9b },
+	{ 0x9c, 0x9c },
+	{ 0x9d, 0x9d },
+	{ 0x9e, 0x9e },
+	{ 0x9f, 0x9f },
+	{ 0xa0, 0xa0 },
+	{ 0xa1, 0xa1 },
+	{ 0xa2, 0xa2 },
+	{ 0xa3, 0xa3 },
+	{ 0xa4, 0xa4 },
+	{ 0xa5, 0xa5 },
+	{ 0xa6, 0xa6 },
+	{ 0xa7, 0xa7 },
+	{ 0xa8, 0xa8 },
+	{ 0xa9, 0xa9 },
+	{ 0xaa, 0xaa },
+	{ 0xab, 0xab },
+	{ 0xac, 0xac },
+	{ 0xad, 0xad },
+	{ 0xae, 0xae },
+	{ 0xaf, 0xaf },
+	{ 0xb0, 0xb0 },
+	{ 0xb1, 0xb1 },
+	{ 0xb2, 0xb2 },
+	{ 0xb3, 0xb3 },
+	{ 0xb4, 0xb4 },
+	{ 0x39c, 0xb5 },  /* lower */
+	{ 0xb6, 0xb6 },
+	{ 0xb7, 0xb7 },
+	{ 0xb8, 0xb8 },
+	{ 0xb9, 0xb9 },
+	{ 0xba, 0xba },
+	{ 0xbb, 0xbb },
+	{ 0xbc, 0xbc },
+	{ 0xbd, 0xbd },
+	{ 0xbe, 0xbe },
+	{ 0xbf, 0xbf },
+	{ 0xc0, 0xe0 },  /* upper */
+	{ 0xc1, 0xe1 },  /* upper */
+	{ 0xc2, 0xe2 },  /* upper */
+	{ 0xc3, 0xe3 },  /* upper */
+	{ 0xc4, 0xe4 },  /* upper */
+	{ 0xc5, 0xe5 },  /* upper */
+	{ 0xc6, 0xe6 },  /* upper */
+	{ 0xc7, 0xe7 },  /* upper */
+	{ 0xc8, 0xe8 },  /* upper */
+	{ 0xc9, 0xe9 },  /* upper */
+	{ 0xca, 0xea },  /* upper */
+	{ 0xcb, 0xeb },  /* upper */
+	{ 0xcc, 0xec },  /* upper */
+	{ 0xcd, 0xed },  /* upper */
+	{ 0xce, 0xee },  /* upper */
+	{ 0xcf, 0xef },  /* upper */
+	{ 0xd0, 0xf0 },  /* upper */
+	{ 0xd1, 0xf1 },  /* upper */
+	{ 0xd2, 0xf2 },  /* upper */
+	{ 0xd3, 0xf3 },  /* upper */
+	{ 0xd4, 0xf4 },  /* upper */
+	{ 0xd5, 0xf5 },  /* upper */
+	{ 0xd6, 0xf6 },  /* upper */
+	{ 0xd7, 0xd7 },
+	{ 0xd8, 0xf8 },  /* upper */
+	{ 0xd9, 0xf9 },  /* upper */
+	{ 0xda, 0xfa },  /* upper */
+	{ 0xdb, 0xfb },  /* upper */
+	{ 0xdc, 0xfc },  /* upper */
+	{ 0xdd, 0xfd },  /* upper */
+	{ 0xde, 0xfe },  /* upper */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xc0, 0xe0 },  /* lower */
+	{ 0xc1, 0xe1 },  /* lower */
+	{ 0xc2, 0xe2 },  /* lower */
+	{ 0xc3, 0xe3 },  /* lower */
+	{ 0xc4, 0xe4 },  /* lower */
+	{ 0xc5, 0xe5 },  /* lower */
+	{ 0xc6, 0xe6 },  /* lower */
+	{ 0xc7, 0xe7 },  /* lower */
+	{ 0xc8, 0xe8 },  /* lower */
+	{ 0xc9, 0xe9 },  /* lower */
+	{ 0xca, 0xea },  /* lower */
+	{ 0xcb, 0xeb },  /* lower */
+	{ 0xcc, 0xec },  /* lower */
+	{ 0xcd, 0xed },  /* lower */
+	{ 0xce, 0xee },  /* lower */
+	{ 0xcf, 0xef },  /* lower */
+	{ 0xd0, 0xf0 },  /* lower */
+	{ 0xd1, 0xf1 },  /* lower */
+	{ 0xd2, 0xf2 },  /* lower */
+	{ 0xd3, 0xf3 },  /* lower */
+	{ 0xd4, 0xf4 },  /* lower */
+	{ 0xd5, 0xf5 },  /* lower */
+	{ 0xd6, 0xf6 },  /* lower */
+	{ 0xf7, 0xf7 },
+	{ 0xd8, 0xf8 },  /* lower */
+	{ 0xd9, 0xf9 },  /* lower */
+	{ 0xda, 0xfa },  /* lower */
+	{ 0xdb, 0xfb },  /* lower */
+	{ 0xdc, 0xfc },  /* lower */
+	{ 0xdd, 0xfd },  /* lower */
+	{ 0xde, 0xfe },  /* lower */
+	{ 0x178, 0xff },  /* lower */
+};
+
+gli_case_block_t unigen_case_block_0x1[256] = {
+	{ 0x100, 0x101 },  /* upper */
+	{ 0x100, 0x101 },  /* lower */
+	{ 0x102, 0x103 },  /* upper */
+	{ 0x102, 0x103 },  /* lower */
+	{ 0x104, 0x105 },  /* upper */
+	{ 0x104, 0x105 },  /* lower */
+	{ 0x106, 0x107 },  /* upper */
+	{ 0x106, 0x107 },  /* lower */
+	{ 0x108, 0x109 },  /* upper */
+	{ 0x108, 0x109 },  /* lower */
+	{ 0x10a, 0x10b },  /* upper */
+	{ 0x10a, 0x10b },  /* lower */
+	{ 0x10c, 0x10d },  /* upper */
+	{ 0x10c, 0x10d },  /* lower */
+	{ 0x10e, 0x10f },  /* upper */
+	{ 0x10e, 0x10f },  /* lower */
+	{ 0x110, 0x111 },  /* upper */
+	{ 0x110, 0x111 },  /* lower */
+	{ 0x112, 0x113 },  /* upper */
+	{ 0x112, 0x113 },  /* lower */
+	{ 0x114, 0x115 },  /* upper */
+	{ 0x114, 0x115 },  /* lower */
+	{ 0x116, 0x117 },  /* upper */
+	{ 0x116, 0x117 },  /* lower */
+	{ 0x118, 0x119 },  /* upper */
+	{ 0x118, 0x119 },  /* lower */
+	{ 0x11a, 0x11b },  /* upper */
+	{ 0x11a, 0x11b },  /* lower */
+	{ 0x11c, 0x11d },  /* upper */
+	{ 0x11c, 0x11d },  /* lower */
+	{ 0x11e, 0x11f },  /* upper */
+	{ 0x11e, 0x11f },  /* lower */
+	{ 0x120, 0x121 },  /* upper */
+	{ 0x120, 0x121 },  /* lower */
+	{ 0x122, 0x123 },  /* upper */
+	{ 0x122, 0x123 },  /* lower */
+	{ 0x124, 0x125 },  /* upper */
+	{ 0x124, 0x125 },  /* lower */
+	{ 0x126, 0x127 },  /* upper */
+	{ 0x126, 0x127 },  /* lower */
+	{ 0x128, 0x129 },  /* upper */
+	{ 0x128, 0x129 },  /* lower */
+	{ 0x12a, 0x12b },  /* upper */
+	{ 0x12a, 0x12b },  /* lower */
+	{ 0x12c, 0x12d },  /* upper */
+	{ 0x12c, 0x12d },  /* lower */
+	{ 0x12e, 0x12f },  /* upper */
+	{ 0x12e, 0x12f },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x49, 0x131 },  /* lower */
+	{ 0x132, 0x133 },  /* upper */
+	{ 0x132, 0x133 },  /* lower */
+	{ 0x134, 0x135 },  /* upper */
+	{ 0x134, 0x135 },  /* lower */
+	{ 0x136, 0x137 },  /* upper */
+	{ 0x136, 0x137 },  /* lower */
+	{ 0x138, 0x138 },
+	{ 0x139, 0x13a },  /* upper */
+	{ 0x139, 0x13a },  /* lower */
+	{ 0x13b, 0x13c },  /* upper */
+	{ 0x13b, 0x13c },  /* lower */
+	{ 0x13d, 0x13e },  /* upper */
+	{ 0x13d, 0x13e },  /* lower */
+	{ 0x13f, 0x140 },  /* upper */
+	{ 0x13f, 0x140 },  /* lower */
+	{ 0x141, 0x142 },  /* upper */
+	{ 0x141, 0x142 },  /* lower */
+	{ 0x143, 0x144 },  /* upper */
+	{ 0x143, 0x144 },  /* lower */
+	{ 0x145, 0x146 },  /* upper */
+	{ 0x145, 0x146 },  /* lower */
+	{ 0x147, 0x148 },  /* upper */
+	{ 0x147, 0x148 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x14a, 0x14b },  /* upper */
+	{ 0x14a, 0x14b },  /* lower */
+	{ 0x14c, 0x14d },  /* upper */
+	{ 0x14c, 0x14d },  /* lower */
+	{ 0x14e, 0x14f },  /* upper */
+	{ 0x14e, 0x14f },  /* lower */
+	{ 0x150, 0x151 },  /* upper */
+	{ 0x150, 0x151 },  /* lower */
+	{ 0x152, 0x153 },  /* upper */
+	{ 0x152, 0x153 },  /* lower */
+	{ 0x154, 0x155 },  /* upper */
+	{ 0x154, 0x155 },  /* lower */
+	{ 0x156, 0x157 },  /* upper */
+	{ 0x156, 0x157 },  /* lower */
+	{ 0x158, 0x159 },  /* upper */
+	{ 0x158, 0x159 },  /* lower */
+	{ 0x15a, 0x15b },  /* upper */
+	{ 0x15a, 0x15b },  /* lower */
+	{ 0x15c, 0x15d },  /* upper */
+	{ 0x15c, 0x15d },  /* lower */
+	{ 0x15e, 0x15f },  /* upper */
+	{ 0x15e, 0x15f },  /* lower */
+	{ 0x160, 0x161 },  /* upper */
+	{ 0x160, 0x161 },  /* lower */
+	{ 0x162, 0x163 },  /* upper */
+	{ 0x162, 0x163 },  /* lower */
+	{ 0x164, 0x165 },  /* upper */
+	{ 0x164, 0x165 },  /* lower */
+	{ 0x166, 0x167 },  /* upper */
+	{ 0x166, 0x167 },  /* lower */
+	{ 0x168, 0x169 },  /* upper */
+	{ 0x168, 0x169 },  /* lower */
+	{ 0x16a, 0x16b },  /* upper */
+	{ 0x16a, 0x16b },  /* lower */
+	{ 0x16c, 0x16d },  /* upper */
+	{ 0x16c, 0x16d },  /* lower */
+	{ 0x16e, 0x16f },  /* upper */
+	{ 0x16e, 0x16f },  /* lower */
+	{ 0x170, 0x171 },  /* upper */
+	{ 0x170, 0x171 },  /* lower */
+	{ 0x172, 0x173 },  /* upper */
+	{ 0x172, 0x173 },  /* lower */
+	{ 0x174, 0x175 },  /* upper */
+	{ 0x174, 0x175 },  /* lower */
+	{ 0x176, 0x177 },  /* upper */
+	{ 0x176, 0x177 },  /* lower */
+	{ 0x178, 0xff },  /* upper */
+	{ 0x179, 0x17a },  /* upper */
+	{ 0x179, 0x17a },  /* lower */
+	{ 0x17b, 0x17c },  /* upper */
+	{ 0x17b, 0x17c },  /* lower */
+	{ 0x17d, 0x17e },  /* upper */
+	{ 0x17d, 0x17e },  /* lower */
+	{ 0x53, 0x17f },  /* lower */
+	{ 0x180, 0x180 },
+	{ 0x181, 0x253 },  /* upper */
+	{ 0x182, 0x183 },  /* upper */
+	{ 0x182, 0x183 },  /* lower */
+	{ 0x184, 0x185 },  /* upper */
+	{ 0x184, 0x185 },  /* lower */
+	{ 0x186, 0x254 },  /* upper */
+	{ 0x187, 0x188 },  /* upper */
+	{ 0x187, 0x188 },  /* lower */
+	{ 0x189, 0x256 },  /* upper */
+	{ 0x18a, 0x257 },  /* upper */
+	{ 0x18b, 0x18c },  /* upper */
+	{ 0x18b, 0x18c },  /* lower */
+	{ 0x18d, 0x18d },
+	{ 0x18e, 0x1dd },  /* upper */
+	{ 0x18f, 0x259 },  /* upper */
+	{ 0x190, 0x25b },  /* upper */
+	{ 0x191, 0x192 },  /* upper */
+	{ 0x191, 0x192 },  /* lower */
+	{ 0x193, 0x260 },  /* upper */
+	{ 0x194, 0x263 },  /* upper */
+	{ 0x1f6, 0x195 },  /* lower */
+	{ 0x196, 0x269 },  /* upper */
+	{ 0x197, 0x268 },  /* upper */
+	{ 0x198, 0x199 },  /* upper */
+	{ 0x198, 0x199 },  /* lower */
+	{ 0x19a, 0x19a },
+	{ 0x19b, 0x19b },
+	{ 0x19c, 0x26f },  /* upper */
+	{ 0x19d, 0x272 },  /* upper */
+	{ 0x220, 0x19e },  /* lower */
+	{ 0x19f, 0x275 },  /* upper */
+	{ 0x1a0, 0x1a1 },  /* upper */
+	{ 0x1a0, 0x1a1 },  /* lower */
+	{ 0x1a2, 0x1a3 },  /* upper */
+	{ 0x1a2, 0x1a3 },  /* lower */
+	{ 0x1a4, 0x1a5 },  /* upper */
+	{ 0x1a4, 0x1a5 },  /* lower */
+	{ 0x1a6, 0x280 },  /* upper */
+	{ 0x1a7, 0x1a8 },  /* upper */
+	{ 0x1a7, 0x1a8 },  /* lower */
+	{ 0x1a9, 0x283 },  /* upper */
+	{ 0x1aa, 0x1aa },
+	{ 0x1ab, 0x1ab },
+	{ 0x1ac, 0x1ad },  /* upper */
+	{ 0x1ac, 0x1ad },  /* lower */
+	{ 0x1ae, 0x288 },  /* upper */
+	{ 0x1af, 0x1b0 },  /* upper */
+	{ 0x1af, 0x1b0 },  /* lower */
+	{ 0x1b1, 0x28a },  /* upper */
+	{ 0x1b2, 0x28b },  /* upper */
+	{ 0x1b3, 0x1b4 },  /* upper */
+	{ 0x1b3, 0x1b4 },  /* lower */
+	{ 0x1b5, 0x1b6 },  /* upper */
+	{ 0x1b5, 0x1b6 },  /* lower */
+	{ 0x1b7, 0x292 },  /* upper */
+	{ 0x1b8, 0x1b9 },  /* upper */
+	{ 0x1b8, 0x1b9 },  /* lower */
+	{ 0x1ba, 0x1ba },
+	{ 0x1bb, 0x1bb },
+	{ 0x1bc, 0x1bd },  /* upper */
+	{ 0x1bc, 0x1bd },  /* lower */
+	{ 0x1be, 0x1be },
+	{ 0x1f7, 0x1bf },  /* lower */
+	{ 0x1c0, 0x1c0 },
+	{ 0x1c1, 0x1c1 },
+	{ 0x1c2, 0x1c2 },
+	{ 0x1c3, 0x1c3 },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1cd, 0x1ce },  /* upper */
+	{ 0x1cd, 0x1ce },  /* lower */
+	{ 0x1cf, 0x1d0 },  /* upper */
+	{ 0x1cf, 0x1d0 },  /* lower */
+	{ 0x1d1, 0x1d2 },  /* upper */
+	{ 0x1d1, 0x1d2 },  /* lower */
+	{ 0x1d3, 0x1d4 },  /* upper */
+	{ 0x1d3, 0x1d4 },  /* lower */
+	{ 0x1d5, 0x1d6 },  /* upper */
+	{ 0x1d5, 0x1d6 },  /* lower */
+	{ 0x1d7, 0x1d8 },  /* upper */
+	{ 0x1d7, 0x1d8 },  /* lower */
+	{ 0x1d9, 0x1da },  /* upper */
+	{ 0x1d9, 0x1da },  /* lower */
+	{ 0x1db, 0x1dc },  /* upper */
+	{ 0x1db, 0x1dc },  /* lower */
+	{ 0x18e, 0x1dd },  /* lower */
+	{ 0x1de, 0x1df },  /* upper */
+	{ 0x1de, 0x1df },  /* lower */
+	{ 0x1e0, 0x1e1 },  /* upper */
+	{ 0x1e0, 0x1e1 },  /* lower */
+	{ 0x1e2, 0x1e3 },  /* upper */
+	{ 0x1e2, 0x1e3 },  /* lower */
+	{ 0x1e4, 0x1e5 },  /* upper */
+	{ 0x1e4, 0x1e5 },  /* lower */
+	{ 0x1e6, 0x1e7 },  /* upper */
+	{ 0x1e6, 0x1e7 },  /* lower */
+	{ 0x1e8, 0x1e9 },  /* upper */
+	{ 0x1e8, 0x1e9 },  /* lower */
+	{ 0x1ea, 0x1eb },  /* upper */
+	{ 0x1ea, 0x1eb },  /* lower */
+	{ 0x1ec, 0x1ed },  /* upper */
+	{ 0x1ec, 0x1ed },  /* lower */
+	{ 0x1ee, 0x1ef },  /* upper */
+	{ 0x1ee, 0x1ef },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1f4, 0x1f5 },  /* upper */
+	{ 0x1f4, 0x1f5 },  /* lower */
+	{ 0x1f6, 0x195 },  /* upper */
+	{ 0x1f7, 0x1bf },  /* upper */
+	{ 0x1f8, 0x1f9 },  /* upper */
+	{ 0x1f8, 0x1f9 },  /* lower */
+	{ 0x1fa, 0x1fb },  /* upper */
+	{ 0x1fa, 0x1fb },  /* lower */
+	{ 0x1fc, 0x1fd },  /* upper */
+	{ 0x1fc, 0x1fd },  /* lower */
+	{ 0x1fe, 0x1ff },  /* upper */
+	{ 0x1fe, 0x1ff },  /* lower */
+};
+
+gli_case_block_t unigen_case_block_0x2[256] = {
+	{ 0x200, 0x201 },  /* upper */
+	{ 0x200, 0x201 },  /* lower */
+	{ 0x202, 0x203 },  /* upper */
+	{ 0x202, 0x203 },  /* lower */
+	{ 0x204, 0x205 },  /* upper */
+	{ 0x204, 0x205 },  /* lower */
+	{ 0x206, 0x207 },  /* upper */
+	{ 0x206, 0x207 },  /* lower */
+	{ 0x208, 0x209 },  /* upper */
+	{ 0x208, 0x209 },  /* lower */
+	{ 0x20a, 0x20b },  /* upper */
+	{ 0x20a, 0x20b },  /* lower */
+	{ 0x20c, 0x20d },  /* upper */
+	{ 0x20c, 0x20d },  /* lower */
+	{ 0x20e, 0x20f },  /* upper */
+	{ 0x20e, 0x20f },  /* lower */
+	{ 0x210, 0x211 },  /* upper */
+	{ 0x210, 0x211 },  /* lower */
+	{ 0x212, 0x213 },  /* upper */
+	{ 0x212, 0x213 },  /* lower */
+	{ 0x214, 0x215 },  /* upper */
+	{ 0x214, 0x215 },  /* lower */
+	{ 0x216, 0x217 },  /* upper */
+	{ 0x216, 0x217 },  /* lower */
+	{ 0x218, 0x219 },  /* upper */
+	{ 0x218, 0x219 },  /* lower */
+	{ 0x21a, 0x21b },  /* upper */
+	{ 0x21a, 0x21b },  /* lower */
+	{ 0x21c, 0x21d },  /* upper */
+	{ 0x21c, 0x21d },  /* lower */
+	{ 0x21e, 0x21f },  /* upper */
+	{ 0x21e, 0x21f },  /* lower */
+	{ 0x220, 0x19e },  /* upper */
+	{ 0x221, 0x221 },
+	{ 0x222, 0x223 },  /* upper */
+	{ 0x222, 0x223 },  /* lower */
+	{ 0x224, 0x225 },  /* upper */
+	{ 0x224, 0x225 },  /* lower */
+	{ 0x226, 0x227 },  /* upper */
+	{ 0x226, 0x227 },  /* lower */
+	{ 0x228, 0x229 },  /* upper */
+	{ 0x228, 0x229 },  /* lower */
+	{ 0x22a, 0x22b },  /* upper */
+	{ 0x22a, 0x22b },  /* lower */
+	{ 0x22c, 0x22d },  /* upper */
+	{ 0x22c, 0x22d },  /* lower */
+	{ 0x22e, 0x22f },  /* upper */
+	{ 0x22e, 0x22f },  /* lower */
+	{ 0x230, 0x231 },  /* upper */
+	{ 0x230, 0x231 },  /* lower */
+	{ 0x232, 0x233 },  /* upper */
+	{ 0x232, 0x233 },  /* lower */
+	{ 0x234, 0x234 },
+	{ 0x235, 0x235 },
+	{ 0x236, 0x236 },
+	{ 0x237, 0x237 },
+	{ 0x238, 0x238 },
+	{ 0x239, 0x239 },
+	{ 0x23a, 0x23a },
+	{ 0x23b, 0x23b },
+	{ 0x23c, 0x23c },
+	{ 0x23d, 0x23d },
+	{ 0x23e, 0x23e },
+	{ 0x23f, 0x23f },
+	{ 0x240, 0x240 },
+	{ 0x241, 0x241 },
+	{ 0x242, 0x242 },
+	{ 0x243, 0x243 },
+	{ 0x244, 0x244 },
+	{ 0x245, 0x245 },
+	{ 0x246, 0x246 },
+	{ 0x247, 0x247 },
+	{ 0x248, 0x248 },
+	{ 0x249, 0x249 },
+	{ 0x24a, 0x24a },
+	{ 0x24b, 0x24b },
+	{ 0x24c, 0x24c },
+	{ 0x24d, 0x24d },
+	{ 0x24e, 0x24e },
+	{ 0x24f, 0x24f },
+	{ 0x250, 0x250 },
+	{ 0x251, 0x251 },
+	{ 0x252, 0x252 },
+	{ 0x181, 0x253 },  /* lower */
+	{ 0x186, 0x254 },  /* lower */
+	{ 0x255, 0x255 },
+	{ 0x189, 0x256 },  /* lower */
+	{ 0x18a, 0x257 },  /* lower */
+	{ 0x258, 0x258 },
+	{ 0x18f, 0x259 },  /* lower */
+	{ 0x25a, 0x25a },
+	{ 0x190, 0x25b },  /* lower */
+	{ 0x25c, 0x25c },
+	{ 0x25d, 0x25d },
+	{ 0x25e, 0x25e },
+	{ 0x25f, 0x25f },
+	{ 0x193, 0x260 },  /* lower */
+	{ 0x261, 0x261 },
+	{ 0x262, 0x262 },
+	{ 0x194, 0x263 },  /* lower */
+	{ 0x264, 0x264 },
+	{ 0x265, 0x265 },
+	{ 0x266, 0x266 },
+	{ 0x267, 0x267 },
+	{ 0x197, 0x268 },  /* lower */
+	{ 0x196, 0x269 },  /* lower */
+	{ 0x26a, 0x26a },
+	{ 0x26b, 0x26b },
+	{ 0x26c, 0x26c },
+	{ 0x26d, 0x26d },
+	{ 0x26e, 0x26e },
+	{ 0x19c, 0x26f },  /* lower */
+	{ 0x270, 0x270 },
+	{ 0x271, 0x271 },
+	{ 0x19d, 0x272 },  /* lower */
+	{ 0x273, 0x273 },
+	{ 0x274, 0x274 },
+	{ 0x19f, 0x275 },  /* lower */
+	{ 0x276, 0x276 },
+	{ 0x277, 0x277 },
+	{ 0x278, 0x278 },
+	{ 0x279, 0x279 },
+	{ 0x27a, 0x27a },
+	{ 0x27b, 0x27b },
+	{ 0x27c, 0x27c },
+	{ 0x27d, 0x27d },
+	{ 0x27e, 0x27e },
+	{ 0x27f, 0x27f },
+	{ 0x1a6, 0x280 },  /* lower */
+	{ 0x281, 0x281 },
+	{ 0x282, 0x282 },
+	{ 0x1a9, 0x283 },  /* lower */
+	{ 0x284, 0x284 },
+	{ 0x285, 0x285 },
+	{ 0x286, 0x286 },
+	{ 0x287, 0x287 },
+	{ 0x1ae, 0x288 },  /* lower */
+	{ 0x289, 0x289 },
+	{ 0x1b1, 0x28a },  /* lower */
+	{ 0x1b2, 0x28b },  /* lower */
+	{ 0x28c, 0x28c },
+	{ 0x28d, 0x28d },
+	{ 0x28e, 0x28e },
+	{ 0x28f, 0x28f },
+	{ 0x290, 0x290 },
+	{ 0x291, 0x291 },
+	{ 0x1b7, 0x292 },  /* lower */
+	{ 0x293, 0x293 },
+	{ 0x294, 0x294 },
+	{ 0x295, 0x295 },
+	{ 0x296, 0x296 },
+	{ 0x297, 0x297 },
+	{ 0x298, 0x298 },
+	{ 0x299, 0x299 },
+	{ 0x29a, 0x29a },
+	{ 0x29b, 0x29b },
+	{ 0x29c, 0x29c },
+	{ 0x29d, 0x29d },
+	{ 0x29e, 0x29e },
+	{ 0x29f, 0x29f },
+	{ 0x2a0, 0x2a0 },
+	{ 0x2a1, 0x2a1 },
+	{ 0x2a2, 0x2a2 },
+	{ 0x2a3, 0x2a3 },
+	{ 0x2a4, 0x2a4 },
+	{ 0x2a5, 0x2a5 },
+	{ 0x2a6, 0x2a6 },
+	{ 0x2a7, 0x2a7 },
+	{ 0x2a8, 0x2a8 },
+	{ 0x2a9, 0x2a9 },
+	{ 0x2aa, 0x2aa },
+	{ 0x2ab, 0x2ab },
+	{ 0x2ac, 0x2ac },
+	{ 0x2ad, 0x2ad },
+	{ 0x2ae, 0x2ae },
+	{ 0x2af, 0x2af },
+	{ 0x2b0, 0x2b0 },
+	{ 0x2b1, 0x2b1 },
+	{ 0x2b2, 0x2b2 },
+	{ 0x2b3, 0x2b3 },
+	{ 0x2b4, 0x2b4 },
+	{ 0x2b5, 0x2b5 },
+	{ 0x2b6, 0x2b6 },
+	{ 0x2b7, 0x2b7 },
+	{ 0x2b8, 0x2b8 },
+	{ 0x2b9, 0x2b9 },
+	{ 0x2ba, 0x2ba },
+	{ 0x2bb, 0x2bb },
+	{ 0x2bc, 0x2bc },
+	{ 0x2bd, 0x2bd },
+	{ 0x2be, 0x2be },
+	{ 0x2bf, 0x2bf },
+	{ 0x2c0, 0x2c0 },
+	{ 0x2c1, 0x2c1 },
+	{ 0x2c2, 0x2c2 },
+	{ 0x2c3, 0x2c3 },
+	{ 0x2c4, 0x2c4 },
+	{ 0x2c5, 0x2c5 },
+	{ 0x2c6, 0x2c6 },
+	{ 0x2c7, 0x2c7 },
+	{ 0x2c8, 0x2c8 },
+	{ 0x2c9, 0x2c9 },
+	{ 0x2ca, 0x2ca },
+	{ 0x2cb, 0x2cb },
+	{ 0x2cc, 0x2cc },
+	{ 0x2cd, 0x2cd },
+	{ 0x2ce, 0x2ce },
+	{ 0x2cf, 0x2cf },
+	{ 0x2d0, 0x2d0 },
+	{ 0x2d1, 0x2d1 },
+	{ 0x2d2, 0x2d2 },
+	{ 0x2d3, 0x2d3 },
+	{ 0x2d4, 0x2d4 },
+	{ 0x2d5, 0x2d5 },
+	{ 0x2d6, 0x2d6 },
+	{ 0x2d7, 0x2d7 },
+	{ 0x2d8, 0x2d8 },
+	{ 0x2d9, 0x2d9 },
+	{ 0x2da, 0x2da },
+	{ 0x2db, 0x2db },
+	{ 0x2dc, 0x2dc },
+	{ 0x2dd, 0x2dd },
+	{ 0x2de, 0x2de },
+	{ 0x2df, 0x2df },
+	{ 0x2e0, 0x2e0 },
+	{ 0x2e1, 0x2e1 },
+	{ 0x2e2, 0x2e2 },
+	{ 0x2e3, 0x2e3 },
+	{ 0x2e4, 0x2e4 },
+	{ 0x2e5, 0x2e5 },
+	{ 0x2e6, 0x2e6 },
+	{ 0x2e7, 0x2e7 },
+	{ 0x2e8, 0x2e8 },
+	{ 0x2e9, 0x2e9 },
+	{ 0x2ea, 0x2ea },
+	{ 0x2eb, 0x2eb },
+	{ 0x2ec, 0x2ec },
+	{ 0x2ed, 0x2ed },
+	{ 0x2ee, 0x2ee },
+	{ 0x2ef, 0x2ef },
+	{ 0x2f0, 0x2f0 },
+	{ 0x2f1, 0x2f1 },
+	{ 0x2f2, 0x2f2 },
+	{ 0x2f3, 0x2f3 },
+	{ 0x2f4, 0x2f4 },
+	{ 0x2f5, 0x2f5 },
+	{ 0x2f6, 0x2f6 },
+	{ 0x2f7, 0x2f7 },
+	{ 0x2f8, 0x2f8 },
+	{ 0x2f9, 0x2f9 },
+	{ 0x2fa, 0x2fa },
+	{ 0x2fb, 0x2fb },
+	{ 0x2fc, 0x2fc },
+	{ 0x2fd, 0x2fd },
+	{ 0x2fe, 0x2fe },
+	{ 0x2ff, 0x2ff },
+};
+
+gli_case_block_t unigen_case_block_0x3[256] = {
+	{ 0x300, 0x300 },
+	{ 0x301, 0x301 },
+	{ 0x302, 0x302 },
+	{ 0x303, 0x303 },
+	{ 0x304, 0x304 },
+	{ 0x305, 0x305 },
+	{ 0x306, 0x306 },
+	{ 0x307, 0x307 },
+	{ 0x308, 0x308 },
+	{ 0x309, 0x309 },
+	{ 0x30a, 0x30a },
+	{ 0x30b, 0x30b },
+	{ 0x30c, 0x30c },
+	{ 0x30d, 0x30d },
+	{ 0x30e, 0x30e },
+	{ 0x30f, 0x30f },
+	{ 0x310, 0x310 },
+	{ 0x311, 0x311 },
+	{ 0x312, 0x312 },
+	{ 0x313, 0x313 },
+	{ 0x314, 0x314 },
+	{ 0x315, 0x315 },
+	{ 0x316, 0x316 },
+	{ 0x317, 0x317 },
+	{ 0x318, 0x318 },
+	{ 0x319, 0x319 },
+	{ 0x31a, 0x31a },
+	{ 0x31b, 0x31b },
+	{ 0x31c, 0x31c },
+	{ 0x31d, 0x31d },
+	{ 0x31e, 0x31e },
+	{ 0x31f, 0x31f },
+	{ 0x320, 0x320 },
+	{ 0x321, 0x321 },
+	{ 0x322, 0x322 },
+	{ 0x323, 0x323 },
+	{ 0x324, 0x324 },
+	{ 0x325, 0x325 },
+	{ 0x326, 0x326 },
+	{ 0x327, 0x327 },
+	{ 0x328, 0x328 },
+	{ 0x329, 0x329 },
+	{ 0x32a, 0x32a },
+	{ 0x32b, 0x32b },
+	{ 0x32c, 0x32c },
+	{ 0x32d, 0x32d },
+	{ 0x32e, 0x32e },
+	{ 0x32f, 0x32f },
+	{ 0x330, 0x330 },
+	{ 0x331, 0x331 },
+	{ 0x332, 0x332 },
+	{ 0x333, 0x333 },
+	{ 0x334, 0x334 },
+	{ 0x335, 0x335 },
+	{ 0x336, 0x336 },
+	{ 0x337, 0x337 },
+	{ 0x338, 0x338 },
+	{ 0x339, 0x339 },
+	{ 0x33a, 0x33a },
+	{ 0x33b, 0x33b },
+	{ 0x33c, 0x33c },
+	{ 0x33d, 0x33d },
+	{ 0x33e, 0x33e },
+	{ 0x33f, 0x33f },
+	{ 0x340, 0x340 },
+	{ 0x341, 0x341 },
+	{ 0x342, 0x342 },
+	{ 0x343, 0x343 },
+	{ 0x344, 0x344 },
+	{ 0x399, 0x345 },  /* lower */
+	{ 0x346, 0x346 },
+	{ 0x347, 0x347 },
+	{ 0x348, 0x348 },
+	{ 0x349, 0x349 },
+	{ 0x34a, 0x34a },
+	{ 0x34b, 0x34b },
+	{ 0x34c, 0x34c },
+	{ 0x34d, 0x34d },
+	{ 0x34e, 0x34e },
+	{ 0x34f, 0x34f },
+	{ 0x350, 0x350 },
+	{ 0x351, 0x351 },
+	{ 0x352, 0x352 },
+	{ 0x353, 0x353 },
+	{ 0x354, 0x354 },
+	{ 0x355, 0x355 },
+	{ 0x356, 0x356 },
+	{ 0x357, 0x357 },
+	{ 0x358, 0x358 },
+	{ 0x359, 0x359 },
+	{ 0x35a, 0x35a },
+	{ 0x35b, 0x35b },
+	{ 0x35c, 0x35c },
+	{ 0x35d, 0x35d },
+	{ 0x35e, 0x35e },
+	{ 0x35f, 0x35f },
+	{ 0x360, 0x360 },
+	{ 0x361, 0x361 },
+	{ 0x362, 0x362 },
+	{ 0x363, 0x363 },
+	{ 0x364, 0x364 },
+	{ 0x365, 0x365 },
+	{ 0x366, 0x366 },
+	{ 0x367, 0x367 },
+	{ 0x368, 0x368 },
+	{ 0x369, 0x369 },
+	{ 0x36a, 0x36a },
+	{ 0x36b, 0x36b },
+	{ 0x36c, 0x36c },
+	{ 0x36d, 0x36d },
+	{ 0x36e, 0x36e },
+	{ 0x36f, 0x36f },
+	{ 0x370, 0x370 },
+	{ 0x371, 0x371 },
+	{ 0x372, 0x372 },
+	{ 0x373, 0x373 },
+	{ 0x374, 0x374 },
+	{ 0x375, 0x375 },
+	{ 0x376, 0x376 },
+	{ 0x377, 0x377 },
+	{ 0x378, 0x378 },
+	{ 0x379, 0x379 },
+	{ 0x37a, 0x37a },
+	{ 0x37b, 0x37b },
+	{ 0x37c, 0x37c },
+	{ 0x37d, 0x37d },
+	{ 0x37e, 0x37e },
+	{ 0x37f, 0x37f },
+	{ 0x380, 0x380 },
+	{ 0x381, 0x381 },
+	{ 0x382, 0x382 },
+	{ 0x383, 0x383 },
+	{ 0x384, 0x384 },
+	{ 0x385, 0x385 },
+	{ 0x386, 0x3ac },  /* upper */
+	{ 0x387, 0x387 },
+	{ 0x388, 0x3ad },  /* upper */
+	{ 0x389, 0x3ae },  /* upper */
+	{ 0x38a, 0x3af },  /* upper */
+	{ 0x38b, 0x38b },
+	{ 0x38c, 0x3cc },  /* upper */
+	{ 0x38d, 0x38d },
+	{ 0x38e, 0x3cd },  /* upper */
+	{ 0x38f, 0x3ce },  /* upper */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x391, 0x3b1 },  /* upper */
+	{ 0x392, 0x3b2 },  /* upper */
+	{ 0x393, 0x3b3 },  /* upper */
+	{ 0x394, 0x3b4 },  /* upper */
+	{ 0x395, 0x3b5 },  /* upper */
+	{ 0x396, 0x3b6 },  /* upper */
+	{ 0x397, 0x3b7 },  /* upper */
+	{ 0x398, 0x3b8 },  /* upper */
+	{ 0x399, 0x3b9 },  /* upper */
+	{ 0x39a, 0x3ba },  /* upper */
+	{ 0x39b, 0x3bb },  /* upper */
+	{ 0x39c, 0x3bc },  /* upper */
+	{ 0x39d, 0x3bd },  /* upper */
+	{ 0x39e, 0x3be },  /* upper */
+	{ 0x39f, 0x3bf },  /* upper */
+	{ 0x3a0, 0x3c0 },  /* upper */
+	{ 0x3a1, 0x3c1 },  /* upper */
+	{ 0x3a2, 0x3a2 },
+	{ 0x3a3, 0x3c3 },  /* upper */
+	{ 0x3a4, 0x3c4 },  /* upper */
+	{ 0x3a5, 0x3c5 },  /* upper */
+	{ 0x3a6, 0x3c6 },  /* upper */
+	{ 0x3a7, 0x3c7 },  /* upper */
+	{ 0x3a8, 0x3c8 },  /* upper */
+	{ 0x3a9, 0x3c9 },  /* upper */
+	{ 0x3aa, 0x3ca },  /* upper */
+	{ 0x3ab, 0x3cb },  /* upper */
+	{ 0x386, 0x3ac },  /* lower */
+	{ 0x388, 0x3ad },  /* lower */
+	{ 0x389, 0x3ae },  /* lower */
+	{ 0x38a, 0x3af },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x391, 0x3b1 },  /* lower */
+	{ 0x392, 0x3b2 },  /* lower */
+	{ 0x393, 0x3b3 },  /* lower */
+	{ 0x394, 0x3b4 },  /* lower */
+	{ 0x395, 0x3b5 },  /* lower */
+	{ 0x396, 0x3b6 },  /* lower */
+	{ 0x397, 0x3b7 },  /* lower */
+	{ 0x398, 0x3b8 },  /* lower */
+	{ 0x399, 0x3b9 },  /* lower */
+	{ 0x39a, 0x3ba },  /* lower */
+	{ 0x39b, 0x3bb },  /* lower */
+	{ 0x39c, 0x3bc },  /* lower */
+	{ 0x39d, 0x3bd },  /* lower */
+	{ 0x39e, 0x3be },  /* lower */
+	{ 0x39f, 0x3bf },  /* lower */
+	{ 0x3a0, 0x3c0 },  /* lower */
+	{ 0x3a1, 0x3c1 },  /* lower */
+	{ 0x3a3, 0x3c2 },  /* lower */
+	{ 0x3a3, 0x3c3 },  /* lower */
+	{ 0x3a4, 0x3c4 },  /* lower */
+	{ 0x3a5, 0x3c5 },  /* lower */
+	{ 0x3a6, 0x3c6 },  /* lower */
+	{ 0x3a7, 0x3c7 },  /* lower */
+	{ 0x3a8, 0x3c8 },  /* lower */
+	{ 0x3a9, 0x3c9 },  /* lower */
+	{ 0x3aa, 0x3ca },  /* lower */
+	{ 0x3ab, 0x3cb },  /* lower */
+	{ 0x38c, 0x3cc },  /* lower */
+	{ 0x38e, 0x3cd },  /* lower */
+	{ 0x38f, 0x3ce },  /* lower */
+	{ 0x3cf, 0x3cf },
+	{ 0x392, 0x3d0 },  /* lower */
+	{ 0x398, 0x3d1 },  /* lower */
+	{ 0x3d2, 0x3d2 },
+	{ 0x3d3, 0x3d3 },
+	{ 0x3d4, 0x3d4 },
+	{ 0x3a6, 0x3d5 },  /* lower */
+	{ 0x3a0, 0x3d6 },  /* lower */
+	{ 0x3d7, 0x3d7 },
+	{ 0x3d8, 0x3d9 },  /* upper */
+	{ 0x3d8, 0x3d9 },  /* lower */
+	{ 0x3da, 0x3db },  /* upper */
+	{ 0x3da, 0x3db },  /* lower */
+	{ 0x3dc, 0x3dd },  /* upper */
+	{ 0x3dc, 0x3dd },  /* lower */
+	{ 0x3de, 0x3df },  /* upper */
+	{ 0x3de, 0x3df },  /* lower */
+	{ 0x3e0, 0x3e1 },  /* upper */
+	{ 0x3e0, 0x3e1 },  /* lower */
+	{ 0x3e2, 0x3e3 },  /* upper */
+	{ 0x3e2, 0x3e3 },  /* lower */
+	{ 0x3e4, 0x3e5 },  /* upper */
+	{ 0x3e4, 0x3e5 },  /* lower */
+	{ 0x3e6, 0x3e7 },  /* upper */
+	{ 0x3e6, 0x3e7 },  /* lower */
+	{ 0x3e8, 0x3e9 },  /* upper */
+	{ 0x3e8, 0x3e9 },  /* lower */
+	{ 0x3ea, 0x3eb },  /* upper */
+	{ 0x3ea, 0x3eb },  /* lower */
+	{ 0x3ec, 0x3ed },  /* upper */
+	{ 0x3ec, 0x3ed },  /* lower */
+	{ 0x3ee, 0x3ef },  /* upper */
+	{ 0x3ee, 0x3ef },  /* lower */
+	{ 0x39a, 0x3f0 },  /* lower */
+	{ 0x3a1, 0x3f1 },  /* lower */
+	{ 0x3f9, 0x3f2 },  /* lower */
+	{ 0x3f3, 0x3f3 },
+	{ 0x3f4, 0x3b8 },  /* upper */
+	{ 0x395, 0x3f5 },  /* lower */
+	{ 0x3f6, 0x3f6 },
+	{ 0x3f7, 0x3f8 },  /* upper */
+	{ 0x3f7, 0x3f8 },  /* lower */
+	{ 0x3f9, 0x3f2 },  /* upper */
+	{ 0x3fa, 0x3fb },  /* upper */
+	{ 0x3fa, 0x3fb },  /* lower */
+	{ 0x3fc, 0x3fc },
+	{ 0x3fd, 0x3fd },
+	{ 0x3fe, 0x3fe },
+	{ 0x3ff, 0x3ff },
+};
+
+gli_case_block_t unigen_case_block_0x4[256] = {
+	{ 0x400, 0x450 },  /* upper */
+	{ 0x401, 0x451 },  /* upper */
+	{ 0x402, 0x452 },  /* upper */
+	{ 0x403, 0x453 },  /* upper */
+	{ 0x404, 0x454 },  /* upper */
+	{ 0x405, 0x455 },  /* upper */
+	{ 0x406, 0x456 },  /* upper */
+	{ 0x407, 0x457 },  /* upper */
+	{ 0x408, 0x458 },  /* upper */
+	{ 0x409, 0x459 },  /* upper */
+	{ 0x40a, 0x45a },  /* upper */
+	{ 0x40b, 0x45b },  /* upper */
+	{ 0x40c, 0x45c },  /* upper */
+	{ 0x40d, 0x45d },  /* upper */
+	{ 0x40e, 0x45e },  /* upper */
+	{ 0x40f, 0x45f },  /* upper */
+	{ 0x410, 0x430 },  /* upper */
+	{ 0x411, 0x431 },  /* upper */
+	{ 0x412, 0x432 },  /* upper */
+	{ 0x413, 0x433 },  /* upper */
+	{ 0x414, 0x434 },  /* upper */
+	{ 0x415, 0x435 },  /* upper */
+	{ 0x416, 0x436 },  /* upper */
+	{ 0x417, 0x437 },  /* upper */
+	{ 0x418, 0x438 },  /* upper */
+	{ 0x419, 0x439 },  /* upper */
+	{ 0x41a, 0x43a },  /* upper */
+	{ 0x41b, 0x43b },  /* upper */
+	{ 0x41c, 0x43c },  /* upper */
+	{ 0x41d, 0x43d },  /* upper */
+	{ 0x41e, 0x43e },  /* upper */
+	{ 0x41f, 0x43f },  /* upper */
+	{ 0x420, 0x440 },  /* upper */
+	{ 0x421, 0x441 },  /* upper */
+	{ 0x422, 0x442 },  /* upper */
+	{ 0x423, 0x443 },  /* upper */
+	{ 0x424, 0x444 },  /* upper */
+	{ 0x425, 0x445 },  /* upper */
+	{ 0x426, 0x446 },  /* upper */
+	{ 0x427, 0x447 },  /* upper */
+	{ 0x428, 0x448 },  /* upper */
+	{ 0x429, 0x449 },  /* upper */
+	{ 0x42a, 0x44a },  /* upper */
+	{ 0x42b, 0x44b },  /* upper */
+	{ 0x42c, 0x44c },  /* upper */
+	{ 0x42d, 0x44d },  /* upper */
+	{ 0x42e, 0x44e },  /* upper */
+	{ 0x42f, 0x44f },  /* upper */
+	{ 0x410, 0x430 },  /* lower */
+	{ 0x411, 0x431 },  /* lower */
+	{ 0x412, 0x432 },  /* lower */
+	{ 0x413, 0x433 },  /* lower */
+	{ 0x414, 0x434 },  /* lower */
+	{ 0x415, 0x435 },  /* lower */
+	{ 0x416, 0x436 },  /* lower */
+	{ 0x417, 0x437 },  /* lower */
+	{ 0x418, 0x438 },  /* lower */
+	{ 0x419, 0x439 },  /* lower */
+	{ 0x41a, 0x43a },  /* lower */
+	{ 0x41b, 0x43b },  /* lower */
+	{ 0x41c, 0x43c },  /* lower */
+	{ 0x41d, 0x43d },  /* lower */
+	{ 0x41e, 0x43e },  /* lower */
+	{ 0x41f, 0x43f },  /* lower */
+	{ 0x420, 0x440 },  /* lower */
+	{ 0x421, 0x441 },  /* lower */
+	{ 0x422, 0x442 },  /* lower */
+	{ 0x423, 0x443 },  /* lower */
+	{ 0x424, 0x444 },  /* lower */
+	{ 0x425, 0x445 },  /* lower */
+	{ 0x426, 0x446 },  /* lower */
+	{ 0x427, 0x447 },  /* lower */
+	{ 0x428, 0x448 },  /* lower */
+	{ 0x429, 0x449 },  /* lower */
+	{ 0x42a, 0x44a },  /* lower */
+	{ 0x42b, 0x44b },  /* lower */
+	{ 0x42c, 0x44c },  /* lower */
+	{ 0x42d, 0x44d },  /* lower */
+	{ 0x42e, 0x44e },  /* lower */
+	{ 0x42f, 0x44f },  /* lower */
+	{ 0x400, 0x450 },  /* lower */
+	{ 0x401, 0x451 },  /* lower */
+	{ 0x402, 0x452 },  /* lower */
+	{ 0x403, 0x453 },  /* lower */
+	{ 0x404, 0x454 },  /* lower */
+	{ 0x405, 0x455 },  /* lower */
+	{ 0x406, 0x456 },  /* lower */
+	{ 0x407, 0x457 },  /* lower */
+	{ 0x408, 0x458 },  /* lower */
+	{ 0x409, 0x459 },  /* lower */
+	{ 0x40a, 0x45a },  /* lower */
+	{ 0x40b, 0x45b },  /* lower */
+	{ 0x40c, 0x45c },  /* lower */
+	{ 0x40d, 0x45d },  /* lower */
+	{ 0x40e, 0x45e },  /* lower */
+	{ 0x40f, 0x45f },  /* lower */
+	{ 0x460, 0x461 },  /* upper */
+	{ 0x460, 0x461 },  /* lower */
+	{ 0x462, 0x463 },  /* upper */
+	{ 0x462, 0x463 },  /* lower */
+	{ 0x464, 0x465 },  /* upper */
+	{ 0x464, 0x465 },  /* lower */
+	{ 0x466, 0x467 },  /* upper */
+	{ 0x466, 0x467 },  /* lower */
+	{ 0x468, 0x469 },  /* upper */
+	{ 0x468, 0x469 },  /* lower */
+	{ 0x46a, 0x46b },  /* upper */
+	{ 0x46a, 0x46b },  /* lower */
+	{ 0x46c, 0x46d },  /* upper */
+	{ 0x46c, 0x46d },  /* lower */
+	{ 0x46e, 0x46f },  /* upper */
+	{ 0x46e, 0x46f },  /* lower */
+	{ 0x470, 0x471 },  /* upper */
+	{ 0x470, 0x471 },  /* lower */
+	{ 0x472, 0x473 },  /* upper */
+	{ 0x472, 0x473 },  /* lower */
+	{ 0x474, 0x475 },  /* upper */
+	{ 0x474, 0x475 },  /* lower */
+	{ 0x476, 0x477 },  /* upper */
+	{ 0x476, 0x477 },  /* lower */
+	{ 0x478, 0x479 },  /* upper */
+	{ 0x478, 0x479 },  /* lower */
+	{ 0x47a, 0x47b },  /* upper */
+	{ 0x47a, 0x47b },  /* lower */
+	{ 0x47c, 0x47d },  /* upper */
+	{ 0x47c, 0x47d },  /* lower */
+	{ 0x47e, 0x47f },  /* upper */
+	{ 0x47e, 0x47f },  /* lower */
+	{ 0x480, 0x481 },  /* upper */
+	{ 0x480, 0x481 },  /* lower */
+	{ 0x482, 0x482 },
+	{ 0x483, 0x483 },
+	{ 0x484, 0x484 },
+	{ 0x485, 0x485 },
+	{ 0x486, 0x486 },
+	{ 0x487, 0x487 },
+	{ 0x488, 0x488 },
+	{ 0x489, 0x489 },
+	{ 0x48a, 0x48b },  /* upper */
+	{ 0x48a, 0x48b },  /* lower */
+	{ 0x48c, 0x48d },  /* upper */
+	{ 0x48c, 0x48d },  /* lower */
+	{ 0x48e, 0x48f },  /* upper */
+	{ 0x48e, 0x48f },  /* lower */
+	{ 0x490, 0x491 },  /* upper */
+	{ 0x490, 0x491 },  /* lower */
+	{ 0x492, 0x493 },  /* upper */
+	{ 0x492, 0x493 },  /* lower */
+	{ 0x494, 0x495 },  /* upper */
+	{ 0x494, 0x495 },  /* lower */
+	{ 0x496, 0x497 },  /* upper */
+	{ 0x496, 0x497 },  /* lower */
+	{ 0x498, 0x499 },  /* upper */
+	{ 0x498, 0x499 },  /* lower */
+	{ 0x49a, 0x49b },  /* upper */
+	{ 0x49a, 0x49b },  /* lower */
+	{ 0x49c, 0x49d },  /* upper */
+	{ 0x49c, 0x49d },  /* lower */
+	{ 0x49e, 0x49f },  /* upper */
+	{ 0x49e, 0x49f },  /* lower */
+	{ 0x4a0, 0x4a1 },  /* upper */
+	{ 0x4a0, 0x4a1 },  /* lower */
+	{ 0x4a2, 0x4a3 },  /* upper */
+	{ 0x4a2, 0x4a3 },  /* lower */
+	{ 0x4a4, 0x4a5 },  /* upper */
+	{ 0x4a4, 0x4a5 },  /* lower */
+	{ 0x4a6, 0x4a7 },  /* upper */
+	{ 0x4a6, 0x4a7 },  /* lower */
+	{ 0x4a8, 0x4a9 },  /* upper */
+	{ 0x4a8, 0x4a9 },  /* lower */
+	{ 0x4aa, 0x4ab },  /* upper */
+	{ 0x4aa, 0x4ab },  /* lower */
+	{ 0x4ac, 0x4ad },  /* upper */
+	{ 0x4ac, 0x4ad },  /* lower */
+	{ 0x4ae, 0x4af },  /* upper */
+	{ 0x4ae, 0x4af },  /* lower */
+	{ 0x4b0, 0x4b1 },  /* upper */
+	{ 0x4b0, 0x4b1 },  /* lower */
+	{ 0x4b2, 0x4b3 },  /* upper */
+	{ 0x4b2, 0x4b3 },  /* lower */
+	{ 0x4b4, 0x4b5 },  /* upper */
+	{ 0x4b4, 0x4b5 },  /* lower */
+	{ 0x4b6, 0x4b7 },  /* upper */
+	{ 0x4b6, 0x4b7 },  /* lower */
+	{ 0x4b8, 0x4b9 },  /* upper */
+	{ 0x4b8, 0x4b9 },  /* lower */
+	{ 0x4ba, 0x4bb },  /* upper */
+	{ 0x4ba, 0x4bb },  /* lower */
+	{ 0x4bc, 0x4bd },  /* upper */
+	{ 0x4bc, 0x4bd },  /* lower */
+	{ 0x4be, 0x4bf },  /* upper */
+	{ 0x4be, 0x4bf },  /* lower */
+	{ 0x4c0, 0x4c0 },
+	{ 0x4c1, 0x4c2 },  /* upper */
+	{ 0x4c1, 0x4c2 },  /* lower */
+	{ 0x4c3, 0x4c4 },  /* upper */
+	{ 0x4c3, 0x4c4 },  /* lower */
+	{ 0x4c5, 0x4c6 },  /* upper */
+	{ 0x4c5, 0x4c6 },  /* lower */
+	{ 0x4c7, 0x4c8 },  /* upper */
+	{ 0x4c7, 0x4c8 },  /* lower */
+	{ 0x4c9, 0x4ca },  /* upper */
+	{ 0x4c9, 0x4ca },  /* lower */
+	{ 0x4cb, 0x4cc },  /* upper */
+	{ 0x4cb, 0x4cc },  /* lower */
+	{ 0x4cd, 0x4ce },  /* upper */
+	{ 0x4cd, 0x4ce },  /* lower */
+	{ 0x4cf, 0x4cf },
+	{ 0x4d0, 0x4d1 },  /* upper */
+	{ 0x4d0, 0x4d1 },  /* lower */
+	{ 0x4d2, 0x4d3 },  /* upper */
+	{ 0x4d2, 0x4d3 },  /* lower */
+	{ 0x4d4, 0x4d5 },  /* upper */
+	{ 0x4d4, 0x4d5 },  /* lower */
+	{ 0x4d6, 0x4d7 },  /* upper */
+	{ 0x4d6, 0x4d7 },  /* lower */
+	{ 0x4d8, 0x4d9 },  /* upper */
+	{ 0x4d8, 0x4d9 },  /* lower */
+	{ 0x4da, 0x4db },  /* upper */
+	{ 0x4da, 0x4db },  /* lower */
+	{ 0x4dc, 0x4dd },  /* upper */
+	{ 0x4dc, 0x4dd },  /* lower */
+	{ 0x4de, 0x4df },  /* upper */
+	{ 0x4de, 0x4df },  /* lower */
+	{ 0x4e0, 0x4e1 },  /* upper */
+	{ 0x4e0, 0x4e1 },  /* lower */
+	{ 0x4e2, 0x4e3 },  /* upper */
+	{ 0x4e2, 0x4e3 },  /* lower */
+	{ 0x4e4, 0x4e5 },  /* upper */
+	{ 0x4e4, 0x4e5 },  /* lower */
+	{ 0x4e6, 0x4e7 },  /* upper */
+	{ 0x4e6, 0x4e7 },  /* lower */
+	{ 0x4e8, 0x4e9 },  /* upper */
+	{ 0x4e8, 0x4e9 },  /* lower */
+	{ 0x4ea, 0x4eb },  /* upper */
+	{ 0x4ea, 0x4eb },  /* lower */
+	{ 0x4ec, 0x4ed },  /* upper */
+	{ 0x4ec, 0x4ed },  /* lower */
+	{ 0x4ee, 0x4ef },  /* upper */
+	{ 0x4ee, 0x4ef },  /* lower */
+	{ 0x4f0, 0x4f1 },  /* upper */
+	{ 0x4f0, 0x4f1 },  /* lower */
+	{ 0x4f2, 0x4f3 },  /* upper */
+	{ 0x4f2, 0x4f3 },  /* lower */
+	{ 0x4f4, 0x4f5 },  /* upper */
+	{ 0x4f4, 0x4f5 },  /* lower */
+	{ 0x4f6, 0x4f6 },
+	{ 0x4f7, 0x4f7 },
+	{ 0x4f8, 0x4f9 },  /* upper */
+	{ 0x4f8, 0x4f9 },  /* lower */
+	{ 0x4fa, 0x4fa },
+	{ 0x4fb, 0x4fb },
+	{ 0x4fc, 0x4fc },
+	{ 0x4fd, 0x4fd },
+	{ 0x4fe, 0x4fe },
+	{ 0x4ff, 0x4ff },
+};
+
+gli_case_block_t unigen_case_block_0x5[256] = {
+	{ 0x500, 0x501 },  /* upper */
+	{ 0x500, 0x501 },  /* lower */
+	{ 0x502, 0x503 },  /* upper */
+	{ 0x502, 0x503 },  /* lower */
+	{ 0x504, 0x505 },  /* upper */
+	{ 0x504, 0x505 },  /* lower */
+	{ 0x506, 0x507 },  /* upper */
+	{ 0x506, 0x507 },  /* lower */
+	{ 0x508, 0x509 },  /* upper */
+	{ 0x508, 0x509 },  /* lower */
+	{ 0x50a, 0x50b },  /* upper */
+	{ 0x50a, 0x50b },  /* lower */
+	{ 0x50c, 0x50d },  /* upper */
+	{ 0x50c, 0x50d },  /* lower */
+	{ 0x50e, 0x50f },  /* upper */
+	{ 0x50e, 0x50f },  /* lower */
+	{ 0x510, 0x510 },
+	{ 0x511, 0x511 },
+	{ 0x512, 0x512 },
+	{ 0x513, 0x513 },
+	{ 0x514, 0x514 },
+	{ 0x515, 0x515 },
+	{ 0x516, 0x516 },
+	{ 0x517, 0x517 },
+	{ 0x518, 0x518 },
+	{ 0x519, 0x519 },
+	{ 0x51a, 0x51a },
+	{ 0x51b, 0x51b },
+	{ 0x51c, 0x51c },
+	{ 0x51d, 0x51d },
+	{ 0x51e, 0x51e },
+	{ 0x51f, 0x51f },
+	{ 0x520, 0x520 },
+	{ 0x521, 0x521 },
+	{ 0x522, 0x522 },
+	{ 0x523, 0x523 },
+	{ 0x524, 0x524 },
+	{ 0x525, 0x525 },
+	{ 0x526, 0x526 },
+	{ 0x527, 0x527 },
+	{ 0x528, 0x528 },
+	{ 0x529, 0x529 },
+	{ 0x52a, 0x52a },
+	{ 0x52b, 0x52b },
+	{ 0x52c, 0x52c },
+	{ 0x52d, 0x52d },
+	{ 0x52e, 0x52e },
+	{ 0x52f, 0x52f },
+	{ 0x530, 0x530 },
+	{ 0x531, 0x561 },  /* upper */
+	{ 0x532, 0x562 },  /* upper */
+	{ 0x533, 0x563 },  /* upper */
+	{ 0x534, 0x564 },  /* upper */
+	{ 0x535, 0x565 },  /* upper */
+	{ 0x536, 0x566 },  /* upper */
+	{ 0x537, 0x567 },  /* upper */
+	{ 0x538, 0x568 },  /* upper */
+	{ 0x539, 0x569 },  /* upper */
+	{ 0x53a, 0x56a },  /* upper */
+	{ 0x53b, 0x56b },  /* upper */
+	{ 0x53c, 0x56c },  /* upper */
+	{ 0x53d, 0x56d },  /* upper */
+	{ 0x53e, 0x56e },  /* upper */
+	{ 0x53f, 0x56f },  /* upper */
+	{ 0x540, 0x570 },  /* upper */
+	{ 0x541, 0x571 },  /* upper */
+	{ 0x542, 0x572 },  /* upper */
+	{ 0x543, 0x573 },  /* upper */
+	{ 0x544, 0x574 },  /* upper */
+	{ 0x545, 0x575 },  /* upper */
+	{ 0x546, 0x576 },  /* upper */
+	{ 0x547, 0x577 },  /* upper */
+	{ 0x548, 0x578 },  /* upper */
+	{ 0x549, 0x579 },  /* upper */
+	{ 0x54a, 0x57a },  /* upper */
+	{ 0x54b, 0x57b },  /* upper */
+	{ 0x54c, 0x57c },  /* upper */
+	{ 0x54d, 0x57d },  /* upper */
+	{ 0x54e, 0x57e },  /* upper */
+	{ 0x54f, 0x57f },  /* upper */
+	{ 0x550, 0x580 },  /* upper */
+	{ 0x551, 0x581 },  /* upper */
+	{ 0x552, 0x582 },  /* upper */
+	{ 0x553, 0x583 },  /* upper */
+	{ 0x554, 0x584 },  /* upper */
+	{ 0x555, 0x585 },  /* upper */
+	{ 0x556, 0x586 },  /* upper */
+	{ 0x557, 0x557 },
+	{ 0x558, 0x558 },
+	{ 0x559, 0x559 },
+	{ 0x55a, 0x55a },
+	{ 0x55b, 0x55b },
+	{ 0x55c, 0x55c },
+	{ 0x55d, 0x55d },
+	{ 0x55e, 0x55e },
+	{ 0x55f, 0x55f },
+	{ 0x560, 0x560 },
+	{ 0x531, 0x561 },  /* lower */
+	{ 0x532, 0x562 },  /* lower */
+	{ 0x533, 0x563 },  /* lower */
+	{ 0x534, 0x564 },  /* lower */
+	{ 0x535, 0x565 },  /* lower */
+	{ 0x536, 0x566 },  /* lower */
+	{ 0x537, 0x567 },  /* lower */
+	{ 0x538, 0x568 },  /* lower */
+	{ 0x539, 0x569 },  /* lower */
+	{ 0x53a, 0x56a },  /* lower */
+	{ 0x53b, 0x56b },  /* lower */
+	{ 0x53c, 0x56c },  /* lower */
+	{ 0x53d, 0x56d },  /* lower */
+	{ 0x53e, 0x56e },  /* lower */
+	{ 0x53f, 0x56f },  /* lower */
+	{ 0x540, 0x570 },  /* lower */
+	{ 0x541, 0x571 },  /* lower */
+	{ 0x542, 0x572 },  /* lower */
+	{ 0x543, 0x573 },  /* lower */
+	{ 0x544, 0x574 },  /* lower */
+	{ 0x545, 0x575 },  /* lower */
+	{ 0x546, 0x576 },  /* lower */
+	{ 0x547, 0x577 },  /* lower */
+	{ 0x548, 0x578 },  /* lower */
+	{ 0x549, 0x579 },  /* lower */
+	{ 0x54a, 0x57a },  /* lower */
+	{ 0x54b, 0x57b },  /* lower */
+	{ 0x54c, 0x57c },  /* lower */
+	{ 0x54d, 0x57d },  /* lower */
+	{ 0x54e, 0x57e },  /* lower */
+	{ 0x54f, 0x57f },  /* lower */
+	{ 0x550, 0x580 },  /* lower */
+	{ 0x551, 0x581 },  /* lower */
+	{ 0x552, 0x582 },  /* lower */
+	{ 0x553, 0x583 },  /* lower */
+	{ 0x554, 0x584 },  /* lower */
+	{ 0x555, 0x585 },  /* lower */
+	{ 0x556, 0x586 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x588, 0x588 },
+	{ 0x589, 0x589 },
+	{ 0x58a, 0x58a },
+	{ 0x58b, 0x58b },
+	{ 0x58c, 0x58c },
+	{ 0x58d, 0x58d },
+	{ 0x58e, 0x58e },
+	{ 0x58f, 0x58f },
+	{ 0x590, 0x590 },
+	{ 0x591, 0x591 },
+	{ 0x592, 0x592 },
+	{ 0x593, 0x593 },
+	{ 0x594, 0x594 },
+	{ 0x595, 0x595 },
+	{ 0x596, 0x596 },
+	{ 0x597, 0x597 },
+	{ 0x598, 0x598 },
+	{ 0x599, 0x599 },
+	{ 0x59a, 0x59a },
+	{ 0x59b, 0x59b },
+	{ 0x59c, 0x59c },
+	{ 0x59d, 0x59d },
+	{ 0x59e, 0x59e },
+	{ 0x59f, 0x59f },
+	{ 0x5a0, 0x5a0 },
+	{ 0x5a1, 0x5a1 },
+	{ 0x5a2, 0x5a2 },
+	{ 0x5a3, 0x5a3 },
+	{ 0x5a4, 0x5a4 },
+	{ 0x5a5, 0x5a5 },
+	{ 0x5a6, 0x5a6 },
+	{ 0x5a7, 0x5a7 },
+	{ 0x5a8, 0x5a8 },
+	{ 0x5a9, 0x5a9 },
+	{ 0x5aa, 0x5aa },
+	{ 0x5ab, 0x5ab },
+	{ 0x5ac, 0x5ac },
+	{ 0x5ad, 0x5ad },
+	{ 0x5ae, 0x5ae },
+	{ 0x5af, 0x5af },
+	{ 0x5b0, 0x5b0 },
+	{ 0x5b1, 0x5b1 },
+	{ 0x5b2, 0x5b2 },
+	{ 0x5b3, 0x5b3 },
+	{ 0x5b4, 0x5b4 },
+	{ 0x5b5, 0x5b5 },
+	{ 0x5b6, 0x5b6 },
+	{ 0x5b7, 0x5b7 },
+	{ 0x5b8, 0x5b8 },
+	{ 0x5b9, 0x5b9 },
+	{ 0x5ba, 0x5ba },
+	{ 0x5bb, 0x5bb },
+	{ 0x5bc, 0x5bc },
+	{ 0x5bd, 0x5bd },
+	{ 0x5be, 0x5be },
+	{ 0x5bf, 0x5bf },
+	{ 0x5c0, 0x5c0 },
+	{ 0x5c1, 0x5c1 },
+	{ 0x5c2, 0x5c2 },
+	{ 0x5c3, 0x5c3 },
+	{ 0x5c4, 0x5c4 },
+	{ 0x5c5, 0x5c5 },
+	{ 0x5c6, 0x5c6 },
+	{ 0x5c7, 0x5c7 },
+	{ 0x5c8, 0x5c8 },
+	{ 0x5c9, 0x5c9 },
+	{ 0x5ca, 0x5ca },
+	{ 0x5cb, 0x5cb },
+	{ 0x5cc, 0x5cc },
+	{ 0x5cd, 0x5cd },
+	{ 0x5ce, 0x5ce },
+	{ 0x5cf, 0x5cf },
+	{ 0x5d0, 0x5d0 },
+	{ 0x5d1, 0x5d1 },
+	{ 0x5d2, 0x5d2 },
+	{ 0x5d3, 0x5d3 },
+	{ 0x5d4, 0x5d4 },
+	{ 0x5d5, 0x5d5 },
+	{ 0x5d6, 0x5d6 },
+	{ 0x5d7, 0x5d7 },
+	{ 0x5d8, 0x5d8 },
+	{ 0x5d9, 0x5d9 },
+	{ 0x5da, 0x5da },
+	{ 0x5db, 0x5db },
+	{ 0x5dc, 0x5dc },
+	{ 0x5dd, 0x5dd },
+	{ 0x5de, 0x5de },
+	{ 0x5df, 0x5df },
+	{ 0x5e0, 0x5e0 },
+	{ 0x5e1, 0x5e1 },
+	{ 0x5e2, 0x5e2 },
+	{ 0x5e3, 0x5e3 },
+	{ 0x5e4, 0x5e4 },
+	{ 0x5e5, 0x5e5 },
+	{ 0x5e6, 0x5e6 },
+	{ 0x5e7, 0x5e7 },
+	{ 0x5e8, 0x5e8 },
+	{ 0x5e9, 0x5e9 },
+	{ 0x5ea, 0x5ea },
+	{ 0x5eb, 0x5eb },
+	{ 0x5ec, 0x5ec },
+	{ 0x5ed, 0x5ed },
+	{ 0x5ee, 0x5ee },
+	{ 0x5ef, 0x5ef },
+	{ 0x5f0, 0x5f0 },
+	{ 0x5f1, 0x5f1 },
+	{ 0x5f2, 0x5f2 },
+	{ 0x5f3, 0x5f3 },
+	{ 0x5f4, 0x5f4 },
+	{ 0x5f5, 0x5f5 },
+	{ 0x5f6, 0x5f6 },
+	{ 0x5f7, 0x5f7 },
+	{ 0x5f8, 0x5f8 },
+	{ 0x5f9, 0x5f9 },
+	{ 0x5fa, 0x5fa },
+	{ 0x5fb, 0x5fb },
+	{ 0x5fc, 0x5fc },
+	{ 0x5fd, 0x5fd },
+	{ 0x5fe, 0x5fe },
+	{ 0x5ff, 0x5ff },
+};
+
+gli_case_block_t unigen_case_block_0x1e[256] = {
+	{ 0x1e00, 0x1e01 },  /* upper */
+	{ 0x1e00, 0x1e01 },  /* lower */
+	{ 0x1e02, 0x1e03 },  /* upper */
+	{ 0x1e02, 0x1e03 },  /* lower */
+	{ 0x1e04, 0x1e05 },  /* upper */
+	{ 0x1e04, 0x1e05 },  /* lower */
+	{ 0x1e06, 0x1e07 },  /* upper */
+	{ 0x1e06, 0x1e07 },  /* lower */
+	{ 0x1e08, 0x1e09 },  /* upper */
+	{ 0x1e08, 0x1e09 },  /* lower */
+	{ 0x1e0a, 0x1e0b },  /* upper */
+	{ 0x1e0a, 0x1e0b },  /* lower */
+	{ 0x1e0c, 0x1e0d },  /* upper */
+	{ 0x1e0c, 0x1e0d },  /* lower */
+	{ 0x1e0e, 0x1e0f },  /* upper */
+	{ 0x1e0e, 0x1e0f },  /* lower */
+	{ 0x1e10, 0x1e11 },  /* upper */
+	{ 0x1e10, 0x1e11 },  /* lower */
+	{ 0x1e12, 0x1e13 },  /* upper */
+	{ 0x1e12, 0x1e13 },  /* lower */
+	{ 0x1e14, 0x1e15 },  /* upper */
+	{ 0x1e14, 0x1e15 },  /* lower */
+	{ 0x1e16, 0x1e17 },  /* upper */
+	{ 0x1e16, 0x1e17 },  /* lower */
+	{ 0x1e18, 0x1e19 },  /* upper */
+	{ 0x1e18, 0x1e19 },  /* lower */
+	{ 0x1e1a, 0x1e1b },  /* upper */
+	{ 0x1e1a, 0x1e1b },  /* lower */
+	{ 0x1e1c, 0x1e1d },  /* upper */
+	{ 0x1e1c, 0x1e1d },  /* lower */
+	{ 0x1e1e, 0x1e1f },  /* upper */
+	{ 0x1e1e, 0x1e1f },  /* lower */
+	{ 0x1e20, 0x1e21 },  /* upper */
+	{ 0x1e20, 0x1e21 },  /* lower */
+	{ 0x1e22, 0x1e23 },  /* upper */
+	{ 0x1e22, 0x1e23 },  /* lower */
+	{ 0x1e24, 0x1e25 },  /* upper */
+	{ 0x1e24, 0x1e25 },  /* lower */
+	{ 0x1e26, 0x1e27 },  /* upper */
+	{ 0x1e26, 0x1e27 },  /* lower */
+	{ 0x1e28, 0x1e29 },  /* upper */
+	{ 0x1e28, 0x1e29 },  /* lower */
+	{ 0x1e2a, 0x1e2b },  /* upper */
+	{ 0x1e2a, 0x1e2b },  /* lower */
+	{ 0x1e2c, 0x1e2d },  /* upper */
+	{ 0x1e2c, 0x1e2d },  /* lower */
+	{ 0x1e2e, 0x1e2f },  /* upper */
+	{ 0x1e2e, 0x1e2f },  /* lower */
+	{ 0x1e30, 0x1e31 },  /* upper */
+	{ 0x1e30, 0x1e31 },  /* lower */
+	{ 0x1e32, 0x1e33 },  /* upper */
+	{ 0x1e32, 0x1e33 },  /* lower */
+	{ 0x1e34, 0x1e35 },  /* upper */
+	{ 0x1e34, 0x1e35 },  /* lower */
+	{ 0x1e36, 0x1e37 },  /* upper */
+	{ 0x1e36, 0x1e37 },  /* lower */
+	{ 0x1e38, 0x1e39 },  /* upper */
+	{ 0x1e38, 0x1e39 },  /* lower */
+	{ 0x1e3a, 0x1e3b },  /* upper */
+	{ 0x1e3a, 0x1e3b },  /* lower */
+	{ 0x1e3c, 0x1e3d },  /* upper */
+	{ 0x1e3c, 0x1e3d },  /* lower */
+	{ 0x1e3e, 0x1e3f },  /* upper */
+	{ 0x1e3e, 0x1e3f },  /* lower */
+	{ 0x1e40, 0x1e41 },  /* upper */
+	{ 0x1e40, 0x1e41 },  /* lower */
+	{ 0x1e42, 0x1e43 },  /* upper */
+	{ 0x1e42, 0x1e43 },  /* lower */
+	{ 0x1e44, 0x1e45 },  /* upper */
+	{ 0x1e44, 0x1e45 },  /* lower */
+	{ 0x1e46, 0x1e47 },  /* upper */
+	{ 0x1e46, 0x1e47 },  /* lower */
+	{ 0x1e48, 0x1e49 },  /* upper */
+	{ 0x1e48, 0x1e49 },  /* lower */
+	{ 0x1e4a, 0x1e4b },  /* upper */
+	{ 0x1e4a, 0x1e4b },  /* lower */
+	{ 0x1e4c, 0x1e4d },  /* upper */
+	{ 0x1e4c, 0x1e4d },  /* lower */
+	{ 0x1e4e, 0x1e4f },  /* upper */
+	{ 0x1e4e, 0x1e4f },  /* lower */
+	{ 0x1e50, 0x1e51 },  /* upper */
+	{ 0x1e50, 0x1e51 },  /* lower */
+	{ 0x1e52, 0x1e53 },  /* upper */
+	{ 0x1e52, 0x1e53 },  /* lower */
+	{ 0x1e54, 0x1e55 },  /* upper */
+	{ 0x1e54, 0x1e55 },  /* lower */
+	{ 0x1e56, 0x1e57 },  /* upper */
+	{ 0x1e56, 0x1e57 },  /* lower */
+	{ 0x1e58, 0x1e59 },  /* upper */
+	{ 0x1e58, 0x1e59 },  /* lower */
+	{ 0x1e5a, 0x1e5b },  /* upper */
+	{ 0x1e5a, 0x1e5b },  /* lower */
+	{ 0x1e5c, 0x1e5d },  /* upper */
+	{ 0x1e5c, 0x1e5d },  /* lower */
+	{ 0x1e5e, 0x1e5f },  /* upper */
+	{ 0x1e5e, 0x1e5f },  /* lower */
+	{ 0x1e60, 0x1e61 },  /* upper */
+	{ 0x1e60, 0x1e61 },  /* lower */
+	{ 0x1e62, 0x1e63 },  /* upper */
+	{ 0x1e62, 0x1e63 },  /* lower */
+	{ 0x1e64, 0x1e65 },  /* upper */
+	{ 0x1e64, 0x1e65 },  /* lower */
+	{ 0x1e66, 0x1e67 },  /* upper */
+	{ 0x1e66, 0x1e67 },  /* lower */
+	{ 0x1e68, 0x1e69 },  /* upper */
+	{ 0x1e68, 0x1e69 },  /* lower */
+	{ 0x1e6a, 0x1e6b },  /* upper */
+	{ 0x1e6a, 0x1e6b },  /* lower */
+	{ 0x1e6c, 0x1e6d },  /* upper */
+	{ 0x1e6c, 0x1e6d },  /* lower */
+	{ 0x1e6e, 0x1e6f },  /* upper */
+	{ 0x1e6e, 0x1e6f },  /* lower */
+	{ 0x1e70, 0x1e71 },  /* upper */
+	{ 0x1e70, 0x1e71 },  /* lower */
+	{ 0x1e72, 0x1e73 },  /* upper */
+	{ 0x1e72, 0x1e73 },  /* lower */
+	{ 0x1e74, 0x1e75 },  /* upper */
+	{ 0x1e74, 0x1e75 },  /* lower */
+	{ 0x1e76, 0x1e77 },  /* upper */
+	{ 0x1e76, 0x1e77 },  /* lower */
+	{ 0x1e78, 0x1e79 },  /* upper */
+	{ 0x1e78, 0x1e79 },  /* lower */
+	{ 0x1e7a, 0x1e7b },  /* upper */
+	{ 0x1e7a, 0x1e7b },  /* lower */
+	{ 0x1e7c, 0x1e7d },  /* upper */
+	{ 0x1e7c, 0x1e7d },  /* lower */
+	{ 0x1e7e, 0x1e7f },  /* upper */
+	{ 0x1e7e, 0x1e7f },  /* lower */
+	{ 0x1e80, 0x1e81 },  /* upper */
+	{ 0x1e80, 0x1e81 },  /* lower */
+	{ 0x1e82, 0x1e83 },  /* upper */
+	{ 0x1e82, 0x1e83 },  /* lower */
+	{ 0x1e84, 0x1e85 },  /* upper */
+	{ 0x1e84, 0x1e85 },  /* lower */
+	{ 0x1e86, 0x1e87 },  /* upper */
+	{ 0x1e86, 0x1e87 },  /* lower */
+	{ 0x1e88, 0x1e89 },  /* upper */
+	{ 0x1e88, 0x1e89 },  /* lower */
+	{ 0x1e8a, 0x1e8b },  /* upper */
+	{ 0x1e8a, 0x1e8b },  /* lower */
+	{ 0x1e8c, 0x1e8d },  /* upper */
+	{ 0x1e8c, 0x1e8d },  /* lower */
+	{ 0x1e8e, 0x1e8f },  /* upper */
+	{ 0x1e8e, 0x1e8f },  /* lower */
+	{ 0x1e90, 0x1e91 },  /* upper */
+	{ 0x1e90, 0x1e91 },  /* lower */
+	{ 0x1e92, 0x1e93 },  /* upper */
+	{ 0x1e92, 0x1e93 },  /* lower */
+	{ 0x1e94, 0x1e95 },  /* upper */
+	{ 0x1e94, 0x1e95 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1e60, 0x1e9b },  /* lower */
+	{ 0x1e9c, 0x1e9c },
+	{ 0x1e9d, 0x1e9d },
+	{ 0x1e9e, 0x1e9e },
+	{ 0x1e9f, 0x1e9f },
+	{ 0x1ea0, 0x1ea1 },  /* upper */
+	{ 0x1ea0, 0x1ea1 },  /* lower */
+	{ 0x1ea2, 0x1ea3 },  /* upper */
+	{ 0x1ea2, 0x1ea3 },  /* lower */
+	{ 0x1ea4, 0x1ea5 },  /* upper */
+	{ 0x1ea4, 0x1ea5 },  /* lower */
+	{ 0x1ea6, 0x1ea7 },  /* upper */
+	{ 0x1ea6, 0x1ea7 },  /* lower */
+	{ 0x1ea8, 0x1ea9 },  /* upper */
+	{ 0x1ea8, 0x1ea9 },  /* lower */
+	{ 0x1eaa, 0x1eab },  /* upper */
+	{ 0x1eaa, 0x1eab },  /* lower */
+	{ 0x1eac, 0x1ead },  /* upper */
+	{ 0x1eac, 0x1ead },  /* lower */
+	{ 0x1eae, 0x1eaf },  /* upper */
+	{ 0x1eae, 0x1eaf },  /* lower */
+	{ 0x1eb0, 0x1eb1 },  /* upper */
+	{ 0x1eb0, 0x1eb1 },  /* lower */
+	{ 0x1eb2, 0x1eb3 },  /* upper */
+	{ 0x1eb2, 0x1eb3 },  /* lower */
+	{ 0x1eb4, 0x1eb5 },  /* upper */
+	{ 0x1eb4, 0x1eb5 },  /* lower */
+	{ 0x1eb6, 0x1eb7 },  /* upper */
+	{ 0x1eb6, 0x1eb7 },  /* lower */
+	{ 0x1eb8, 0x1eb9 },  /* upper */
+	{ 0x1eb8, 0x1eb9 },  /* lower */
+	{ 0x1eba, 0x1ebb },  /* upper */
+	{ 0x1eba, 0x1ebb },  /* lower */
+	{ 0x1ebc, 0x1ebd },  /* upper */
+	{ 0x1ebc, 0x1ebd },  /* lower */
+	{ 0x1ebe, 0x1ebf },  /* upper */
+	{ 0x1ebe, 0x1ebf },  /* lower */
+	{ 0x1ec0, 0x1ec1 },  /* upper */
+	{ 0x1ec0, 0x1ec1 },  /* lower */
+	{ 0x1ec2, 0x1ec3 },  /* upper */
+	{ 0x1ec2, 0x1ec3 },  /* lower */
+	{ 0x1ec4, 0x1ec5 },  /* upper */
+	{ 0x1ec4, 0x1ec5 },  /* lower */
+	{ 0x1ec6, 0x1ec7 },  /* upper */
+	{ 0x1ec6, 0x1ec7 },  /* lower */
+	{ 0x1ec8, 0x1ec9 },  /* upper */
+	{ 0x1ec8, 0x1ec9 },  /* lower */
+	{ 0x1eca, 0x1ecb },  /* upper */
+	{ 0x1eca, 0x1ecb },  /* lower */
+	{ 0x1ecc, 0x1ecd },  /* upper */
+	{ 0x1ecc, 0x1ecd },  /* lower */
+	{ 0x1ece, 0x1ecf },  /* upper */
+	{ 0x1ece, 0x1ecf },  /* lower */
+	{ 0x1ed0, 0x1ed1 },  /* upper */
+	{ 0x1ed0, 0x1ed1 },  /* lower */
+	{ 0x1ed2, 0x1ed3 },  /* upper */
+	{ 0x1ed2, 0x1ed3 },  /* lower */
+	{ 0x1ed4, 0x1ed5 },  /* upper */
+	{ 0x1ed4, 0x1ed5 },  /* lower */
+	{ 0x1ed6, 0x1ed7 },  /* upper */
+	{ 0x1ed6, 0x1ed7 },  /* lower */
+	{ 0x1ed8, 0x1ed9 },  /* upper */
+	{ 0x1ed8, 0x1ed9 },  /* lower */
+	{ 0x1eda, 0x1edb },  /* upper */
+	{ 0x1eda, 0x1edb },  /* lower */
+	{ 0x1edc, 0x1edd },  /* upper */
+	{ 0x1edc, 0x1edd },  /* lower */
+	{ 0x1ede, 0x1edf },  /* upper */
+	{ 0x1ede, 0x1edf },  /* lower */
+	{ 0x1ee0, 0x1ee1 },  /* upper */
+	{ 0x1ee0, 0x1ee1 },  /* lower */
+	{ 0x1ee2, 0x1ee3 },  /* upper */
+	{ 0x1ee2, 0x1ee3 },  /* lower */
+	{ 0x1ee4, 0x1ee5 },  /* upper */
+	{ 0x1ee4, 0x1ee5 },  /* lower */
+	{ 0x1ee6, 0x1ee7 },  /* upper */
+	{ 0x1ee6, 0x1ee7 },  /* lower */
+	{ 0x1ee8, 0x1ee9 },  /* upper */
+	{ 0x1ee8, 0x1ee9 },  /* lower */
+	{ 0x1eea, 0x1eeb },  /* upper */
+	{ 0x1eea, 0x1eeb },  /* lower */
+	{ 0x1eec, 0x1eed },  /* upper */
+	{ 0x1eec, 0x1eed },  /* lower */
+	{ 0x1eee, 0x1eef },  /* upper */
+	{ 0x1eee, 0x1eef },  /* lower */
+	{ 0x1ef0, 0x1ef1 },  /* upper */
+	{ 0x1ef0, 0x1ef1 },  /* lower */
+	{ 0x1ef2, 0x1ef3 },  /* upper */
+	{ 0x1ef2, 0x1ef3 },  /* lower */
+	{ 0x1ef4, 0x1ef5 },  /* upper */
+	{ 0x1ef4, 0x1ef5 },  /* lower */
+	{ 0x1ef6, 0x1ef7 },  /* upper */
+	{ 0x1ef6, 0x1ef7 },  /* lower */
+	{ 0x1ef8, 0x1ef9 },  /* upper */
+	{ 0x1ef8, 0x1ef9 },  /* lower */
+	{ 0x1efa, 0x1efa },
+	{ 0x1efb, 0x1efb },
+	{ 0x1efc, 0x1efc },
+	{ 0x1efd, 0x1efd },
+	{ 0x1efe, 0x1efe },
+	{ 0x1eff, 0x1eff },
+};
+
+gli_case_block_t unigen_case_block_0x1f[256] = {
+	{ 0x1f08, 0x1f00 },  /* lower */
+	{ 0x1f09, 0x1f01 },  /* lower */
+	{ 0x1f0a, 0x1f02 },  /* lower */
+	{ 0x1f0b, 0x1f03 },  /* lower */
+	{ 0x1f0c, 0x1f04 },  /* lower */
+	{ 0x1f0d, 0x1f05 },  /* lower */
+	{ 0x1f0e, 0x1f06 },  /* lower */
+	{ 0x1f0f, 0x1f07 },  /* lower */
+	{ 0x1f08, 0x1f00 },  /* upper */
+	{ 0x1f09, 0x1f01 },  /* upper */
+	{ 0x1f0a, 0x1f02 },  /* upper */
+	{ 0x1f0b, 0x1f03 },  /* upper */
+	{ 0x1f0c, 0x1f04 },  /* upper */
+	{ 0x1f0d, 0x1f05 },  /* upper */
+	{ 0x1f0e, 0x1f06 },  /* upper */
+	{ 0x1f0f, 0x1f07 },  /* upper */
+	{ 0x1f18, 0x1f10 },  /* lower */
+	{ 0x1f19, 0x1f11 },  /* lower */
+	{ 0x1f1a, 0x1f12 },  /* lower */
+	{ 0x1f1b, 0x1f13 },  /* lower */
+	{ 0x1f1c, 0x1f14 },  /* lower */
+	{ 0x1f1d, 0x1f15 },  /* lower */
+	{ 0x1f16, 0x1f16 },
+	{ 0x1f17, 0x1f17 },
+	{ 0x1f18, 0x1f10 },  /* upper */
+	{ 0x1f19, 0x1f11 },  /* upper */
+	{ 0x1f1a, 0x1f12 },  /* upper */
+	{ 0x1f1b, 0x1f13 },  /* upper */
+	{ 0x1f1c, 0x1f14 },  /* upper */
+	{ 0x1f1d, 0x1f15 },  /* upper */
+	{ 0x1f1e, 0x1f1e },
+	{ 0x1f1f, 0x1f1f },
+	{ 0x1f28, 0x1f20 },  /* lower */
+	{ 0x1f29, 0x1f21 },  /* lower */
+	{ 0x1f2a, 0x1f22 },  /* lower */
+	{ 0x1f2b, 0x1f23 },  /* lower */
+	{ 0x1f2c, 0x1f24 },  /* lower */
+	{ 0x1f2d, 0x1f25 },  /* lower */
+	{ 0x1f2e, 0x1f26 },  /* lower */
+	{ 0x1f2f, 0x1f27 },  /* lower */
+	{ 0x1f28, 0x1f20 },  /* upper */
+	{ 0x1f29, 0x1f21 },  /* upper */
+	{ 0x1f2a, 0x1f22 },  /* upper */
+	{ 0x1f2b, 0x1f23 },  /* upper */
+	{ 0x1f2c, 0x1f24 },  /* upper */
+	{ 0x1f2d, 0x1f25 },  /* upper */
+	{ 0x1f2e, 0x1f26 },  /* upper */
+	{ 0x1f2f, 0x1f27 },  /* upper */
+	{ 0x1f38, 0x1f30 },  /* lower */
+	{ 0x1f39, 0x1f31 },  /* lower */
+	{ 0x1f3a, 0x1f32 },  /* lower */
+	{ 0x1f3b, 0x1f33 },  /* lower */
+	{ 0x1f3c, 0x1f34 },  /* lower */
+	{ 0x1f3d, 0x1f35 },  /* lower */
+	{ 0x1f3e, 0x1f36 },  /* lower */
+	{ 0x1f3f, 0x1f37 },  /* lower */
+	{ 0x1f38, 0x1f30 },  /* upper */
+	{ 0x1f39, 0x1f31 },  /* upper */
+	{ 0x1f3a, 0x1f32 },  /* upper */
+	{ 0x1f3b, 0x1f33 },  /* upper */
+	{ 0x1f3c, 0x1f34 },  /* upper */
+	{ 0x1f3d, 0x1f35 },  /* upper */
+	{ 0x1f3e, 0x1f36 },  /* upper */
+	{ 0x1f3f, 0x1f37 },  /* upper */
+	{ 0x1f48, 0x1f40 },  /* lower */
+	{ 0x1f49, 0x1f41 },  /* lower */
+	{ 0x1f4a, 0x1f42 },  /* lower */
+	{ 0x1f4b, 0x1f43 },  /* lower */
+	{ 0x1f4c, 0x1f44 },  /* lower */
+	{ 0x1f4d, 0x1f45 },  /* lower */
+	{ 0x1f46, 0x1f46 },
+	{ 0x1f47, 0x1f47 },
+	{ 0x1f48, 0x1f40 },  /* upper */
+	{ 0x1f49, 0x1f41 },  /* upper */
+	{ 0x1f4a, 0x1f42 },  /* upper */
+	{ 0x1f4b, 0x1f43 },  /* upper */
+	{ 0x1f4c, 0x1f44 },  /* upper */
+	{ 0x1f4d, 0x1f45 },  /* upper */
+	{ 0x1f4e, 0x1f4e },
+	{ 0x1f4f, 0x1f4f },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1f59, 0x1f51 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1f5b, 0x1f53 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1f5d, 0x1f55 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1f5f, 0x1f57 },  /* lower */
+	{ 0x1f58, 0x1f58 },
+	{ 0x1f59, 0x1f51 },  /* upper */
+	{ 0x1f5a, 0x1f5a },
+	{ 0x1f5b, 0x1f53 },  /* upper */
+	{ 0x1f5c, 0x1f5c },
+	{ 0x1f5d, 0x1f55 },  /* upper */
+	{ 0x1f5e, 0x1f5e },
+	{ 0x1f5f, 0x1f57 },  /* upper */
+	{ 0x1f68, 0x1f60 },  /* lower */
+	{ 0x1f69, 0x1f61 },  /* lower */
+	{ 0x1f6a, 0x1f62 },  /* lower */
+	{ 0x1f6b, 0x1f63 },  /* lower */
+	{ 0x1f6c, 0x1f64 },  /* lower */
+	{ 0x1f6d, 0x1f65 },  /* lower */
+	{ 0x1f6e, 0x1f66 },  /* lower */
+	{ 0x1f6f, 0x1f67 },  /* lower */
+	{ 0x1f68, 0x1f60 },  /* upper */
+	{ 0x1f69, 0x1f61 },  /* upper */
+	{ 0x1f6a, 0x1f62 },  /* upper */
+	{ 0x1f6b, 0x1f63 },  /* upper */
+	{ 0x1f6c, 0x1f64 },  /* upper */
+	{ 0x1f6d, 0x1f65 },  /* upper */
+	{ 0x1f6e, 0x1f66 },  /* upper */
+	{ 0x1f6f, 0x1f67 },  /* upper */
+	{ 0x1fba, 0x1f70 },  /* lower */
+	{ 0x1fbb, 0x1f71 },  /* lower */
+	{ 0x1fc8, 0x1f72 },  /* lower */
+	{ 0x1fc9, 0x1f73 },  /* lower */
+	{ 0x1fca, 0x1f74 },  /* lower */
+	{ 0x1fcb, 0x1f75 },  /* lower */
+	{ 0x1fda, 0x1f76 },  /* lower */
+	{ 0x1fdb, 0x1f77 },  /* lower */
+	{ 0x1ff8, 0x1f78 },  /* lower */
+	{ 0x1ff9, 0x1f79 },  /* lower */
+	{ 0x1fea, 0x1f7a },  /* lower */
+	{ 0x1feb, 0x1f7b },  /* lower */
+	{ 0x1ffa, 0x1f7c },  /* lower */
+	{ 0x1ffb, 0x1f7d },  /* lower */
+	{ 0x1f7e, 0x1f7e },
+	{ 0x1f7f, 0x1f7f },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fb8, 0x1fb0 },  /* lower */
+	{ 0x1fb9, 0x1fb1 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fb5, 0x1fb5 },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fb8, 0x1fb0 },  /* upper */
+	{ 0x1fb9, 0x1fb1 },  /* upper */
+	{ 0x1fba, 0x1f70 },  /* upper */
+	{ 0x1fbb, 0x1f71 },  /* upper */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fbd, 0x1fbd },
+	{ 0x399, 0x1fbe },  /* lower */
+	{ 0x1fbf, 0x1fbf },
+	{ 0x1fc0, 0x1fc0 },
+	{ 0x1fc1, 0x1fc1 },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fc5, 0x1fc5 },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fc8, 0x1f72 },  /* upper */
+	{ 0x1fc9, 0x1f73 },  /* upper */
+	{ 0x1fca, 0x1f74 },  /* upper */
+	{ 0x1fcb, 0x1f75 },  /* upper */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fcd, 0x1fcd },
+	{ 0x1fce, 0x1fce },
+	{ 0x1fcf, 0x1fcf },
+	{ 0x1fd8, 0x1fd0 },  /* lower */
+	{ 0x1fd9, 0x1fd1 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fd4, 0x1fd4 },
+	{ 0x1fd5, 0x1fd5 },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fd8, 0x1fd0 },  /* upper */
+	{ 0x1fd9, 0x1fd1 },  /* upper */
+	{ 0x1fda, 0x1f76 },  /* upper */
+	{ 0x1fdb, 0x1f77 },  /* upper */
+	{ 0x1fdc, 0x1fdc },
+	{ 0x1fdd, 0x1fdd },
+	{ 0x1fde, 0x1fde },
+	{ 0x1fdf, 0x1fdf },
+	{ 0x1fe8, 0x1fe0 },  /* lower */
+	{ 0x1fe9, 0x1fe1 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fec, 0x1fe5 },  /* lower */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1fe8, 0x1fe0 },  /* upper */
+	{ 0x1fe9, 0x1fe1 },  /* upper */
+	{ 0x1fea, 0x1f7a },  /* upper */
+	{ 0x1feb, 0x1f7b },  /* upper */
+	{ 0x1fec, 0x1fe5 },  /* upper */
+	{ 0x1fed, 0x1fed },
+	{ 0x1fee, 0x1fee },
+	{ 0x1fef, 0x1fef },
+	{ 0x1ff0, 0x1ff0 },
+	{ 0x1ff1, 0x1ff1 },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1ff5, 0x1ff5 },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1ff8, 0x1f78 },  /* upper */
+	{ 0x1ff9, 0x1f79 },  /* upper */
+	{ 0x1ffa, 0x1f7c },  /* upper */
+	{ 0x1ffb, 0x1f7d },  /* upper */
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0x1ffd, 0x1ffd },
+	{ 0x1ffe, 0x1ffe },
+	{ 0x1fff, 0x1fff },
+};
+
+gli_case_block_t unigen_case_block_0x21[256] = {
+	{ 0x2100, 0x2100 },
+	{ 0x2101, 0x2101 },
+	{ 0x2102, 0x2102 },
+	{ 0x2103, 0x2103 },
+	{ 0x2104, 0x2104 },
+	{ 0x2105, 0x2105 },
+	{ 0x2106, 0x2106 },
+	{ 0x2107, 0x2107 },
+	{ 0x2108, 0x2108 },
+	{ 0x2109, 0x2109 },
+	{ 0x210a, 0x210a },
+	{ 0x210b, 0x210b },
+	{ 0x210c, 0x210c },
+	{ 0x210d, 0x210d },
+	{ 0x210e, 0x210e },
+	{ 0x210f, 0x210f },
+	{ 0x2110, 0x2110 },
+	{ 0x2111, 0x2111 },
+	{ 0x2112, 0x2112 },
+	{ 0x2113, 0x2113 },
+	{ 0x2114, 0x2114 },
+	{ 0x2115, 0x2115 },
+	{ 0x2116, 0x2116 },
+	{ 0x2117, 0x2117 },
+	{ 0x2118, 0x2118 },
+	{ 0x2119, 0x2119 },
+	{ 0x211a, 0x211a },
+	{ 0x211b, 0x211b },
+	{ 0x211c, 0x211c },
+	{ 0x211d, 0x211d },
+	{ 0x211e, 0x211e },
+	{ 0x211f, 0x211f },
+	{ 0x2120, 0x2120 },
+	{ 0x2121, 0x2121 },
+	{ 0x2122, 0x2122 },
+	{ 0x2123, 0x2123 },
+	{ 0x2124, 0x2124 },
+	{ 0x2125, 0x2125 },
+	{ 0x2126, 0x3c9 },  /* upper */
+	{ 0x2127, 0x2127 },
+	{ 0x2128, 0x2128 },
+	{ 0x2129, 0x2129 },
+	{ 0x212a, 0x6b },  /* upper */
+	{ 0x212b, 0xe5 },  /* upper */
+	{ 0x212c, 0x212c },
+	{ 0x212d, 0x212d },
+	{ 0x212e, 0x212e },
+	{ 0x212f, 0x212f },
+	{ 0x2130, 0x2130 },
+	{ 0x2131, 0x2131 },
+	{ 0x2132, 0x2132 },
+	{ 0x2133, 0x2133 },
+	{ 0x2134, 0x2134 },
+	{ 0x2135, 0x2135 },
+	{ 0x2136, 0x2136 },
+	{ 0x2137, 0x2137 },
+	{ 0x2138, 0x2138 },
+	{ 0x2139, 0x2139 },
+	{ 0x213a, 0x213a },
+	{ 0x213b, 0x213b },
+	{ 0x213c, 0x213c },
+	{ 0x213d, 0x213d },
+	{ 0x213e, 0x213e },
+	{ 0x213f, 0x213f },
+	{ 0x2140, 0x2140 },
+	{ 0x2141, 0x2141 },
+	{ 0x2142, 0x2142 },
+	{ 0x2143, 0x2143 },
+	{ 0x2144, 0x2144 },
+	{ 0x2145, 0x2145 },
+	{ 0x2146, 0x2146 },
+	{ 0x2147, 0x2147 },
+	{ 0x2148, 0x2148 },
+	{ 0x2149, 0x2149 },
+	{ 0x214a, 0x214a },
+	{ 0x214b, 0x214b },
+	{ 0x214c, 0x214c },
+	{ 0x214d, 0x214d },
+	{ 0x214e, 0x214e },
+	{ 0x214f, 0x214f },
+	{ 0x2150, 0x2150 },
+	{ 0x2151, 0x2151 },
+	{ 0x2152, 0x2152 },
+	{ 0x2153, 0x2153 },
+	{ 0x2154, 0x2154 },
+	{ 0x2155, 0x2155 },
+	{ 0x2156, 0x2156 },
+	{ 0x2157, 0x2157 },
+	{ 0x2158, 0x2158 },
+	{ 0x2159, 0x2159 },
+	{ 0x215a, 0x215a },
+	{ 0x215b, 0x215b },
+	{ 0x215c, 0x215c },
+	{ 0x215d, 0x215d },
+	{ 0x215e, 0x215e },
+	{ 0x215f, 0x215f },
+	{ 0x2160, 0x2170 },  /* upper */
+	{ 0x2161, 0x2171 },  /* upper */
+	{ 0x2162, 0x2172 },  /* upper */
+	{ 0x2163, 0x2173 },  /* upper */
+	{ 0x2164, 0x2174 },  /* upper */
+	{ 0x2165, 0x2175 },  /* upper */
+	{ 0x2166, 0x2176 },  /* upper */
+	{ 0x2167, 0x2177 },  /* upper */
+	{ 0x2168, 0x2178 },  /* upper */
+	{ 0x2169, 0x2179 },  /* upper */
+	{ 0x216a, 0x217a },  /* upper */
+	{ 0x216b, 0x217b },  /* upper */
+	{ 0x216c, 0x217c },  /* upper */
+	{ 0x216d, 0x217d },  /* upper */
+	{ 0x216e, 0x217e },  /* upper */
+	{ 0x216f, 0x217f },  /* upper */
+	{ 0x2160, 0x2170 },  /* lower */
+	{ 0x2161, 0x2171 },  /* lower */
+	{ 0x2162, 0x2172 },  /* lower */
+	{ 0x2163, 0x2173 },  /* lower */
+	{ 0x2164, 0x2174 },  /* lower */
+	{ 0x2165, 0x2175 },  /* lower */
+	{ 0x2166, 0x2176 },  /* lower */
+	{ 0x2167, 0x2177 },  /* lower */
+	{ 0x2168, 0x2178 },  /* lower */
+	{ 0x2169, 0x2179 },  /* lower */
+	{ 0x216a, 0x217a },  /* lower */
+	{ 0x216b, 0x217b },  /* lower */
+	{ 0x216c, 0x217c },  /* lower */
+	{ 0x216d, 0x217d },  /* lower */
+	{ 0x216e, 0x217e },  /* lower */
+	{ 0x216f, 0x217f },  /* lower */
+	{ 0x2180, 0x2180 },
+	{ 0x2181, 0x2181 },
+	{ 0x2182, 0x2182 },
+	{ 0x2183, 0x2183 },
+	{ 0x2184, 0x2184 },
+	{ 0x2185, 0x2185 },
+	{ 0x2186, 0x2186 },
+	{ 0x2187, 0x2187 },
+	{ 0x2188, 0x2188 },
+	{ 0x2189, 0x2189 },
+	{ 0x218a, 0x218a },
+	{ 0x218b, 0x218b },
+	{ 0x218c, 0x218c },
+	{ 0x218d, 0x218d },
+	{ 0x218e, 0x218e },
+	{ 0x218f, 0x218f },
+	{ 0x2190, 0x2190 },
+	{ 0x2191, 0x2191 },
+	{ 0x2192, 0x2192 },
+	{ 0x2193, 0x2193 },
+	{ 0x2194, 0x2194 },
+	{ 0x2195, 0x2195 },
+	{ 0x2196, 0x2196 },
+	{ 0x2197, 0x2197 },
+	{ 0x2198, 0x2198 },
+	{ 0x2199, 0x2199 },
+	{ 0x219a, 0x219a },
+	{ 0x219b, 0x219b },
+	{ 0x219c, 0x219c },
+	{ 0x219d, 0x219d },
+	{ 0x219e, 0x219e },
+	{ 0x219f, 0x219f },
+	{ 0x21a0, 0x21a0 },
+	{ 0x21a1, 0x21a1 },
+	{ 0x21a2, 0x21a2 },
+	{ 0x21a3, 0x21a3 },
+	{ 0x21a4, 0x21a4 },
+	{ 0x21a5, 0x21a5 },
+	{ 0x21a6, 0x21a6 },
+	{ 0x21a7, 0x21a7 },
+	{ 0x21a8, 0x21a8 },
+	{ 0x21a9, 0x21a9 },
+	{ 0x21aa, 0x21aa },
+	{ 0x21ab, 0x21ab },
+	{ 0x21ac, 0x21ac },
+	{ 0x21ad, 0x21ad },
+	{ 0x21ae, 0x21ae },
+	{ 0x21af, 0x21af },
+	{ 0x21b0, 0x21b0 },
+	{ 0x21b1, 0x21b1 },
+	{ 0x21b2, 0x21b2 },
+	{ 0x21b3, 0x21b3 },
+	{ 0x21b4, 0x21b4 },
+	{ 0x21b5, 0x21b5 },
+	{ 0x21b6, 0x21b6 },
+	{ 0x21b7, 0x21b7 },
+	{ 0x21b8, 0x21b8 },
+	{ 0x21b9, 0x21b9 },
+	{ 0x21ba, 0x21ba },
+	{ 0x21bb, 0x21bb },
+	{ 0x21bc, 0x21bc },
+	{ 0x21bd, 0x21bd },
+	{ 0x21be, 0x21be },
+	{ 0x21bf, 0x21bf },
+	{ 0x21c0, 0x21c0 },
+	{ 0x21c1, 0x21c1 },
+	{ 0x21c2, 0x21c2 },
+	{ 0x21c3, 0x21c3 },
+	{ 0x21c4, 0x21c4 },
+	{ 0x21c5, 0x21c5 },
+	{ 0x21c6, 0x21c6 },
+	{ 0x21c7, 0x21c7 },
+	{ 0x21c8, 0x21c8 },
+	{ 0x21c9, 0x21c9 },
+	{ 0x21ca, 0x21ca },
+	{ 0x21cb, 0x21cb },
+	{ 0x21cc, 0x21cc },
+	{ 0x21cd, 0x21cd },
+	{ 0x21ce, 0x21ce },
+	{ 0x21cf, 0x21cf },
+	{ 0x21d0, 0x21d0 },
+	{ 0x21d1, 0x21d1 },
+	{ 0x21d2, 0x21d2 },
+	{ 0x21d3, 0x21d3 },
+	{ 0x21d4, 0x21d4 },
+	{ 0x21d5, 0x21d5 },
+	{ 0x21d6, 0x21d6 },
+	{ 0x21d7, 0x21d7 },
+	{ 0x21d8, 0x21d8 },
+	{ 0x21d9, 0x21d9 },
+	{ 0x21da, 0x21da },
+	{ 0x21db, 0x21db },
+	{ 0x21dc, 0x21dc },
+	{ 0x21dd, 0x21dd },
+	{ 0x21de, 0x21de },
+	{ 0x21df, 0x21df },
+	{ 0x21e0, 0x21e0 },
+	{ 0x21e1, 0x21e1 },
+	{ 0x21e2, 0x21e2 },
+	{ 0x21e3, 0x21e3 },
+	{ 0x21e4, 0x21e4 },
+	{ 0x21e5, 0x21e5 },
+	{ 0x21e6, 0x21e6 },
+	{ 0x21e7, 0x21e7 },
+	{ 0x21e8, 0x21e8 },
+	{ 0x21e9, 0x21e9 },
+	{ 0x21ea, 0x21ea },
+	{ 0x21eb, 0x21eb },
+	{ 0x21ec, 0x21ec },
+	{ 0x21ed, 0x21ed },
+	{ 0x21ee, 0x21ee },
+	{ 0x21ef, 0x21ef },
+	{ 0x21f0, 0x21f0 },
+	{ 0x21f1, 0x21f1 },
+	{ 0x21f2, 0x21f2 },
+	{ 0x21f3, 0x21f3 },
+	{ 0x21f4, 0x21f4 },
+	{ 0x21f5, 0x21f5 },
+	{ 0x21f6, 0x21f6 },
+	{ 0x21f7, 0x21f7 },
+	{ 0x21f8, 0x21f8 },
+	{ 0x21f9, 0x21f9 },
+	{ 0x21fa, 0x21fa },
+	{ 0x21fb, 0x21fb },
+	{ 0x21fc, 0x21fc },
+	{ 0x21fd, 0x21fd },
+	{ 0x21fe, 0x21fe },
+	{ 0x21ff, 0x21ff },
+};
+
+gli_case_block_t unigen_case_block_0x24[256] = {
+	{ 0x2400, 0x2400 },
+	{ 0x2401, 0x2401 },
+	{ 0x2402, 0x2402 },
+	{ 0x2403, 0x2403 },
+	{ 0x2404, 0x2404 },
+	{ 0x2405, 0x2405 },
+	{ 0x2406, 0x2406 },
+	{ 0x2407, 0x2407 },
+	{ 0x2408, 0x2408 },
+	{ 0x2409, 0x2409 },
+	{ 0x240a, 0x240a },
+	{ 0x240b, 0x240b },
+	{ 0x240c, 0x240c },
+	{ 0x240d, 0x240d },
+	{ 0x240e, 0x240e },
+	{ 0x240f, 0x240f },
+	{ 0x2410, 0x2410 },
+	{ 0x2411, 0x2411 },
+	{ 0x2412, 0x2412 },
+	{ 0x2413, 0x2413 },
+	{ 0x2414, 0x2414 },
+	{ 0x2415, 0x2415 },
+	{ 0x2416, 0x2416 },
+	{ 0x2417, 0x2417 },
+	{ 0x2418, 0x2418 },
+	{ 0x2419, 0x2419 },
+	{ 0x241a, 0x241a },
+	{ 0x241b, 0x241b },
+	{ 0x241c, 0x241c },
+	{ 0x241d, 0x241d },
+	{ 0x241e, 0x241e },
+	{ 0x241f, 0x241f },
+	{ 0x2420, 0x2420 },
+	{ 0x2421, 0x2421 },
+	{ 0x2422, 0x2422 },
+	{ 0x2423, 0x2423 },
+	{ 0x2424, 0x2424 },
+	{ 0x2425, 0x2425 },
+	{ 0x2426, 0x2426 },
+	{ 0x2427, 0x2427 },
+	{ 0x2428, 0x2428 },
+	{ 0x2429, 0x2429 },
+	{ 0x242a, 0x242a },
+	{ 0x242b, 0x242b },
+	{ 0x242c, 0x242c },
+	{ 0x242d, 0x242d },
+	{ 0x242e, 0x242e },
+	{ 0x242f, 0x242f },
+	{ 0x2430, 0x2430 },
+	{ 0x2431, 0x2431 },
+	{ 0x2432, 0x2432 },
+	{ 0x2433, 0x2433 },
+	{ 0x2434, 0x2434 },
+	{ 0x2435, 0x2435 },
+	{ 0x2436, 0x2436 },
+	{ 0x2437, 0x2437 },
+	{ 0x2438, 0x2438 },
+	{ 0x2439, 0x2439 },
+	{ 0x243a, 0x243a },
+	{ 0x243b, 0x243b },
+	{ 0x243c, 0x243c },
+	{ 0x243d, 0x243d },
+	{ 0x243e, 0x243e },
+	{ 0x243f, 0x243f },
+	{ 0x2440, 0x2440 },
+	{ 0x2441, 0x2441 },
+	{ 0x2442, 0x2442 },
+	{ 0x2443, 0x2443 },
+	{ 0x2444, 0x2444 },
+	{ 0x2445, 0x2445 },
+	{ 0x2446, 0x2446 },
+	{ 0x2447, 0x2447 },
+	{ 0x2448, 0x2448 },
+	{ 0x2449, 0x2449 },
+	{ 0x244a, 0x244a },
+	{ 0x244b, 0x244b },
+	{ 0x244c, 0x244c },
+	{ 0x244d, 0x244d },
+	{ 0x244e, 0x244e },
+	{ 0x244f, 0x244f },
+	{ 0x2450, 0x2450 },
+	{ 0x2451, 0x2451 },
+	{ 0x2452, 0x2452 },
+	{ 0x2453, 0x2453 },
+	{ 0x2454, 0x2454 },
+	{ 0x2455, 0x2455 },
+	{ 0x2456, 0x2456 },
+	{ 0x2457, 0x2457 },
+	{ 0x2458, 0x2458 },
+	{ 0x2459, 0x2459 },
+	{ 0x245a, 0x245a },
+	{ 0x245b, 0x245b },
+	{ 0x245c, 0x245c },
+	{ 0x245d, 0x245d },
+	{ 0x245e, 0x245e },
+	{ 0x245f, 0x245f },
+	{ 0x2460, 0x2460 },
+	{ 0x2461, 0x2461 },
+	{ 0x2462, 0x2462 },
+	{ 0x2463, 0x2463 },
+	{ 0x2464, 0x2464 },
+	{ 0x2465, 0x2465 },
+	{ 0x2466, 0x2466 },
+	{ 0x2467, 0x2467 },
+	{ 0x2468, 0x2468 },
+	{ 0x2469, 0x2469 },
+	{ 0x246a, 0x246a },
+	{ 0x246b, 0x246b },
+	{ 0x246c, 0x246c },
+	{ 0x246d, 0x246d },
+	{ 0x246e, 0x246e },
+	{ 0x246f, 0x246f },
+	{ 0x2470, 0x2470 },
+	{ 0x2471, 0x2471 },
+	{ 0x2472, 0x2472 },
+	{ 0x2473, 0x2473 },
+	{ 0x2474, 0x2474 },
+	{ 0x2475, 0x2475 },
+	{ 0x2476, 0x2476 },
+	{ 0x2477, 0x2477 },
+	{ 0x2478, 0x2478 },
+	{ 0x2479, 0x2479 },
+	{ 0x247a, 0x247a },
+	{ 0x247b, 0x247b },
+	{ 0x247c, 0x247c },
+	{ 0x247d, 0x247d },
+	{ 0x247e, 0x247e },
+	{ 0x247f, 0x247f },
+	{ 0x2480, 0x2480 },
+	{ 0x2481, 0x2481 },
+	{ 0x2482, 0x2482 },
+	{ 0x2483, 0x2483 },
+	{ 0x2484, 0x2484 },
+	{ 0x2485, 0x2485 },
+	{ 0x2486, 0x2486 },
+	{ 0x2487, 0x2487 },
+	{ 0x2488, 0x2488 },
+	{ 0x2489, 0x2489 },
+	{ 0x248a, 0x248a },
+	{ 0x248b, 0x248b },
+	{ 0x248c, 0x248c },
+	{ 0x248d, 0x248d },
+	{ 0x248e, 0x248e },
+	{ 0x248f, 0x248f },
+	{ 0x2490, 0x2490 },
+	{ 0x2491, 0x2491 },
+	{ 0x2492, 0x2492 },
+	{ 0x2493, 0x2493 },
+	{ 0x2494, 0x2494 },
+	{ 0x2495, 0x2495 },
+	{ 0x2496, 0x2496 },
+	{ 0x2497, 0x2497 },
+	{ 0x2498, 0x2498 },
+	{ 0x2499, 0x2499 },
+	{ 0x249a, 0x249a },
+	{ 0x249b, 0x249b },
+	{ 0x249c, 0x249c },
+	{ 0x249d, 0x249d },
+	{ 0x249e, 0x249e },
+	{ 0x249f, 0x249f },
+	{ 0x24a0, 0x24a0 },
+	{ 0x24a1, 0x24a1 },
+	{ 0x24a2, 0x24a2 },
+	{ 0x24a3, 0x24a3 },
+	{ 0x24a4, 0x24a4 },
+	{ 0x24a5, 0x24a5 },
+	{ 0x24a6, 0x24a6 },
+	{ 0x24a7, 0x24a7 },
+	{ 0x24a8, 0x24a8 },
+	{ 0x24a9, 0x24a9 },
+	{ 0x24aa, 0x24aa },
+	{ 0x24ab, 0x24ab },
+	{ 0x24ac, 0x24ac },
+	{ 0x24ad, 0x24ad },
+	{ 0x24ae, 0x24ae },
+	{ 0x24af, 0x24af },
+	{ 0x24b0, 0x24b0 },
+	{ 0x24b1, 0x24b1 },
+	{ 0x24b2, 0x24b2 },
+	{ 0x24b3, 0x24b3 },
+	{ 0x24b4, 0x24b4 },
+	{ 0x24b5, 0x24b5 },
+	{ 0x24b6, 0x24d0 },  /* upper */
+	{ 0x24b7, 0x24d1 },  /* upper */
+	{ 0x24b8, 0x24d2 },  /* upper */
+	{ 0x24b9, 0x24d3 },  /* upper */
+	{ 0x24ba, 0x24d4 },  /* upper */
+	{ 0x24bb, 0x24d5 },  /* upper */
+	{ 0x24bc, 0x24d6 },  /* upper */
+	{ 0x24bd, 0x24d7 },  /* upper */
+	{ 0x24be, 0x24d8 },  /* upper */
+	{ 0x24bf, 0x24d9 },  /* upper */
+	{ 0x24c0, 0x24da },  /* upper */
+	{ 0x24c1, 0x24db },  /* upper */
+	{ 0x24c2, 0x24dc },  /* upper */
+	{ 0x24c3, 0x24dd },  /* upper */
+	{ 0x24c4, 0x24de },  /* upper */
+	{ 0x24c5, 0x24df },  /* upper */
+	{ 0x24c6, 0x24e0 },  /* upper */
+	{ 0x24c7, 0x24e1 },  /* upper */
+	{ 0x24c8, 0x24e2 },  /* upper */
+	{ 0x24c9, 0x24e3 },  /* upper */
+	{ 0x24ca, 0x24e4 },  /* upper */
+	{ 0x24cb, 0x24e5 },  /* upper */
+	{ 0x24cc, 0x24e6 },  /* upper */
+	{ 0x24cd, 0x24e7 },  /* upper */
+	{ 0x24ce, 0x24e8 },  /* upper */
+	{ 0x24cf, 0x24e9 },  /* upper */
+	{ 0x24b6, 0x24d0 },  /* lower */
+	{ 0x24b7, 0x24d1 },  /* lower */
+	{ 0x24b8, 0x24d2 },  /* lower */
+	{ 0x24b9, 0x24d3 },  /* lower */
+	{ 0x24ba, 0x24d4 },  /* lower */
+	{ 0x24bb, 0x24d5 },  /* lower */
+	{ 0x24bc, 0x24d6 },  /* lower */
+	{ 0x24bd, 0x24d7 },  /* lower */
+	{ 0x24be, 0x24d8 },  /* lower */
+	{ 0x24bf, 0x24d9 },  /* lower */
+	{ 0x24c0, 0x24da },  /* lower */
+	{ 0x24c1, 0x24db },  /* lower */
+	{ 0x24c2, 0x24dc },  /* lower */
+	{ 0x24c3, 0x24dd },  /* lower */
+	{ 0x24c4, 0x24de },  /* lower */
+	{ 0x24c5, 0x24df },  /* lower */
+	{ 0x24c6, 0x24e0 },  /* lower */
+	{ 0x24c7, 0x24e1 },  /* lower */
+	{ 0x24c8, 0x24e2 },  /* lower */
+	{ 0x24c9, 0x24e3 },  /* lower */
+	{ 0x24ca, 0x24e4 },  /* lower */
+	{ 0x24cb, 0x24e5 },  /* lower */
+	{ 0x24cc, 0x24e6 },  /* lower */
+	{ 0x24cd, 0x24e7 },  /* lower */
+	{ 0x24ce, 0x24e8 },  /* lower */
+	{ 0x24cf, 0x24e9 },  /* lower */
+	{ 0x24ea, 0x24ea },
+	{ 0x24eb, 0x24eb },
+	{ 0x24ec, 0x24ec },
+	{ 0x24ed, 0x24ed },
+	{ 0x24ee, 0x24ee },
+	{ 0x24ef, 0x24ef },
+	{ 0x24f0, 0x24f0 },
+	{ 0x24f1, 0x24f1 },
+	{ 0x24f2, 0x24f2 },
+	{ 0x24f3, 0x24f3 },
+	{ 0x24f4, 0x24f4 },
+	{ 0x24f5, 0x24f5 },
+	{ 0x24f6, 0x24f6 },
+	{ 0x24f7, 0x24f7 },
+	{ 0x24f8, 0x24f8 },
+	{ 0x24f9, 0x24f9 },
+	{ 0x24fa, 0x24fa },
+	{ 0x24fb, 0x24fb },
+	{ 0x24fc, 0x24fc },
+	{ 0x24fd, 0x24fd },
+	{ 0x24fe, 0x24fe },
+	{ 0x24ff, 0x24ff },
+};
+
+gli_case_block_t unigen_case_block_0xfb[256] = {
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xfb07, 0xfb07 },
+	{ 0xfb08, 0xfb08 },
+	{ 0xfb09, 0xfb09 },
+	{ 0xfb0a, 0xfb0a },
+	{ 0xfb0b, 0xfb0b },
+	{ 0xfb0c, 0xfb0c },
+	{ 0xfb0d, 0xfb0d },
+	{ 0xfb0e, 0xfb0e },
+	{ 0xfb0f, 0xfb0f },
+	{ 0xfb10, 0xfb10 },
+	{ 0xfb11, 0xfb11 },
+	{ 0xfb12, 0xfb12 },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xFFFFFFFF, 0xFFFFFFFF },
+	{ 0xfb18, 0xfb18 },
+	{ 0xfb19, 0xfb19 },
+	{ 0xfb1a, 0xfb1a },
+	{ 0xfb1b, 0xfb1b },
+	{ 0xfb1c, 0xfb1c },
+	{ 0xfb1d, 0xfb1d },
+	{ 0xfb1e, 0xfb1e },
+	{ 0xfb1f, 0xfb1f },
+	{ 0xfb20, 0xfb20 },
+	{ 0xfb21, 0xfb21 },
+	{ 0xfb22, 0xfb22 },
+	{ 0xfb23, 0xfb23 },
+	{ 0xfb24, 0xfb24 },
+	{ 0xfb25, 0xfb25 },
+	{ 0xfb26, 0xfb26 },
+	{ 0xfb27, 0xfb27 },
+	{ 0xfb28, 0xfb28 },
+	{ 0xfb29, 0xfb29 },
+	{ 0xfb2a, 0xfb2a },
+	{ 0xfb2b, 0xfb2b },
+	{ 0xfb2c, 0xfb2c },
+	{ 0xfb2d, 0xfb2d },
+	{ 0xfb2e, 0xfb2e },
+	{ 0xfb2f, 0xfb2f },
+	{ 0xfb30, 0xfb30 },
+	{ 0xfb31, 0xfb31 },
+	{ 0xfb32, 0xfb32 },
+	{ 0xfb33, 0xfb33 },
+	{ 0xfb34, 0xfb34 },
+	{ 0xfb35, 0xfb35 },
+	{ 0xfb36, 0xfb36 },
+	{ 0xfb37, 0xfb37 },
+	{ 0xfb38, 0xfb38 },
+	{ 0xfb39, 0xfb39 },
+	{ 0xfb3a, 0xfb3a },
+	{ 0xfb3b, 0xfb3b },
+	{ 0xfb3c, 0xfb3c },
+	{ 0xfb3d, 0xfb3d },
+	{ 0xfb3e, 0xfb3e },
+	{ 0xfb3f, 0xfb3f },
+	{ 0xfb40, 0xfb40 },
+	{ 0xfb41, 0xfb41 },
+	{ 0xfb42, 0xfb42 },
+	{ 0xfb43, 0xfb43 },
+	{ 0xfb44, 0xfb44 },
+	{ 0xfb45, 0xfb45 },
+	{ 0xfb46, 0xfb46 },
+	{ 0xfb47, 0xfb47 },
+	{ 0xfb48, 0xfb48 },
+	{ 0xfb49, 0xfb49 },
+	{ 0xfb4a, 0xfb4a },
+	{ 0xfb4b, 0xfb4b },
+	{ 0xfb4c, 0xfb4c },
+	{ 0xfb4d, 0xfb4d },
+	{ 0xfb4e, 0xfb4e },
+	{ 0xfb4f, 0xfb4f },
+	{ 0xfb50, 0xfb50 },
+	{ 0xfb51, 0xfb51 },
+	{ 0xfb52, 0xfb52 },
+	{ 0xfb53, 0xfb53 },
+	{ 0xfb54, 0xfb54 },
+	{ 0xfb55, 0xfb55 },
+	{ 0xfb56, 0xfb56 },
+	{ 0xfb57, 0xfb57 },
+	{ 0xfb58, 0xfb58 },
+	{ 0xfb59, 0xfb59 },
+	{ 0xfb5a, 0xfb5a },
+	{ 0xfb5b, 0xfb5b },
+	{ 0xfb5c, 0xfb5c },
+	{ 0xfb5d, 0xfb5d },
+	{ 0xfb5e, 0xfb5e },
+	{ 0xfb5f, 0xfb5f },
+	{ 0xfb60, 0xfb60 },
+	{ 0xfb61, 0xfb61 },
+	{ 0xfb62, 0xfb62 },
+	{ 0xfb63, 0xfb63 },
+	{ 0xfb64, 0xfb64 },
+	{ 0xfb65, 0xfb65 },
+	{ 0xfb66, 0xfb66 },
+	{ 0xfb67, 0xfb67 },
+	{ 0xfb68, 0xfb68 },
+	{ 0xfb69, 0xfb69 },
+	{ 0xfb6a, 0xfb6a },
+	{ 0xfb6b, 0xfb6b },
+	{ 0xfb6c, 0xfb6c },
+	{ 0xfb6d, 0xfb6d },
+	{ 0xfb6e, 0xfb6e },
+	{ 0xfb6f, 0xfb6f },
+	{ 0xfb70, 0xfb70 },
+	{ 0xfb71, 0xfb71 },
+	{ 0xfb72, 0xfb72 },
+	{ 0xfb73, 0xfb73 },
+	{ 0xfb74, 0xfb74 },
+	{ 0xfb75, 0xfb75 },
+	{ 0xfb76, 0xfb76 },
+	{ 0xfb77, 0xfb77 },
+	{ 0xfb78, 0xfb78 },
+	{ 0xfb79, 0xfb79 },
+	{ 0xfb7a, 0xfb7a },
+	{ 0xfb7b, 0xfb7b },
+	{ 0xfb7c, 0xfb7c },
+	{ 0xfb7d, 0xfb7d },
+	{ 0xfb7e, 0xfb7e },
+	{ 0xfb7f, 0xfb7f },
+	{ 0xfb80, 0xfb80 },
+	{ 0xfb81, 0xfb81 },
+	{ 0xfb82, 0xfb82 },
+	{ 0xfb83, 0xfb83 },
+	{ 0xfb84, 0xfb84 },
+	{ 0xfb85, 0xfb85 },
+	{ 0xfb86, 0xfb86 },
+	{ 0xfb87, 0xfb87 },
+	{ 0xfb88, 0xfb88 },
+	{ 0xfb89, 0xfb89 },
+	{ 0xfb8a, 0xfb8a },
+	{ 0xfb8b, 0xfb8b },
+	{ 0xfb8c, 0xfb8c },
+	{ 0xfb8d, 0xfb8d },
+	{ 0xfb8e, 0xfb8e },
+	{ 0xfb8f, 0xfb8f },
+	{ 0xfb90, 0xfb90 },
+	{ 0xfb91, 0xfb91 },
+	{ 0xfb92, 0xfb92 },
+	{ 0xfb93, 0xfb93 },
+	{ 0xfb94, 0xfb94 },
+	{ 0xfb95, 0xfb95 },
+	{ 0xfb96, 0xfb96 },
+	{ 0xfb97, 0xfb97 },
+	{ 0xfb98, 0xfb98 },
+	{ 0xfb99, 0xfb99 },
+	{ 0xfb9a, 0xfb9a },
+	{ 0xfb9b, 0xfb9b },
+	{ 0xfb9c, 0xfb9c },
+	{ 0xfb9d, 0xfb9d },
+	{ 0xfb9e, 0xfb9e },
+	{ 0xfb9f, 0xfb9f },
+	{ 0xfba0, 0xfba0 },
+	{ 0xfba1, 0xfba1 },
+	{ 0xfba2, 0xfba2 },
+	{ 0xfba3, 0xfba3 },
+	{ 0xfba4, 0xfba4 },
+	{ 0xfba5, 0xfba5 },
+	{ 0xfba6, 0xfba6 },
+	{ 0xfba7, 0xfba7 },
+	{ 0xfba8, 0xfba8 },
+	{ 0xfba9, 0xfba9 },
+	{ 0xfbaa, 0xfbaa },
+	{ 0xfbab, 0xfbab },
+	{ 0xfbac, 0xfbac },
+	{ 0xfbad, 0xfbad },
+	{ 0xfbae, 0xfbae },
+	{ 0xfbaf, 0xfbaf },
+	{ 0xfbb0, 0xfbb0 },
+	{ 0xfbb1, 0xfbb1 },
+	{ 0xfbb2, 0xfbb2 },
+	{ 0xfbb3, 0xfbb3 },
+	{ 0xfbb4, 0xfbb4 },
+	{ 0xfbb5, 0xfbb5 },
+	{ 0xfbb6, 0xfbb6 },
+	{ 0xfbb7, 0xfbb7 },
+	{ 0xfbb8, 0xfbb8 },
+	{ 0xfbb9, 0xfbb9 },
+	{ 0xfbba, 0xfbba },
+	{ 0xfbbb, 0xfbbb },
+	{ 0xfbbc, 0xfbbc },
+	{ 0xfbbd, 0xfbbd },
+	{ 0xfbbe, 0xfbbe },
+	{ 0xfbbf, 0xfbbf },
+	{ 0xfbc0, 0xfbc0 },
+	{ 0xfbc1, 0xfbc1 },
+	{ 0xfbc2, 0xfbc2 },
+	{ 0xfbc3, 0xfbc3 },
+	{ 0xfbc4, 0xfbc4 },
+	{ 0xfbc5, 0xfbc5 },
+	{ 0xfbc6, 0xfbc6 },
+	{ 0xfbc7, 0xfbc7 },
+	{ 0xfbc8, 0xfbc8 },
+	{ 0xfbc9, 0xfbc9 },
+	{ 0xfbca, 0xfbca },
+	{ 0xfbcb, 0xfbcb },
+	{ 0xfbcc, 0xfbcc },
+	{ 0xfbcd, 0xfbcd },
+	{ 0xfbce, 0xfbce },
+	{ 0xfbcf, 0xfbcf },
+	{ 0xfbd0, 0xfbd0 },
+	{ 0xfbd1, 0xfbd1 },
+	{ 0xfbd2, 0xfbd2 },
+	{ 0xfbd3, 0xfbd3 },
+	{ 0xfbd4, 0xfbd4 },
+	{ 0xfbd5, 0xfbd5 },
+	{ 0xfbd6, 0xfbd6 },
+	{ 0xfbd7, 0xfbd7 },
+	{ 0xfbd8, 0xfbd8 },
+	{ 0xfbd9, 0xfbd9 },
+	{ 0xfbda, 0xfbda },
+	{ 0xfbdb, 0xfbdb },
+	{ 0xfbdc, 0xfbdc },
+	{ 0xfbdd, 0xfbdd },
+	{ 0xfbde, 0xfbde },
+	{ 0xfbdf, 0xfbdf },
+	{ 0xfbe0, 0xfbe0 },
+	{ 0xfbe1, 0xfbe1 },
+	{ 0xfbe2, 0xfbe2 },
+	{ 0xfbe3, 0xfbe3 },
+	{ 0xfbe4, 0xfbe4 },
+	{ 0xfbe5, 0xfbe5 },
+	{ 0xfbe6, 0xfbe6 },
+	{ 0xfbe7, 0xfbe7 },
+	{ 0xfbe8, 0xfbe8 },
+	{ 0xfbe9, 0xfbe9 },
+	{ 0xfbea, 0xfbea },
+	{ 0xfbeb, 0xfbeb },
+	{ 0xfbec, 0xfbec },
+	{ 0xfbed, 0xfbed },
+	{ 0xfbee, 0xfbee },
+	{ 0xfbef, 0xfbef },
+	{ 0xfbf0, 0xfbf0 },
+	{ 0xfbf1, 0xfbf1 },
+	{ 0xfbf2, 0xfbf2 },
+	{ 0xfbf3, 0xfbf3 },
+	{ 0xfbf4, 0xfbf4 },
+	{ 0xfbf5, 0xfbf5 },
+	{ 0xfbf6, 0xfbf6 },
+	{ 0xfbf7, 0xfbf7 },
+	{ 0xfbf8, 0xfbf8 },
+	{ 0xfbf9, 0xfbf9 },
+	{ 0xfbfa, 0xfbfa },
+	{ 0xfbfb, 0xfbfb },
+	{ 0xfbfc, 0xfbfc },
+	{ 0xfbfd, 0xfbfd },
+	{ 0xfbfe, 0xfbfe },
+	{ 0xfbff, 0xfbff },
+};
+
+gli_case_block_t unigen_case_block_0xff[256] = {
+	{ 0xff00, 0xff00 },
+	{ 0xff01, 0xff01 },
+	{ 0xff02, 0xff02 },
+	{ 0xff03, 0xff03 },
+	{ 0xff04, 0xff04 },
+	{ 0xff05, 0xff05 },
+	{ 0xff06, 0xff06 },
+	{ 0xff07, 0xff07 },
+	{ 0xff08, 0xff08 },
+	{ 0xff09, 0xff09 },
+	{ 0xff0a, 0xff0a },
+	{ 0xff0b, 0xff0b },
+	{ 0xff0c, 0xff0c },
+	{ 0xff0d, 0xff0d },
+	{ 0xff0e, 0xff0e },
+	{ 0xff0f, 0xff0f },
+	{ 0xff10, 0xff10 },
+	{ 0xff11, 0xff11 },
+	{ 0xff12, 0xff12 },
+	{ 0xff13, 0xff13 },
+	{ 0xff14, 0xff14 },
+	{ 0xff15, 0xff15 },
+	{ 0xff16, 0xff16 },
+	{ 0xff17, 0xff17 },
+	{ 0xff18, 0xff18 },
+	{ 0xff19, 0xff19 },
+	{ 0xff1a, 0xff1a },
+	{ 0xff1b, 0xff1b },
+	{ 0xff1c, 0xff1c },
+	{ 0xff1d, 0xff1d },
+	{ 0xff1e, 0xff1e },
+	{ 0xff1f, 0xff1f },
+	{ 0xff20, 0xff20 },
+	{ 0xff21, 0xff41 },  /* upper */
+	{ 0xff22, 0xff42 },  /* upper */
+	{ 0xff23, 0xff43 },  /* upper */
+	{ 0xff24, 0xff44 },  /* upper */
+	{ 0xff25, 0xff45 },  /* upper */
+	{ 0xff26, 0xff46 },  /* upper */
+	{ 0xff27, 0xff47 },  /* upper */
+	{ 0xff28, 0xff48 },  /* upper */
+	{ 0xff29, 0xff49 },  /* upper */
+	{ 0xff2a, 0xff4a },  /* upper */
+	{ 0xff2b, 0xff4b },  /* upper */
+	{ 0xff2c, 0xff4c },  /* upper */
+	{ 0xff2d, 0xff4d },  /* upper */
+	{ 0xff2e, 0xff4e },  /* upper */
+	{ 0xff2f, 0xff4f },  /* upper */
+	{ 0xff30, 0xff50 },  /* upper */
+	{ 0xff31, 0xff51 },  /* upper */
+	{ 0xff32, 0xff52 },  /* upper */
+	{ 0xff33, 0xff53 },  /* upper */
+	{ 0xff34, 0xff54 },  /* upper */
+	{ 0xff35, 0xff55 },  /* upper */
+	{ 0xff36, 0xff56 },  /* upper */
+	{ 0xff37, 0xff57 },  /* upper */
+	{ 0xff38, 0xff58 },  /* upper */
+	{ 0xff39, 0xff59 },  /* upper */
+	{ 0xff3a, 0xff5a },  /* upper */
+	{ 0xff3b, 0xff3b },
+	{ 0xff3c, 0xff3c },
+	{ 0xff3d, 0xff3d },
+	{ 0xff3e, 0xff3e },
+	{ 0xff3f, 0xff3f },
+	{ 0xff40, 0xff40 },
+	{ 0xff21, 0xff41 },  /* lower */
+	{ 0xff22, 0xff42 },  /* lower */
+	{ 0xff23, 0xff43 },  /* lower */
+	{ 0xff24, 0xff44 },  /* lower */
+	{ 0xff25, 0xff45 },  /* lower */
+	{ 0xff26, 0xff46 },  /* lower */
+	{ 0xff27, 0xff47 },  /* lower */
+	{ 0xff28, 0xff48 },  /* lower */
+	{ 0xff29, 0xff49 },  /* lower */
+	{ 0xff2a, 0xff4a },  /* lower */
+	{ 0xff2b, 0xff4b },  /* lower */
+	{ 0xff2c, 0xff4c },  /* lower */
+	{ 0xff2d, 0xff4d },  /* lower */
+	{ 0xff2e, 0xff4e },  /* lower */
+	{ 0xff2f, 0xff4f },  /* lower */
+	{ 0xff30, 0xff50 },  /* lower */
+	{ 0xff31, 0xff51 },  /* lower */
+	{ 0xff32, 0xff52 },  /* lower */
+	{ 0xff33, 0xff53 },  /* lower */
+	{ 0xff34, 0xff54 },  /* lower */
+	{ 0xff35, 0xff55 },  /* lower */
+	{ 0xff36, 0xff56 },  /* lower */
+	{ 0xff37, 0xff57 },  /* lower */
+	{ 0xff38, 0xff58 },  /* lower */
+	{ 0xff39, 0xff59 },  /* lower */
+	{ 0xff3a, 0xff5a },  /* lower */
+	{ 0xff5b, 0xff5b },
+	{ 0xff5c, 0xff5c },
+	{ 0xff5d, 0xff5d },
+	{ 0xff5e, 0xff5e },
+	{ 0xff5f, 0xff5f },
+	{ 0xff60, 0xff60 },
+	{ 0xff61, 0xff61 },
+	{ 0xff62, 0xff62 },
+	{ 0xff63, 0xff63 },
+	{ 0xff64, 0xff64 },
+	{ 0xff65, 0xff65 },
+	{ 0xff66, 0xff66 },
+	{ 0xff67, 0xff67 },
+	{ 0xff68, 0xff68 },
+	{ 0xff69, 0xff69 },
+	{ 0xff6a, 0xff6a },
+	{ 0xff6b, 0xff6b },
+	{ 0xff6c, 0xff6c },
+	{ 0xff6d, 0xff6d },
+	{ 0xff6e, 0xff6e },
+	{ 0xff6f, 0xff6f },
+	{ 0xff70, 0xff70 },
+	{ 0xff71, 0xff71 },
+	{ 0xff72, 0xff72 },
+	{ 0xff73, 0xff73 },
+	{ 0xff74, 0xff74 },
+	{ 0xff75, 0xff75 },
+	{ 0xff76, 0xff76 },
+	{ 0xff77, 0xff77 },
+	{ 0xff78, 0xff78 },
+	{ 0xff79, 0xff79 },
+	{ 0xff7a, 0xff7a },
+	{ 0xff7b, 0xff7b },
+	{ 0xff7c, 0xff7c },
+	{ 0xff7d, 0xff7d },
+	{ 0xff7e, 0xff7e },
+	{ 0xff7f, 0xff7f },
+	{ 0xff80, 0xff80 },
+	{ 0xff81, 0xff81 },
+	{ 0xff82, 0xff82 },
+	{ 0xff83, 0xff83 },
+	{ 0xff84, 0xff84 },
+	{ 0xff85, 0xff85 },
+	{ 0xff86, 0xff86 },
+	{ 0xff87, 0xff87 },
+	{ 0xff88, 0xff88 },
+	{ 0xff89, 0xff89 },
+	{ 0xff8a, 0xff8a },
+	{ 0xff8b, 0xff8b },
+	{ 0xff8c, 0xff8c },
+	{ 0xff8d, 0xff8d },
+	{ 0xff8e, 0xff8e },
+	{ 0xff8f, 0xff8f },
+	{ 0xff90, 0xff90 },
+	{ 0xff91, 0xff91 },
+	{ 0xff92, 0xff92 },
+	{ 0xff93, 0xff93 },
+	{ 0xff94, 0xff94 },
+	{ 0xff95, 0xff95 },
+	{ 0xff96, 0xff96 },
+	{ 0xff97, 0xff97 },
+	{ 0xff98, 0xff98 },
+	{ 0xff99, 0xff99 },
+	{ 0xff9a, 0xff9a },
+	{ 0xff9b, 0xff9b },
+	{ 0xff9c, 0xff9c },
+	{ 0xff9d, 0xff9d },
+	{ 0xff9e, 0xff9e },
+	{ 0xff9f, 0xff9f },
+	{ 0xffa0, 0xffa0 },
+	{ 0xffa1, 0xffa1 },
+	{ 0xffa2, 0xffa2 },
+	{ 0xffa3, 0xffa3 },
+	{ 0xffa4, 0xffa4 },
+	{ 0xffa5, 0xffa5 },
+	{ 0xffa6, 0xffa6 },
+	{ 0xffa7, 0xffa7 },
+	{ 0xffa8, 0xffa8 },
+	{ 0xffa9, 0xffa9 },
+	{ 0xffaa, 0xffaa },
+	{ 0xffab, 0xffab },
+	{ 0xffac, 0xffac },
+	{ 0xffad, 0xffad },
+	{ 0xffae, 0xffae },
+	{ 0xffaf, 0xffaf },
+	{ 0xffb0, 0xffb0 },
+	{ 0xffb1, 0xffb1 },
+	{ 0xffb2, 0xffb2 },
+	{ 0xffb3, 0xffb3 },
+	{ 0xffb4, 0xffb4 },
+	{ 0xffb5, 0xffb5 },
+	{ 0xffb6, 0xffb6 },
+	{ 0xffb7, 0xffb7 },
+	{ 0xffb8, 0xffb8 },
+	{ 0xffb9, 0xffb9 },
+	{ 0xffba, 0xffba },
+	{ 0xffbb, 0xffbb },
+	{ 0xffbc, 0xffbc },
+	{ 0xffbd, 0xffbd },
+	{ 0xffbe, 0xffbe },
+	{ 0xffbf, 0xffbf },
+	{ 0xffc0, 0xffc0 },
+	{ 0xffc1, 0xffc1 },
+	{ 0xffc2, 0xffc2 },
+	{ 0xffc3, 0xffc3 },
+	{ 0xffc4, 0xffc4 },
+	{ 0xffc5, 0xffc5 },
+	{ 0xffc6, 0xffc6 },
+	{ 0xffc7, 0xffc7 },
+	{ 0xffc8, 0xffc8 },
+	{ 0xffc9, 0xffc9 },
+	{ 0xffca, 0xffca },
+	{ 0xffcb, 0xffcb },
+	{ 0xffcc, 0xffcc },
+	{ 0xffcd, 0xffcd },
+	{ 0xffce, 0xffce },
+	{ 0xffcf, 0xffcf },
+	{ 0xffd0, 0xffd0 },
+	{ 0xffd1, 0xffd1 },
+	{ 0xffd2, 0xffd2 },
+	{ 0xffd3, 0xffd3 },
+	{ 0xffd4, 0xffd4 },
+	{ 0xffd5, 0xffd5 },
+	{ 0xffd6, 0xffd6 },
+	{ 0xffd7, 0xffd7 },
+	{ 0xffd8, 0xffd8 },
+	{ 0xffd9, 0xffd9 },
+	{ 0xffda, 0xffda },
+	{ 0xffdb, 0xffdb },
+	{ 0xffdc, 0xffdc },
+	{ 0xffdd, 0xffdd },
+	{ 0xffde, 0xffde },
+	{ 0xffdf, 0xffdf },
+	{ 0xffe0, 0xffe0 },
+	{ 0xffe1, 0xffe1 },
+	{ 0xffe2, 0xffe2 },
+	{ 0xffe3, 0xffe3 },
+	{ 0xffe4, 0xffe4 },
+	{ 0xffe5, 0xffe5 },
+	{ 0xffe6, 0xffe6 },
+	{ 0xffe7, 0xffe7 },
+	{ 0xffe8, 0xffe8 },
+	{ 0xffe9, 0xffe9 },
+	{ 0xffea, 0xffea },
+	{ 0xffeb, 0xffeb },
+	{ 0xffec, 0xffec },
+	{ 0xffed, 0xffed },
+	{ 0xffee, 0xffee },
+	{ 0xffef, 0xffef },
+	{ 0xfff0, 0xfff0 },
+	{ 0xfff1, 0xfff1 },
+	{ 0xfff2, 0xfff2 },
+	{ 0xfff3, 0xfff3 },
+	{ 0xfff4, 0xfff4 },
+	{ 0xfff5, 0xfff5 },
+	{ 0xfff6, 0xfff6 },
+	{ 0xfff7, 0xfff7 },
+	{ 0xfff8, 0xfff8 },
+	{ 0xfff9, 0xfff9 },
+	{ 0xfffa, 0xfffa },
+	{ 0xfffb, 0xfffb },
+	{ 0xfffc, 0xfffc },
+	{ 0xfffd, 0xfffd },
+	{ 0xfffe, 0xfffe },
+	{ 0xffff, 0xffff },
+};
+
+gli_case_block_t unigen_case_block_0x104[256] = {
+	{ 0x10400, 0x10428 },  /* upper */
+	{ 0x10401, 0x10429 },  /* upper */
+	{ 0x10402, 0x1042a },  /* upper */
+	{ 0x10403, 0x1042b },  /* upper */
+	{ 0x10404, 0x1042c },  /* upper */
+	{ 0x10405, 0x1042d },  /* upper */
+	{ 0x10406, 0x1042e },  /* upper */
+	{ 0x10407, 0x1042f },  /* upper */
+	{ 0x10408, 0x10430 },  /* upper */
+	{ 0x10409, 0x10431 },  /* upper */
+	{ 0x1040a, 0x10432 },  /* upper */
+	{ 0x1040b, 0x10433 },  /* upper */
+	{ 0x1040c, 0x10434 },  /* upper */
+	{ 0x1040d, 0x10435 },  /* upper */
+	{ 0x1040e, 0x10436 },  /* upper */
+	{ 0x1040f, 0x10437 },  /* upper */
+	{ 0x10410, 0x10438 },  /* upper */
+	{ 0x10411, 0x10439 },  /* upper */
+	{ 0x10412, 0x1043a },  /* upper */
+	{ 0x10413, 0x1043b },  /* upper */
+	{ 0x10414, 0x1043c },  /* upper */
+	{ 0x10415, 0x1043d },  /* upper */
+	{ 0x10416, 0x1043e },  /* upper */
+	{ 0x10417, 0x1043f },  /* upper */
+	{ 0x10418, 0x10440 },  /* upper */
+	{ 0x10419, 0x10441 },  /* upper */
+	{ 0x1041a, 0x10442 },  /* upper */
+	{ 0x1041b, 0x10443 },  /* upper */
+	{ 0x1041c, 0x10444 },  /* upper */
+	{ 0x1041d, 0x10445 },  /* upper */
+	{ 0x1041e, 0x10446 },  /* upper */
+	{ 0x1041f, 0x10447 },  /* upper */
+	{ 0x10420, 0x10448 },  /* upper */
+	{ 0x10421, 0x10449 },  /* upper */
+	{ 0x10422, 0x1044a },  /* upper */
+	{ 0x10423, 0x1044b },  /* upper */
+	{ 0x10424, 0x1044c },  /* upper */
+	{ 0x10425, 0x1044d },  /* upper */
+	{ 0x10426, 0x1044e },  /* upper */
+	{ 0x10427, 0x1044f },  /* upper */
+	{ 0x10400, 0x10428 },  /* lower */
+	{ 0x10401, 0x10429 },  /* lower */
+	{ 0x10402, 0x1042a },  /* lower */
+	{ 0x10403, 0x1042b },  /* lower */
+	{ 0x10404, 0x1042c },  /* lower */
+	{ 0x10405, 0x1042d },  /* lower */
+	{ 0x10406, 0x1042e },  /* lower */
+	{ 0x10407, 0x1042f },  /* lower */
+	{ 0x10408, 0x10430 },  /* lower */
+	{ 0x10409, 0x10431 },  /* lower */
+	{ 0x1040a, 0x10432 },  /* lower */
+	{ 0x1040b, 0x10433 },  /* lower */
+	{ 0x1040c, 0x10434 },  /* lower */
+	{ 0x1040d, 0x10435 },  /* lower */
+	{ 0x1040e, 0x10436 },  /* lower */
+	{ 0x1040f, 0x10437 },  /* lower */
+	{ 0x10410, 0x10438 },  /* lower */
+	{ 0x10411, 0x10439 },  /* lower */
+	{ 0x10412, 0x1043a },  /* lower */
+	{ 0x10413, 0x1043b },  /* lower */
+	{ 0x10414, 0x1043c },  /* lower */
+	{ 0x10415, 0x1043d },  /* lower */
+	{ 0x10416, 0x1043e },  /* lower */
+	{ 0x10417, 0x1043f },  /* lower */
+	{ 0x10418, 0x10440 },  /* lower */
+	{ 0x10419, 0x10441 },  /* lower */
+	{ 0x1041a, 0x10442 },  /* lower */
+	{ 0x1041b, 0x10443 },  /* lower */
+	{ 0x1041c, 0x10444 },  /* lower */
+	{ 0x1041d, 0x10445 },  /* lower */
+	{ 0x1041e, 0x10446 },  /* lower */
+	{ 0x1041f, 0x10447 },  /* lower */
+	{ 0x10420, 0x10448 },  /* lower */
+	{ 0x10421, 0x10449 },  /* lower */
+	{ 0x10422, 0x1044a },  /* lower */
+	{ 0x10423, 0x1044b },  /* lower */
+	{ 0x10424, 0x1044c },  /* lower */
+	{ 0x10425, 0x1044d },  /* lower */
+	{ 0x10426, 0x1044e },  /* lower */
+	{ 0x10427, 0x1044f },  /* lower */
+	{ 0x10450, 0x10450 },
+	{ 0x10451, 0x10451 },
+	{ 0x10452, 0x10452 },
+	{ 0x10453, 0x10453 },
+	{ 0x10454, 0x10454 },
+	{ 0x10455, 0x10455 },
+	{ 0x10456, 0x10456 },
+	{ 0x10457, 0x10457 },
+	{ 0x10458, 0x10458 },
+	{ 0x10459, 0x10459 },
+	{ 0x1045a, 0x1045a },
+	{ 0x1045b, 0x1045b },
+	{ 0x1045c, 0x1045c },
+	{ 0x1045d, 0x1045d },
+	{ 0x1045e, 0x1045e },
+	{ 0x1045f, 0x1045f },
+	{ 0x10460, 0x10460 },
+	{ 0x10461, 0x10461 },
+	{ 0x10462, 0x10462 },
+	{ 0x10463, 0x10463 },
+	{ 0x10464, 0x10464 },
+	{ 0x10465, 0x10465 },
+	{ 0x10466, 0x10466 },
+	{ 0x10467, 0x10467 },
+	{ 0x10468, 0x10468 },
+	{ 0x10469, 0x10469 },
+	{ 0x1046a, 0x1046a },
+	{ 0x1046b, 0x1046b },
+	{ 0x1046c, 0x1046c },
+	{ 0x1046d, 0x1046d },
+	{ 0x1046e, 0x1046e },
+	{ 0x1046f, 0x1046f },
+	{ 0x10470, 0x10470 },
+	{ 0x10471, 0x10471 },
+	{ 0x10472, 0x10472 },
+	{ 0x10473, 0x10473 },
+	{ 0x10474, 0x10474 },
+	{ 0x10475, 0x10475 },
+	{ 0x10476, 0x10476 },
+	{ 0x10477, 0x10477 },
+	{ 0x10478, 0x10478 },
+	{ 0x10479, 0x10479 },
+	{ 0x1047a, 0x1047a },
+	{ 0x1047b, 0x1047b },
+	{ 0x1047c, 0x1047c },
+	{ 0x1047d, 0x1047d },
+	{ 0x1047e, 0x1047e },
+	{ 0x1047f, 0x1047f },
+	{ 0x10480, 0x10480 },
+	{ 0x10481, 0x10481 },
+	{ 0x10482, 0x10482 },
+	{ 0x10483, 0x10483 },
+	{ 0x10484, 0x10484 },
+	{ 0x10485, 0x10485 },
+	{ 0x10486, 0x10486 },
+	{ 0x10487, 0x10487 },
+	{ 0x10488, 0x10488 },
+	{ 0x10489, 0x10489 },
+	{ 0x1048a, 0x1048a },
+	{ 0x1048b, 0x1048b },
+	{ 0x1048c, 0x1048c },
+	{ 0x1048d, 0x1048d },
+	{ 0x1048e, 0x1048e },
+	{ 0x1048f, 0x1048f },
+	{ 0x10490, 0x10490 },
+	{ 0x10491, 0x10491 },
+	{ 0x10492, 0x10492 },
+	{ 0x10493, 0x10493 },
+	{ 0x10494, 0x10494 },
+	{ 0x10495, 0x10495 },
+	{ 0x10496, 0x10496 },
+	{ 0x10497, 0x10497 },
+	{ 0x10498, 0x10498 },
+	{ 0x10499, 0x10499 },
+	{ 0x1049a, 0x1049a },
+	{ 0x1049b, 0x1049b },
+	{ 0x1049c, 0x1049c },
+	{ 0x1049d, 0x1049d },
+	{ 0x1049e, 0x1049e },
+	{ 0x1049f, 0x1049f },
+	{ 0x104a0, 0x104a0 },
+	{ 0x104a1, 0x104a1 },
+	{ 0x104a2, 0x104a2 },
+	{ 0x104a3, 0x104a3 },
+	{ 0x104a4, 0x104a4 },
+	{ 0x104a5, 0x104a5 },
+	{ 0x104a6, 0x104a6 },
+	{ 0x104a7, 0x104a7 },
+	{ 0x104a8, 0x104a8 },
+	{ 0x104a9, 0x104a9 },
+	{ 0x104aa, 0x104aa },
+	{ 0x104ab, 0x104ab },
+	{ 0x104ac, 0x104ac },
+	{ 0x104ad, 0x104ad },
+	{ 0x104ae, 0x104ae },
+	{ 0x104af, 0x104af },
+	{ 0x104b0, 0x104b0 },
+	{ 0x104b1, 0x104b1 },
+	{ 0x104b2, 0x104b2 },
+	{ 0x104b3, 0x104b3 },
+	{ 0x104b4, 0x104b4 },
+	{ 0x104b5, 0x104b5 },
+	{ 0x104b6, 0x104b6 },
+	{ 0x104b7, 0x104b7 },
+	{ 0x104b8, 0x104b8 },
+	{ 0x104b9, 0x104b9 },
+	{ 0x104ba, 0x104ba },
+	{ 0x104bb, 0x104bb },
+	{ 0x104bc, 0x104bc },
+	{ 0x104bd, 0x104bd },
+	{ 0x104be, 0x104be },
+	{ 0x104bf, 0x104bf },
+	{ 0x104c0, 0x104c0 },
+	{ 0x104c1, 0x104c1 },
+	{ 0x104c2, 0x104c2 },
+	{ 0x104c3, 0x104c3 },
+	{ 0x104c4, 0x104c4 },
+	{ 0x104c5, 0x104c5 },
+	{ 0x104c6, 0x104c6 },
+	{ 0x104c7, 0x104c7 },
+	{ 0x104c8, 0x104c8 },
+	{ 0x104c9, 0x104c9 },
+	{ 0x104ca, 0x104ca },
+	{ 0x104cb, 0x104cb },
+	{ 0x104cc, 0x104cc },
+	{ 0x104cd, 0x104cd },
+	{ 0x104ce, 0x104ce },
+	{ 0x104cf, 0x104cf },
+	{ 0x104d0, 0x104d0 },
+	{ 0x104d1, 0x104d1 },
+	{ 0x104d2, 0x104d2 },
+	{ 0x104d3, 0x104d3 },
+	{ 0x104d4, 0x104d4 },
+	{ 0x104d5, 0x104d5 },
+	{ 0x104d6, 0x104d6 },
+	{ 0x104d7, 0x104d7 },
+	{ 0x104d8, 0x104d8 },
+	{ 0x104d9, 0x104d9 },
+	{ 0x104da, 0x104da },
+	{ 0x104db, 0x104db },
+	{ 0x104dc, 0x104dc },
+	{ 0x104dd, 0x104dd },
+	{ 0x104de, 0x104de },
+	{ 0x104df, 0x104df },
+	{ 0x104e0, 0x104e0 },
+	{ 0x104e1, 0x104e1 },
+	{ 0x104e2, 0x104e2 },
+	{ 0x104e3, 0x104e3 },
+	{ 0x104e4, 0x104e4 },
+	{ 0x104e5, 0x104e5 },
+	{ 0x104e6, 0x104e6 },
+	{ 0x104e7, 0x104e7 },
+	{ 0x104e8, 0x104e8 },
+	{ 0x104e9, 0x104e9 },
+	{ 0x104ea, 0x104ea },
+	{ 0x104eb, 0x104eb },
+	{ 0x104ec, 0x104ec },
+	{ 0x104ed, 0x104ed },
+	{ 0x104ee, 0x104ee },
+	{ 0x104ef, 0x104ef },
+	{ 0x104f0, 0x104f0 },
+	{ 0x104f1, 0x104f1 },
+	{ 0x104f2, 0x104f2 },
+	{ 0x104f3, 0x104f3 },
+	{ 0x104f4, 0x104f4 },
+	{ 0x104f5, 0x104f5 },
+	{ 0x104f6, 0x104f6 },
+	{ 0x104f7, 0x104f7 },
+	{ 0x104f8, 0x104f8 },
+	{ 0x104f9, 0x104f9 },
+	{ 0x104fa, 0x104fa },
+	{ 0x104fb, 0x104fb },
+	{ 0x104fc, 0x104fc },
+	{ 0x104fd, 0x104fd },
+	{ 0x104fe, 0x104fe },
+	{ 0x104ff, 0x104ff },
+};
+
+glui32 unigen_special_array[] = {
+	2, 0x53, 0x53,  /* 0xdf upcase */
+	1, 0xdf,  /* 0xdf downcase */
+	2, 0x53, 0x73,  /* 0xdf titlecase */
+	1, 0x130,  /* 0x130 upcase */
+	2, 0x69, 0x307,  /* 0x130 downcase */
+	1, 0x130,  /* 0x130 titlecase */
+	2, 0x2bc, 0x4e,  /* 0x149 upcase */
+	1, 0x149,  /* 0x149 downcase */
+	2, 0x2bc, 0x4e,  /* 0x149 titlecase */
+	1, 0x1c4,  /* 0x1c4 upcase */
+	1, 0x1c6,  /* 0x1c4 downcase */
+	1, 0x1c5,  /* 0x1c4 titlecase */
+	1, 0x1c4,  /* 0x1c5 upcase */
+	1, 0x1c6,  /* 0x1c5 downcase */
+	1, 0x1c5,  /* 0x1c5 titlecase */
+	1, 0x1c4,  /* 0x1c6 upcase */
+	1, 0x1c6,  /* 0x1c6 downcase */
+	1, 0x1c5,  /* 0x1c6 titlecase */
+	1, 0x1c7,  /* 0x1c7 upcase */
+	1, 0x1c9,  /* 0x1c7 downcase */
+	1, 0x1c8,  /* 0x1c7 titlecase */
+	1, 0x1c7,  /* 0x1c8 upcase */
+	1, 0x1c9,  /* 0x1c8 downcase */
+	1, 0x1c8,  /* 0x1c8 titlecase */
+	1, 0x1c7,  /* 0x1c9 upcase */
+	1, 0x1c9,  /* 0x1c9 downcase */
+	1, 0x1c8,  /* 0x1c9 titlecase */
+	1, 0x1ca,  /* 0x1ca upcase */
+	1, 0x1cc,  /* 0x1ca downcase */
+	1, 0x1cb,  /* 0x1ca titlecase */
+	1, 0x1ca,  /* 0x1cb upcase */
+	1, 0x1cc,  /* 0x1cb downcase */
+	1, 0x1cb,  /* 0x1cb titlecase */
+	1, 0x1ca,  /* 0x1cc upcase */
+	1, 0x1cc,  /* 0x1cc downcase */
+	1, 0x1cb,  /* 0x1cc titlecase */
+	2, 0x4a, 0x30c,  /* 0x1f0 upcase */
+	1, 0x1f0,  /* 0x1f0 downcase */
+	2, 0x4a, 0x30c,  /* 0x1f0 titlecase */
+	1, 0x1f1,  /* 0x1f1 upcase */
+	1, 0x1f3,  /* 0x1f1 downcase */
+	1, 0x1f2,  /* 0x1f1 titlecase */
+	1, 0x1f1,  /* 0x1f2 upcase */
+	1, 0x1f3,  /* 0x1f2 downcase */
+	1, 0x1f2,  /* 0x1f2 titlecase */
+	1, 0x1f1,  /* 0x1f3 upcase */
+	1, 0x1f3,  /* 0x1f3 downcase */
+	1, 0x1f2,  /* 0x1f3 titlecase */
+	3, 0x399, 0x308, 0x301,  /* 0x390 upcase */
+	1, 0x390,  /* 0x390 downcase */
+	3, 0x399, 0x308, 0x301,  /* 0x390 titlecase */
+	3, 0x3a5, 0x308, 0x301,  /* 0x3b0 upcase */
+	1, 0x3b0,  /* 0x3b0 downcase */
+	3, 0x3a5, 0x308, 0x301,  /* 0x3b0 titlecase */
+	2, 0x535, 0x552,  /* 0x587 upcase */
+	1, 0x587,  /* 0x587 downcase */
+	2, 0x535, 0x582,  /* 0x587 titlecase */
+	2, 0x48, 0x331,  /* 0x1e96 upcase */
+	1, 0x1e96,  /* 0x1e96 downcase */
+	2, 0x48, 0x331,  /* 0x1e96 titlecase */
+	2, 0x54, 0x308,  /* 0x1e97 upcase */
+	1, 0x1e97,  /* 0x1e97 downcase */
+	2, 0x54, 0x308,  /* 0x1e97 titlecase */
+	2, 0x57, 0x30a,  /* 0x1e98 upcase */
+	1, 0x1e98,  /* 0x1e98 downcase */
+	2, 0x57, 0x30a,  /* 0x1e98 titlecase */
+	2, 0x59, 0x30a,  /* 0x1e99 upcase */
+	1, 0x1e99,  /* 0x1e99 downcase */
+	2, 0x59, 0x30a,  /* 0x1e99 titlecase */
+	2, 0x41, 0x2be,  /* 0x1e9a upcase */
+	1, 0x1e9a,  /* 0x1e9a downcase */
+	2, 0x41, 0x2be,  /* 0x1e9a titlecase */
+	2, 0x3a5, 0x313,  /* 0x1f50 upcase */
+	1, 0x1f50,  /* 0x1f50 downcase */
+	2, 0x3a5, 0x313,  /* 0x1f50 titlecase */
+	3, 0x3a5, 0x313, 0x300,  /* 0x1f52 upcase */
+	1, 0x1f52,  /* 0x1f52 downcase */
+	3, 0x3a5, 0x313, 0x300,  /* 0x1f52 titlecase */
+	3, 0x3a5, 0x313, 0x301,  /* 0x1f54 upcase */
+	1, 0x1f54,  /* 0x1f54 downcase */
+	3, 0x3a5, 0x313, 0x301,  /* 0x1f54 titlecase */
+	3, 0x3a5, 0x313, 0x342,  /* 0x1f56 upcase */
+	1, 0x1f56,  /* 0x1f56 downcase */
+	3, 0x3a5, 0x313, 0x342,  /* 0x1f56 titlecase */
+	2, 0x1f08, 0x399,  /* 0x1f80 upcase */
+	1, 0x1f80,  /* 0x1f80 downcase */
+	1, 0x1f88,  /* 0x1f80 titlecase */
+	2, 0x1f09, 0x399,  /* 0x1f81 upcase */
+	1, 0x1f81,  /* 0x1f81 downcase */
+	1, 0x1f89,  /* 0x1f81 titlecase */
+	2, 0x1f0a, 0x399,  /* 0x1f82 upcase */
+	1, 0x1f82,  /* 0x1f82 downcase */
+	1, 0x1f8a,  /* 0x1f82 titlecase */
+	2, 0x1f0b, 0x399,  /* 0x1f83 upcase */
+	1, 0x1f83,  /* 0x1f83 downcase */
+	1, 0x1f8b,  /* 0x1f83 titlecase */
+	2, 0x1f0c, 0x399,  /* 0x1f84 upcase */
+	1, 0x1f84,  /* 0x1f84 downcase */
+	1, 0x1f8c,  /* 0x1f84 titlecase */
+	2, 0x1f0d, 0x399,  /* 0x1f85 upcase */
+	1, 0x1f85,  /* 0x1f85 downcase */
+	1, 0x1f8d,  /* 0x1f85 titlecase */
+	2, 0x1f0e, 0x399,  /* 0x1f86 upcase */
+	1, 0x1f86,  /* 0x1f86 downcase */
+	1, 0x1f8e,  /* 0x1f86 titlecase */
+	2, 0x1f0f, 0x399,  /* 0x1f87 upcase */
+	1, 0x1f87,  /* 0x1f87 downcase */
+	1, 0x1f8f,  /* 0x1f87 titlecase */
+	2, 0x1f08, 0x399,  /* 0x1f88 upcase */
+	1, 0x1f80,  /* 0x1f88 downcase */
+	1, 0x1f88,  /* 0x1f88 titlecase */
+	2, 0x1f09, 0x399,  /* 0x1f89 upcase */
+	1, 0x1f81,  /* 0x1f89 downcase */
+	1, 0x1f89,  /* 0x1f89 titlecase */
+	2, 0x1f0a, 0x399,  /* 0x1f8a upcase */
+	1, 0x1f82,  /* 0x1f8a downcase */
+	1, 0x1f8a,  /* 0x1f8a titlecase */
+	2, 0x1f0b, 0x399,  /* 0x1f8b upcase */
+	1, 0x1f83,  /* 0x1f8b downcase */
+	1, 0x1f8b,  /* 0x1f8b titlecase */
+	2, 0x1f0c, 0x399,  /* 0x1f8c upcase */
+	1, 0x1f84,  /* 0x1f8c downcase */
+	1, 0x1f8c,  /* 0x1f8c titlecase */
+	2, 0x1f0d, 0x399,  /* 0x1f8d upcase */
+	1, 0x1f85,  /* 0x1f8d downcase */
+	1, 0x1f8d,  /* 0x1f8d titlecase */
+	2, 0x1f0e, 0x399,  /* 0x1f8e upcase */
+	1, 0x1f86,  /* 0x1f8e downcase */
+	1, 0x1f8e,  /* 0x1f8e titlecase */
+	2, 0x1f0f, 0x399,  /* 0x1f8f upcase */
+	1, 0x1f87,  /* 0x1f8f downcase */
+	1, 0x1f8f,  /* 0x1f8f titlecase */
+	2, 0x1f28, 0x399,  /* 0x1f90 upcase */
+	1, 0x1f90,  /* 0x1f90 downcase */
+	1, 0x1f98,  /* 0x1f90 titlecase */
+	2, 0x1f29, 0x399,  /* 0x1f91 upcase */
+	1, 0x1f91,  /* 0x1f91 downcase */
+	1, 0x1f99,  /* 0x1f91 titlecase */
+	2, 0x1f2a, 0x399,  /* 0x1f92 upcase */
+	1, 0x1f92,  /* 0x1f92 downcase */
+	1, 0x1f9a,  /* 0x1f92 titlecase */
+	2, 0x1f2b, 0x399,  /* 0x1f93 upcase */
+	1, 0x1f93,  /* 0x1f93 downcase */
+	1, 0x1f9b,  /* 0x1f93 titlecase */
+	2, 0x1f2c, 0x399,  /* 0x1f94 upcase */
+	1, 0x1f94,  /* 0x1f94 downcase */
+	1, 0x1f9c,  /* 0x1f94 titlecase */
+	2, 0x1f2d, 0x399,  /* 0x1f95 upcase */
+	1, 0x1f95,  /* 0x1f95 downcase */
+	1, 0x1f9d,  /* 0x1f95 titlecase */
+	2, 0x1f2e, 0x399,  /* 0x1f96 upcase */
+	1, 0x1f96,  /* 0x1f96 downcase */
+	1, 0x1f9e,  /* 0x1f96 titlecase */
+	2, 0x1f2f, 0x399,  /* 0x1f97 upcase */
+	1, 0x1f97,  /* 0x1f97 downcase */
+	1, 0x1f9f,  /* 0x1f97 titlecase */
+	2, 0x1f28, 0x399,  /* 0x1f98 upcase */
+	1, 0x1f90,  /* 0x1f98 downcase */
+	1, 0x1f98,  /* 0x1f98 titlecase */
+	2, 0x1f29, 0x399,  /* 0x1f99 upcase */
+	1, 0x1f91,  /* 0x1f99 downcase */
+	1, 0x1f99,  /* 0x1f99 titlecase */
+	2, 0x1f2a, 0x399,  /* 0x1f9a upcase */
+	1, 0x1f92,  /* 0x1f9a downcase */
+	1, 0x1f9a,  /* 0x1f9a titlecase */
+	2, 0x1f2b, 0x399,  /* 0x1f9b upcase */
+	1, 0x1f93,  /* 0x1f9b downcase */
+	1, 0x1f9b,  /* 0x1f9b titlecase */
+	2, 0x1f2c, 0x399,  /* 0x1f9c upcase */
+	1, 0x1f94,  /* 0x1f9c downcase */
+	1, 0x1f9c,  /* 0x1f9c titlecase */
+	2, 0x1f2d, 0x399,  /* 0x1f9d upcase */
+	1, 0x1f95,  /* 0x1f9d downcase */
+	1, 0x1f9d,  /* 0x1f9d titlecase */
+	2, 0x1f2e, 0x399,  /* 0x1f9e upcase */
+	1, 0x1f96,  /* 0x1f9e downcase */
+	1, 0x1f9e,  /* 0x1f9e titlecase */
+	2, 0x1f2f, 0x399,  /* 0x1f9f upcase */
+	1, 0x1f97,  /* 0x1f9f downcase */
+	1, 0x1f9f,  /* 0x1f9f titlecase */
+	2, 0x1f68, 0x399,  /* 0x1fa0 upcase */
+	1, 0x1fa0,  /* 0x1fa0 downcase */
+	1, 0x1fa8,  /* 0x1fa0 titlecase */
+	2, 0x1f69, 0x399,  /* 0x1fa1 upcase */
+	1, 0x1fa1,  /* 0x1fa1 downcase */
+	1, 0x1fa9,  /* 0x1fa1 titlecase */
+	2, 0x1f6a, 0x399,  /* 0x1fa2 upcase */
+	1, 0x1fa2,  /* 0x1fa2 downcase */
+	1, 0x1faa,  /* 0x1fa2 titlecase */
+	2, 0x1f6b, 0x399,  /* 0x1fa3 upcase */
+	1, 0x1fa3,  /* 0x1fa3 downcase */
+	1, 0x1fab,  /* 0x1fa3 titlecase */
+	2, 0x1f6c, 0x399,  /* 0x1fa4 upcase */
+	1, 0x1fa4,  /* 0x1fa4 downcase */
+	1, 0x1fac,  /* 0x1fa4 titlecase */
+	2, 0x1f6d, 0x399,  /* 0x1fa5 upcase */
+	1, 0x1fa5,  /* 0x1fa5 downcase */
+	1, 0x1fad,  /* 0x1fa5 titlecase */
+	2, 0x1f6e, 0x399,  /* 0x1fa6 upcase */
+	1, 0x1fa6,  /* 0x1fa6 downcase */
+	1, 0x1fae,  /* 0x1fa6 titlecase */
+	2, 0x1f6f, 0x399,  /* 0x1fa7 upcase */
+	1, 0x1fa7,  /* 0x1fa7 downcase */
+	1, 0x1faf,  /* 0x1fa7 titlecase */
+	2, 0x1f68, 0x399,  /* 0x1fa8 upcase */
+	1, 0x1fa0,  /* 0x1fa8 downcase */
+	1, 0x1fa8,  /* 0x1fa8 titlecase */
+	2, 0x1f69, 0x399,  /* 0x1fa9 upcase */
+	1, 0x1fa1,  /* 0x1fa9 downcase */
+	1, 0x1fa9,  /* 0x1fa9 titlecase */
+	2, 0x1f6a, 0x399,  /* 0x1faa upcase */
+	1, 0x1fa2,  /* 0x1faa downcase */
+	1, 0x1faa,  /* 0x1faa titlecase */
+	2, 0x1f6b, 0x399,  /* 0x1fab upcase */
+	1, 0x1fa3,  /* 0x1fab downcase */
+	1, 0x1fab,  /* 0x1fab titlecase */
+	2, 0x1f6c, 0x399,  /* 0x1fac upcase */
+	1, 0x1fa4,  /* 0x1fac downcase */
+	1, 0x1fac,  /* 0x1fac titlecase */
+	2, 0x1f6d, 0x399,  /* 0x1fad upcase */
+	1, 0x1fa5,  /* 0x1fad downcase */
+	1, 0x1fad,  /* 0x1fad titlecase */
+	2, 0x1f6e, 0x399,  /* 0x1fae upcase */
+	1, 0x1fa6,  /* 0x1fae downcase */
+	1, 0x1fae,  /* 0x1fae titlecase */
+	2, 0x1f6f, 0x399,  /* 0x1faf upcase */
+	1, 0x1fa7,  /* 0x1faf downcase */
+	1, 0x1faf,  /* 0x1faf titlecase */
+	2, 0x1fba, 0x399,  /* 0x1fb2 upcase */
+	1, 0x1fb2,  /* 0x1fb2 downcase */
+	2, 0x1fba, 0x345,  /* 0x1fb2 titlecase */
+	2, 0x391, 0x399,  /* 0x1fb3 upcase */
+	1, 0x1fb3,  /* 0x1fb3 downcase */
+	1, 0x1fbc,  /* 0x1fb3 titlecase */
+	2, 0x386, 0x399,  /* 0x1fb4 upcase */
+	1, 0x1fb4,  /* 0x1fb4 downcase */
+	2, 0x386, 0x345,  /* 0x1fb4 titlecase */
+	2, 0x391, 0x342,  /* 0x1fb6 upcase */
+	1, 0x1fb6,  /* 0x1fb6 downcase */
+	2, 0x391, 0x342,  /* 0x1fb6 titlecase */
+	3, 0x391, 0x342, 0x399,  /* 0x1fb7 upcase */
+	1, 0x1fb7,  /* 0x1fb7 downcase */
+	3, 0x391, 0x342, 0x345,  /* 0x1fb7 titlecase */
+	2, 0x391, 0x399,  /* 0x1fbc upcase */
+	1, 0x1fb3,  /* 0x1fbc downcase */
+	1, 0x1fbc,  /* 0x1fbc titlecase */
+	2, 0x1fca, 0x399,  /* 0x1fc2 upcase */
+	1, 0x1fc2,  /* 0x1fc2 downcase */
+	2, 0x1fca, 0x345,  /* 0x1fc2 titlecase */
+	2, 0x397, 0x399,  /* 0x1fc3 upcase */
+	1, 0x1fc3,  /* 0x1fc3 downcase */
+	1, 0x1fcc,  /* 0x1fc3 titlecase */
+	2, 0x389, 0x399,  /* 0x1fc4 upcase */
+	1, 0x1fc4,  /* 0x1fc4 downcase */
+	2, 0x389, 0x345,  /* 0x1fc4 titlecase */
+	2, 0x397, 0x342,  /* 0x1fc6 upcase */
+	1, 0x1fc6,  /* 0x1fc6 downcase */
+	2, 0x397, 0x342,  /* 0x1fc6 titlecase */
+	3, 0x397, 0x342, 0x399,  /* 0x1fc7 upcase */
+	1, 0x1fc7,  /* 0x1fc7 downcase */
+	3, 0x397, 0x342, 0x345,  /* 0x1fc7 titlecase */
+	2, 0x397, 0x399,  /* 0x1fcc upcase */
+	1, 0x1fc3,  /* 0x1fcc downcase */
+	1, 0x1fcc,  /* 0x1fcc titlecase */
+	3, 0x399, 0x308, 0x300,  /* 0x1fd2 upcase */
+	1, 0x1fd2,  /* 0x1fd2 downcase */
+	3, 0x399, 0x308, 0x300,  /* 0x1fd2 titlecase */
+	3, 0x399, 0x308, 0x301,  /* 0x1fd3 upcase */
+	1, 0x1fd3,  /* 0x1fd3 downcase */
+	3, 0x399, 0x308, 0x301,  /* 0x1fd3 titlecase */
+	2, 0x399, 0x342,  /* 0x1fd6 upcase */
+	1, 0x1fd6,  /* 0x1fd6 downcase */
+	2, 0x399, 0x342,  /* 0x1fd6 titlecase */
+	3, 0x399, 0x308, 0x342,  /* 0x1fd7 upcase */
+	1, 0x1fd7,  /* 0x1fd7 downcase */
+	3, 0x399, 0x308, 0x342,  /* 0x1fd7 titlecase */
+	3, 0x3a5, 0x308, 0x300,  /* 0x1fe2 upcase */
+	1, 0x1fe2,  /* 0x1fe2 downcase */
+	3, 0x3a5, 0x308, 0x300,  /* 0x1fe2 titlecase */
+	3, 0x3a5, 0x308, 0x301,  /* 0x1fe3 upcase */
+	1, 0x1fe3,  /* 0x1fe3 downcase */
+	3, 0x3a5, 0x308, 0x301,  /* 0x1fe3 titlecase */
+	2, 0x3a1, 0x313,  /* 0x1fe4 upcase */
+	1, 0x1fe4,  /* 0x1fe4 downcase */
+	2, 0x3a1, 0x313,  /* 0x1fe4 titlecase */
+	2, 0x3a5, 0x342,  /* 0x1fe6 upcase */
+	1, 0x1fe6,  /* 0x1fe6 downcase */
+	2, 0x3a5, 0x342,  /* 0x1fe6 titlecase */
+	3, 0x3a5, 0x308, 0x342,  /* 0x1fe7 upcase */
+	1, 0x1fe7,  /* 0x1fe7 downcase */
+	3, 0x3a5, 0x308, 0x342,  /* 0x1fe7 titlecase */
+	2, 0x1ffa, 0x399,  /* 0x1ff2 upcase */
+	1, 0x1ff2,  /* 0x1ff2 downcase */
+	2, 0x1ffa, 0x345,  /* 0x1ff2 titlecase */
+	2, 0x3a9, 0x399,  /* 0x1ff3 upcase */
+	1, 0x1ff3,  /* 0x1ff3 downcase */
+	1, 0x1ffc,  /* 0x1ff3 titlecase */
+	2, 0x38f, 0x399,  /* 0x1ff4 upcase */
+	1, 0x1ff4,  /* 0x1ff4 downcase */
+	2, 0x38f, 0x345,  /* 0x1ff4 titlecase */
+	2, 0x3a9, 0x342,  /* 0x1ff6 upcase */
+	1, 0x1ff6,  /* 0x1ff6 downcase */
+	2, 0x3a9, 0x342,  /* 0x1ff6 titlecase */
+	3, 0x3a9, 0x342, 0x399,  /* 0x1ff7 upcase */
+	1, 0x1ff7,  /* 0x1ff7 downcase */
+	3, 0x3a9, 0x342, 0x345,  /* 0x1ff7 titlecase */
+	2, 0x3a9, 0x399,  /* 0x1ffc upcase */
+	1, 0x1ff3,  /* 0x1ffc downcase */
+	1, 0x1ffc,  /* 0x1ffc titlecase */
+	2, 0x46, 0x46,  /* 0xfb00 upcase */
+	1, 0xfb00,  /* 0xfb00 downcase */
+	2, 0x46, 0x66,  /* 0xfb00 titlecase */
+	2, 0x46, 0x49,  /* 0xfb01 upcase */
+	1, 0xfb01,  /* 0xfb01 downcase */
+	2, 0x46, 0x69,  /* 0xfb01 titlecase */
+	2, 0x46, 0x4c,  /* 0xfb02 upcase */
+	1, 0xfb02,  /* 0xfb02 downcase */
+	2, 0x46, 0x6c,  /* 0xfb02 titlecase */
+	3, 0x46, 0x46, 0x49,  /* 0xfb03 upcase */
+	1, 0xfb03,  /* 0xfb03 downcase */
+	3, 0x46, 0x66, 0x69,  /* 0xfb03 titlecase */
+	3, 0x46, 0x46, 0x4c,  /* 0xfb04 upcase */
+	1, 0xfb04,  /* 0xfb04 downcase */
+	3, 0x46, 0x66, 0x6c,  /* 0xfb04 titlecase */
+	2, 0x53, 0x54,  /* 0xfb05 upcase */
+	1, 0xfb05,  /* 0xfb05 downcase */
+	2, 0x53, 0x74,  /* 0xfb05 titlecase */
+	2, 0x53, 0x54,  /* 0xfb06 upcase */
+	1, 0xfb06,  /* 0xfb06 downcase */
+	2, 0x53, 0x74,  /* 0xfb06 titlecase */
+	2, 0x544, 0x546,  /* 0xfb13 upcase */
+	1, 0xfb13,  /* 0xfb13 downcase */
+	2, 0x544, 0x576,  /* 0xfb13 titlecase */
+	2, 0x544, 0x535,  /* 0xfb14 upcase */
+	1, 0xfb14,  /* 0xfb14 downcase */
+	2, 0x544, 0x565,  /* 0xfb14 titlecase */
+	2, 0x544, 0x53b,  /* 0xfb15 upcase */
+	1, 0xfb15,  /* 0xfb15 downcase */
+	2, 0x544, 0x56b,  /* 0xfb15 titlecase */
+	2, 0x54e, 0x546,  /* 0xfb16 upcase */
+	1, 0xfb16,  /* 0xfb16 downcase */
+	2, 0x54e, 0x576,  /* 0xfb16 titlecase */
+	2, 0x544, 0x53d,  /* 0xfb17 upcase */
+	1, 0xfb17,  /* 0xfb17 downcase */
+	2, 0x544, 0x56d,  /* 0xfb17 titlecase */
+};
+
+gli_case_special_t unigen_special_0xdf = { 0, 3, 5 };
+gli_case_special_t unigen_special_0x130 = { 8, 10, 13 };
+gli_case_special_t unigen_special_0x149 = { 15, 18, 20 };
+gli_case_special_t unigen_special_0x1c4 = { 23, 25, 27 };
+gli_case_special_t unigen_special_0x1c5 = { 29, 31, 33 };
+gli_case_special_t unigen_special_0x1c6 = { 35, 37, 39 };
+gli_case_special_t unigen_special_0x1c7 = { 41, 43, 45 };
+gli_case_special_t unigen_special_0x1c8 = { 47, 49, 51 };
+gli_case_special_t unigen_special_0x1c9 = { 53, 55, 57 };
+gli_case_special_t unigen_special_0x1ca = { 59, 61, 63 };
+gli_case_special_t unigen_special_0x1cb = { 65, 67, 69 };
+gli_case_special_t unigen_special_0x1cc = { 71, 73, 75 };
+gli_case_special_t unigen_special_0x1f0 = { 77, 80, 82 };
+gli_case_special_t unigen_special_0x1f1 = { 85, 87, 89 };
+gli_case_special_t unigen_special_0x1f2 = { 91, 93, 95 };
+gli_case_special_t unigen_special_0x1f3 = { 97, 99, 101 };
+gli_case_special_t unigen_special_0x390 = { 103, 107, 109 };
+gli_case_special_t unigen_special_0x3b0 = { 113, 117, 119 };
+gli_case_special_t unigen_special_0x587 = { 123, 126, 128 };
+gli_case_special_t unigen_special_0x1e96 = { 131, 134, 136 };
+gli_case_special_t unigen_special_0x1e97 = { 139, 142, 144 };
+gli_case_special_t unigen_special_0x1e98 = { 147, 150, 152 };
+gli_case_special_t unigen_special_0x1e99 = { 155, 158, 160 };
+gli_case_special_t unigen_special_0x1e9a = { 163, 166, 168 };
+gli_case_special_t unigen_special_0x1f50 = { 171, 174, 176 };
+gli_case_special_t unigen_special_0x1f52 = { 179, 183, 185 };
+gli_case_special_t unigen_special_0x1f54 = { 189, 193, 195 };
+gli_case_special_t unigen_special_0x1f56 = { 199, 203, 205 };
+gli_case_special_t unigen_special_0x1f80 = { 209, 212, 214 };
+gli_case_special_t unigen_special_0x1f81 = { 216, 219, 221 };
+gli_case_special_t unigen_special_0x1f82 = { 223, 226, 228 };
+gli_case_special_t unigen_special_0x1f83 = { 230, 233, 235 };
+gli_case_special_t unigen_special_0x1f84 = { 237, 240, 242 };
+gli_case_special_t unigen_special_0x1f85 = { 244, 247, 249 };
+gli_case_special_t unigen_special_0x1f86 = { 251, 254, 256 };
+gli_case_special_t unigen_special_0x1f87 = { 258, 261, 263 };
+gli_case_special_t unigen_special_0x1f88 = { 265, 268, 270 };
+gli_case_special_t unigen_special_0x1f89 = { 272, 275, 277 };
+gli_case_special_t unigen_special_0x1f8a = { 279, 282, 284 };
+gli_case_special_t unigen_special_0x1f8b = { 286, 289, 291 };
+gli_case_special_t unigen_special_0x1f8c = { 293, 296, 298 };
+gli_case_special_t unigen_special_0x1f8d = { 300, 303, 305 };
+gli_case_special_t unigen_special_0x1f8e = { 307, 310, 312 };
+gli_case_special_t unigen_special_0x1f8f = { 314, 317, 319 };
+gli_case_special_t unigen_special_0x1f90 = { 321, 324, 326 };
+gli_case_special_t unigen_special_0x1f91 = { 328, 331, 333 };
+gli_case_special_t unigen_special_0x1f92 = { 335, 338, 340 };
+gli_case_special_t unigen_special_0x1f93 = { 342, 345, 347 };
+gli_case_special_t unigen_special_0x1f94 = { 349, 352, 354 };
+gli_case_special_t unigen_special_0x1f95 = { 356, 359, 361 };
+gli_case_special_t unigen_special_0x1f96 = { 363, 366, 368 };
+gli_case_special_t unigen_special_0x1f97 = { 370, 373, 375 };
+gli_case_special_t unigen_special_0x1f98 = { 377, 380, 382 };
+gli_case_special_t unigen_special_0x1f99 = { 384, 387, 389 };
+gli_case_special_t unigen_special_0x1f9a = { 391, 394, 396 };
+gli_case_special_t unigen_special_0x1f9b = { 398, 401, 403 };
+gli_case_special_t unigen_special_0x1f9c = { 405, 408, 410 };
+gli_case_special_t unigen_special_0x1f9d = { 412, 415, 417 };
+gli_case_special_t unigen_special_0x1f9e = { 419, 422, 424 };
+gli_case_special_t unigen_special_0x1f9f = { 426, 429, 431 };
+gli_case_special_t unigen_special_0x1fa0 = { 433, 436, 438 };
+gli_case_special_t unigen_special_0x1fa1 = { 440, 443, 445 };
+gli_case_special_t unigen_special_0x1fa2 = { 447, 450, 452 };
+gli_case_special_t unigen_special_0x1fa3 = { 454, 457, 459 };
+gli_case_special_t unigen_special_0x1fa4 = { 461, 464, 466 };
+gli_case_special_t unigen_special_0x1fa5 = { 468, 471, 473 };
+gli_case_special_t unigen_special_0x1fa6 = { 475, 478, 480 };
+gli_case_special_t unigen_special_0x1fa7 = { 482, 485, 487 };
+gli_case_special_t unigen_special_0x1fa8 = { 489, 492, 494 };
+gli_case_special_t unigen_special_0x1fa9 = { 496, 499, 501 };
+gli_case_special_t unigen_special_0x1faa = { 503, 506, 508 };
+gli_case_special_t unigen_special_0x1fab = { 510, 513, 515 };
+gli_case_special_t unigen_special_0x1fac = { 517, 520, 522 };
+gli_case_special_t unigen_special_0x1fad = { 524, 527, 529 };
+gli_case_special_t unigen_special_0x1fae = { 531, 534, 536 };
+gli_case_special_t unigen_special_0x1faf = { 538, 541, 543 };
+gli_case_special_t unigen_special_0x1fb2 = { 545, 548, 550 };
+gli_case_special_t unigen_special_0x1fb3 = { 553, 556, 558 };
+gli_case_special_t unigen_special_0x1fb4 = { 560, 563, 565 };
+gli_case_special_t unigen_special_0x1fb6 = { 568, 571, 573 };
+gli_case_special_t unigen_special_0x1fb7 = { 576, 580, 582 };
+gli_case_special_t unigen_special_0x1fbc = { 586, 589, 591 };
+gli_case_special_t unigen_special_0x1fc2 = { 593, 596, 598 };
+gli_case_special_t unigen_special_0x1fc3 = { 601, 604, 606 };
+gli_case_special_t unigen_special_0x1fc4 = { 608, 611, 613 };
+gli_case_special_t unigen_special_0x1fc6 = { 616, 619, 621 };
+gli_case_special_t unigen_special_0x1fc7 = { 624, 628, 630 };
+gli_case_special_t unigen_special_0x1fcc = { 634, 637, 639 };
+gli_case_special_t unigen_special_0x1fd2 = { 641, 645, 647 };
+gli_case_special_t unigen_special_0x1fd3 = { 651, 655, 657 };
+gli_case_special_t unigen_special_0x1fd6 = { 661, 664, 666 };
+gli_case_special_t unigen_special_0x1fd7 = { 669, 673, 675 };
+gli_case_special_t unigen_special_0x1fe2 = { 679, 683, 685 };
+gli_case_special_t unigen_special_0x1fe3 = { 689, 693, 695 };
+gli_case_special_t unigen_special_0x1fe4 = { 699, 702, 704 };
+gli_case_special_t unigen_special_0x1fe6 = { 707, 710, 712 };
+gli_case_special_t unigen_special_0x1fe7 = { 715, 719, 721 };
+gli_case_special_t unigen_special_0x1ff2 = { 725, 728, 730 };
+gli_case_special_t unigen_special_0x1ff3 = { 733, 736, 738 };
+gli_case_special_t unigen_special_0x1ff4 = { 740, 743, 745 };
+gli_case_special_t unigen_special_0x1ff6 = { 748, 751, 753 };
+gli_case_special_t unigen_special_0x1ff7 = { 756, 760, 762 };
+gli_case_special_t unigen_special_0x1ffc = { 766, 769, 771 };
+gli_case_special_t unigen_special_0xfb00 = { 773, 776, 778 };
+gli_case_special_t unigen_special_0xfb01 = { 781, 784, 786 };
+gli_case_special_t unigen_special_0xfb02 = { 789, 792, 794 };
+gli_case_special_t unigen_special_0xfb03 = { 797, 801, 803 };
+gli_case_special_t unigen_special_0xfb04 = { 807, 811, 813 };
+gli_case_special_t unigen_special_0xfb05 = { 817, 820, 822 };
+gli_case_special_t unigen_special_0xfb06 = { 825, 828, 830 };
+gli_case_special_t unigen_special_0xfb13 = { 833, 836, 838 };
+gli_case_special_t unigen_special_0xfb14 = { 841, 844, 846 };
+gli_case_special_t unigen_special_0xfb15 = { 849, 852, 854 };
+gli_case_special_t unigen_special_0xfb16 = { 857, 860, 862 };
+gli_case_special_t unigen_special_0xfb17 = { 865, 868, 870 };
+
+#define RETURN_COMBINING_CLASS(ch)  \
+switch ((glui32)(ch) >> 8) {  \
+case 3:  \
+switch (ch) {  \
+    case 820:  \
+    case 821:  \
+    case 822:  \
+    case 823:  \
+    case 824:  \
+    return 1;  \
+    case 801:  \
+    case 802:  \
+    case 807:  \
+    case 808:  \
+    return 202;  \
+    case 795:  \
+    return 216;  \
+    case 790:  \
+    case 791:  \
+    case 792:  \
+    case 793:  \
+    case 796:  \
+    case 797:  \
+    case 798:  \
+    case 799:  \
+    case 800:  \
+    case 803:  \
+    case 804:  \
+    case 805:  \
+    case 806:  \
+    case 809:  \
+    case 810:  \
+    case 811:  \
+    case 812:  \
+    case 813:  \
+    case 814:  \
+    case 815:  \
+    case 816:  \
+    case 817:  \
+    case 818:  \
+    case 819:  \
+    case 825:  \
+    case 826:  \
+    case 827:  \
+    case 828:  \
+    case 839:  \
+    case 840:  \
+    case 841:  \
+    case 845:  \
+    case 846:  \
+    case 851:  \
+    case 852:  \
+    case 853:  \
+    case 854:  \
+    return 220;  \
+    case 768:  \
+    case 769:  \
+    case 770:  \
+    case 771:  \
+    case 772:  \
+    case 773:  \
+    case 774:  \
+    case 775:  \
+    case 776:  \
+    case 777:  \
+    case 778:  \
+    case 779:  \
+    case 780:  \
+    case 781:  \
+    case 782:  \
+    case 783:  \
+    case 784:  \
+    case 785:  \
+    case 786:  \
+    case 787:  \
+    case 788:  \
+    case 829:  \
+    case 830:  \
+    case 831:  \
+    case 832:  \
+    case 833:  \
+    case 834:  \
+    case 835:  \
+    case 836:  \
+    case 838:  \
+    case 842:  \
+    case 843:  \
+    case 844:  \
+    case 848:  \
+    case 849:  \
+    case 850:  \
+    case 855:  \
+    case 867:  \
+    case 868:  \
+    case 869:  \
+    case 870:  \
+    case 871:  \
+    case 872:  \
+    case 873:  \
+    case 874:  \
+    case 875:  \
+    case 876:  \
+    case 877:  \
+    case 878:  \
+    case 879:  \
+    return 230;  \
+    case 789:  \
+    case 794:  \
+    return 232;  \
+    case 863:  \
+    case 866:  \
+    return 233;  \
+    case 861:  \
+    case 862:  \
+    case 864:  \
+    case 865:  \
+    return 234;  \
+    case 837:  \
+    return 240;  \
+}  \
+return 0;  \
+case 4:  \
+switch (ch) {  \
+    case 1155:  \
+    case 1156:  \
+    case 1157:  \
+    case 1158:  \
+    return 230;  \
+}  \
+return 0;  \
+case 5:  \
+switch (ch) {  \
+    case 1456:  \
+    return 10;  \
+    case 1457:  \
+    return 11;  \
+    case 1458:  \
+    return 12;  \
+    case 1459:  \
+    return 13;  \
+    case 1460:  \
+    return 14;  \
+    case 1461:  \
+    return 15;  \
+    case 1462:  \
+    return 16;  \
+    case 1463:  \
+    return 17;  \
+    case 1464:  \
+    return 18;  \
+    case 1465:  \
+    return 19;  \
+    case 1467:  \
+    return 20;  \
+    case 1468:  \
+    return 21;  \
+    case 1469:  \
+    return 22;  \
+    case 1471:  \
+    return 23;  \
+    case 1473:  \
+    return 24;  \
+    case 1474:  \
+    return 25;  \
+    case 1425:  \
+    case 1430:  \
+    case 1435:  \
+    case 1443:  \
+    case 1444:  \
+    case 1445:  \
+    case 1446:  \
+    case 1447:  \
+    case 1450:  \
+    return 220;  \
+    case 1434:  \
+    case 1453:  \
+    return 222;  \
+    case 1454:  \
+    return 228;  \
+    case 1426:  \
+    case 1427:  \
+    case 1428:  \
+    case 1429:  \
+    case 1431:  \
+    case 1432:  \
+    case 1433:  \
+    case 1436:  \
+    case 1437:  \
+    case 1438:  \
+    case 1439:  \
+    case 1440:  \
+    case 1441:  \
+    case 1448:  \
+    case 1449:  \
+    case 1451:  \
+    case 1452:  \
+    case 1455:  \
+    case 1476:  \
+    return 230;  \
+}  \
+return 0;  \
+case 6:  \
+switch (ch) {  \
+    case 1611:  \
+    return 27;  \
+    case 1612:  \
+    return 28;  \
+    case 1613:  \
+    return 29;  \
+    case 1614:  \
+    return 30;  \
+    case 1615:  \
+    return 31;  \
+    case 1616:  \
+    return 32;  \
+    case 1617:  \
+    return 33;  \
+    case 1618:  \
+    return 34;  \
+    case 1648:  \
+    return 35;  \
+    case 1621:  \
+    case 1622:  \
+    case 1763:  \
+    case 1770:  \
+    case 1773:  \
+    return 220;  \
+    case 1552:  \
+    case 1553:  \
+    case 1554:  \
+    case 1555:  \
+    case 1556:  \
+    case 1557:  \
+    case 1619:  \
+    case 1620:  \
+    case 1623:  \
+    case 1624:  \
+    case 1750:  \
+    case 1751:  \
+    case 1752:  \
+    case 1753:  \
+    case 1754:  \
+    case 1755:  \
+    case 1756:  \
+    case 1759:  \
+    case 1760:  \
+    case 1761:  \
+    case 1762:  \
+    case 1764:  \
+    case 1767:  \
+    case 1768:  \
+    case 1771:  \
+    case 1772:  \
+    return 230;  \
+}  \
+return 0;  \
+case 7:  \
+switch (ch) {  \
+    case 1809:  \
+    return 36;  \
+    case 1841:  \
+    case 1844:  \
+    case 1847:  \
+    case 1848:  \
+    case 1849:  \
+    case 1851:  \
+    case 1852:  \
+    case 1854:  \
+    case 1858:  \
+    case 1860:  \
+    case 1862:  \
+    case 1864:  \
+    return 220;  \
+    case 1840:  \
+    case 1842:  \
+    case 1843:  \
+    case 1845:  \
+    case 1846:  \
+    case 1850:  \
+    case 1853:  \
+    case 1855:  \
+    case 1856:  \
+    case 1857:  \
+    case 1859:  \
+    case 1861:  \
+    case 1863:  \
+    case 1865:  \
+    case 1866:  \
+    return 230;  \
+}  \
+return 0;  \
+case 9:  \
+switch (ch) {  \
+    case 2364:  \
+    case 2492:  \
+    return 7;  \
+    case 2381:  \
+    case 2509:  \
+    return 9;  \
+    case 2386:  \
+    return 220;  \
+    case 2385:  \
+    case 2387:  \
+    case 2388:  \
+    return 230;  \
+}  \
+return 0;  \
+case 10:  \
+switch (ch) {  \
+    case 2620:  \
+    case 2748:  \
+    return 7;  \
+    case 2637:  \
+    case 2765:  \
+    return 9;  \
+}  \
+return 0;  \
+case 11:  \
+switch (ch) {  \
+    case 2876:  \
+    return 7;  \
+    case 2893:  \
+    case 3021:  \
+    return 9;  \
+}  \
+return 0;  \
+case 12:  \
+switch (ch) {  \
+    case 3260:  \
+    return 7;  \
+    case 3149:  \
+    case 3277:  \
+    return 9;  \
+    case 3157:  \
+    return 84;  \
+    case 3158:  \
+    return 91;  \
+}  \
+return 0;  \
+case 13:  \
+switch (ch) {  \
+    case 3405:  \
+    case 3530:  \
+    return 9;  \
+}  \
+return 0;  \
+case 14:  \
+switch (ch) {  \
+    case 3642:  \
+    return 9;  \
+    case 3640:  \
+    case 3641:  \
+    return 103;  \
+    case 3656:  \
+    case 3657:  \
+    case 3658:  \
+    case 3659:  \
+    return 107;  \
+    case 3768:  \
+    case 3769:  \
+    return 118;  \
+    case 3784:  \
+    case 3785:  \
+    case 3786:  \
+    case 3787:  \
+    return 122;  \
+}  \
+return 0;  \
+case 15:  \
+switch (ch) {  \
+    case 3972:  \
+    return 9;  \
+    case 3953:  \
+    return 129;  \
+    case 3954:  \
+    case 3962:  \
+    case 3963:  \
+    case 3964:  \
+    case 3965:  \
+    case 3968:  \
+    return 130;  \
+    case 3956:  \
+    return 132;  \
+    case 3897:  \
+    return 216;  \
+    case 3864:  \
+    case 3865:  \
+    case 3893:  \
+    case 3895:  \
+    case 4038:  \
+    return 220;  \
+    case 3970:  \
+    case 3971:  \
+    case 3974:  \
+    case 3975:  \
+    return 230;  \
+}  \
+return 0;  \
+case 16:  \
+switch (ch) {  \
+    case 4151:  \
+    return 7;  \
+    case 4153:  \
+    return 9;  \
+}  \
+return 0;  \
+case 23:  \
+switch (ch) {  \
+    case 5908:  \
+    case 5940:  \
+    case 6098:  \
+    return 9;  \
+    case 6109:  \
+    return 230;  \
+}  \
+return 0;  \
+case 24:  \
+switch (ch) {  \
+    case 6313:  \
+    return 228;  \
+}  \
+return 0;  \
+case 25:  \
+switch (ch) {  \
+    case 6459:  \
+    return 220;  \
+    case 6457:  \
+    return 222;  \
+    case 6458:  \
+    return 230;  \
+}  \
+return 0;  \
+case 32:  \
+switch (ch) {  \
+    case 8402:  \
+    case 8403:  \
+    case 8408:  \
+    case 8409:  \
+    case 8410:  \
+    case 8421:  \
+    case 8422:  \
+    case 8426:  \
+    return 1;  \
+    case 8424:  \
+    return 220;  \
+    case 8400:  \
+    case 8401:  \
+    case 8404:  \
+    case 8405:  \
+    case 8406:  \
+    case 8407:  \
+    case 8411:  \
+    case 8412:  \
+    case 8417:  \
+    case 8423:  \
+    case 8425:  \
+    return 230;  \
+}  \
+return 0;  \
+case 48:  \
+switch (ch) {  \
+    case 12441:  \
+    case 12442:  \
+    return 8;  \
+    case 12330:  \
+    return 218;  \
+    case 12333:  \
+    return 222;  \
+    case 12334:  \
+    case 12335:  \
+    return 224;  \
+    case 12331:  \
+    return 228;  \
+    case 12332:  \
+    return 232;  \
+}  \
+return 0;  \
+case 251:  \
+switch (ch) {  \
+    case 64286:  \
+    return 26;  \
+}  \
+return 0;  \
+case 254:  \
+switch (ch) {  \
+    case 65056:  \
+    case 65057:  \
+    case 65058:  \
+    case 65059:  \
+    return 230;  \
+}  \
+return 0;  \
+case 465:  \
+switch (ch) {  \
+    case 119143:  \
+    case 119144:  \
+    case 119145:  \
+    return 1;  \
+    case 119141:  \
+    case 119142:  \
+    case 119150:  \
+    case 119151:  \
+    case 119152:  \
+    case 119153:  \
+    case 119154:  \
+    return 216;  \
+    case 119163:  \
+    case 119164:  \
+    case 119165:  \
+    case 119166:  \
+    case 119167:  \
+    case 119168:  \
+    case 119169:  \
+    case 119170:  \
+    case 119178:  \
+    case 119179:  \
+    return 220;  \
+    case 119149:  \
+    return 226;  \
+    case 119173:  \
+    case 119174:  \
+    case 119175:  \
+    case 119176:  \
+    case 119177:  \
+    case 119210:  \
+    case 119211:  \
+    case 119212:  \
+    case 119213:  \
+    return 230;  \
+}  \
+return 0;  \
+}  \
+return 0;
+
+#define RETURN_COMPOSITION(ch1, ch2)  \
+switch ((glui32)(ch1) >> 8) {  \
+case 0:  \
+switch (ch1) {  \
+    case 60:  \
+    switch (ch2) {  \
+        case 824: return 8814;  \
+    }  \
+    return 0;  \
+    case 61:  \
+    switch (ch2) {  \
+        case 824: return 8800;  \
+    }  \
+    return 0;  \
+    case 62:  \
+    switch (ch2) {  \
+        case 824: return 8815;  \
+    }  \
+    return 0;  \
+    case 65:  \
+    switch (ch2) {  \
+        case 768: return 192;  \
+        case 769: return 193;  \
+        case 770: return 194;  \
+        case 771: return 195;  \
+        case 772: return 256;  \
+        case 774: return 258;  \
+        case 775: return 550;  \
+        case 776: return 196;  \
+        case 777: return 7842;  \
+        case 778: return 197;  \
+        case 780: return 461;  \
+        case 783: return 512;  \
+        case 785: return 514;  \
+        case 803: return 7840;  \
+        case 805: return 7680;  \
+        case 808: return 260;  \
+    }  \
+    return 0;  \
+    case 66:  \
+    switch (ch2) {  \
+        case 775: return 7682;  \
+        case 803: return 7684;  \
+        case 817: return 7686;  \
+    }  \
+    return 0;  \
+    case 67:  \
+    switch (ch2) {  \
+        case 769: return 262;  \
+        case 770: return 264;  \
+        case 775: return 266;  \
+        case 780: return 268;  \
+        case 807: return 199;  \
+    }  \
+    return 0;  \
+    case 68:  \
+    switch (ch2) {  \
+        case 775: return 7690;  \
+        case 780: return 270;  \
+        case 803: return 7692;  \
+        case 807: return 7696;  \
+        case 813: return 7698;  \
+        case 817: return 7694;  \
+    }  \
+    return 0;  \
+    case 69:  \
+    switch (ch2) {  \
+        case 768: return 200;  \
+        case 769: return 201;  \
+        case 770: return 202;  \
+        case 771: return 7868;  \
+        case 772: return 274;  \
+        case 774: return 276;  \
+        case 775: return 278;  \
+        case 776: return 203;  \
+        case 777: return 7866;  \
+        case 780: return 282;  \
+        case 783: return 516;  \
+        case 785: return 518;  \
+        case 803: return 7864;  \
+        case 807: return 552;  \
+        case 808: return 280;  \
+        case 813: return 7704;  \
+        case 816: return 7706;  \
+    }  \
+    return 0;  \
+    case 70:  \
+    switch (ch2) {  \
+        case 775: return 7710;  \
+    }  \
+    return 0;  \
+    case 71:  \
+    switch (ch2) {  \
+        case 769: return 500;  \
+        case 770: return 284;  \
+        case 772: return 7712;  \
+        case 774: return 286;  \
+        case 775: return 288;  \
+        case 780: return 486;  \
+        case 807: return 290;  \
+    }  \
+    return 0;  \
+    case 72:  \
+    switch (ch2) {  \
+        case 770: return 292;  \
+        case 775: return 7714;  \
+        case 776: return 7718;  \
+        case 780: return 542;  \
+        case 803: return 7716;  \
+        case 807: return 7720;  \
+        case 814: return 7722;  \
+    }  \
+    return 0;  \
+    case 73:  \
+    switch (ch2) {  \
+        case 768: return 204;  \
+        case 769: return 205;  \
+        case 770: return 206;  \
+        case 771: return 296;  \
+        case 772: return 298;  \
+        case 774: return 300;  \
+        case 775: return 304;  \
+        case 776: return 207;  \
+        case 777: return 7880;  \
+        case 780: return 463;  \
+        case 783: return 520;  \
+        case 785: return 522;  \
+        case 803: return 7882;  \
+        case 808: return 302;  \
+        case 816: return 7724;  \
+    }  \
+    return 0;  \
+    case 74:  \
+    switch (ch2) {  \
+        case 770: return 308;  \
+    }  \
+    return 0;  \
+    case 75:  \
+    switch (ch2) {  \
+        case 769: return 7728;  \
+        case 780: return 488;  \
+        case 803: return 7730;  \
+        case 807: return 310;  \
+        case 817: return 7732;  \
+    }  \
+    return 0;  \
+    case 76:  \
+    switch (ch2) {  \
+        case 769: return 313;  \
+        case 780: return 317;  \
+        case 803: return 7734;  \
+        case 807: return 315;  \
+        case 813: return 7740;  \
+        case 817: return 7738;  \
+    }  \
+    return 0;  \
+    case 77:  \
+    switch (ch2) {  \
+        case 769: return 7742;  \
+        case 775: return 7744;  \
+        case 803: return 7746;  \
+    }  \
+    return 0;  \
+    case 78:  \
+    switch (ch2) {  \
+        case 768: return 504;  \
+        case 769: return 323;  \
+        case 771: return 209;  \
+        case 775: return 7748;  \
+        case 780: return 327;  \
+        case 803: return 7750;  \
+        case 807: return 325;  \
+        case 813: return 7754;  \
+        case 817: return 7752;  \
+    }  \
+    return 0;  \
+    case 79:  \
+    switch (ch2) {  \
+        case 768: return 210;  \
+        case 769: return 211;  \
+        case 770: return 212;  \
+        case 771: return 213;  \
+        case 772: return 332;  \
+        case 774: return 334;  \
+        case 775: return 558;  \
+        case 776: return 214;  \
+        case 777: return 7886;  \
+        case 779: return 336;  \
+        case 780: return 465;  \
+        case 783: return 524;  \
+        case 785: return 526;  \
+        case 795: return 416;  \
+        case 803: return 7884;  \
+        case 808: return 490;  \
+    }  \
+    return 0;  \
+    case 80:  \
+    switch (ch2) {  \
+        case 769: return 7764;  \
+        case 775: return 7766;  \
+    }  \
+    return 0;  \
+    case 82:  \
+    switch (ch2) {  \
+        case 769: return 340;  \
+        case 775: return 7768;  \
+        case 780: return 344;  \
+        case 783: return 528;  \
+        case 785: return 530;  \
+        case 803: return 7770;  \
+        case 807: return 342;  \
+        case 817: return 7774;  \
+    }  \
+    return 0;  \
+    case 83:  \
+    switch (ch2) {  \
+        case 769: return 346;  \
+        case 770: return 348;  \
+        case 775: return 7776;  \
+        case 780: return 352;  \
+        case 803: return 7778;  \
+        case 806: return 536;  \
+        case 807: return 350;  \
+    }  \
+    return 0;  \
+    case 84:  \
+    switch (ch2) {  \
+        case 775: return 7786;  \
+        case 780: return 356;  \
+        case 803: return 7788;  \
+        case 806: return 538;  \
+        case 807: return 354;  \
+        case 813: return 7792;  \
+        case 817: return 7790;  \
+    }  \
+    return 0;  \
+    case 85:  \
+    switch (ch2) {  \
+        case 768: return 217;  \
+        case 769: return 218;  \
+        case 770: return 219;  \
+        case 771: return 360;  \
+        case 772: return 362;  \
+        case 774: return 364;  \
+        case 776: return 220;  \
+        case 777: return 7910;  \
+        case 778: return 366;  \
+        case 779: return 368;  \
+        case 780: return 467;  \
+        case 783: return 532;  \
+        case 785: return 534;  \
+        case 795: return 431;  \
+        case 803: return 7908;  \
+        case 804: return 7794;  \
+        case 808: return 370;  \
+        case 813: return 7798;  \
+        case 816: return 7796;  \
+    }  \
+    return 0;  \
+    case 86:  \
+    switch (ch2) {  \
+        case 771: return 7804;  \
+        case 803: return 7806;  \
+    }  \
+    return 0;  \
+    case 87:  \
+    switch (ch2) {  \
+        case 768: return 7808;  \
+        case 769: return 7810;  \
+        case 770: return 372;  \
+        case 775: return 7814;  \
+        case 776: return 7812;  \
+        case 803: return 7816;  \
+    }  \
+    return 0;  \
+    case 88:  \
+    switch (ch2) {  \
+        case 775: return 7818;  \
+        case 776: return 7820;  \
+    }  \
+    return 0;  \
+    case 89:  \
+    switch (ch2) {  \
+        case 768: return 7922;  \
+        case 769: return 221;  \
+        case 770: return 374;  \
+        case 771: return 7928;  \
+        case 772: return 562;  \
+        case 775: return 7822;  \
+        case 776: return 376;  \
+        case 777: return 7926;  \
+        case 803: return 7924;  \
+    }  \
+    return 0;  \
+    case 90:  \
+    switch (ch2) {  \
+        case 769: return 377;  \
+        case 770: return 7824;  \
+        case 775: return 379;  \
+        case 780: return 381;  \
+        case 803: return 7826;  \
+        case 817: return 7828;  \
+    }  \
+    return 0;  \
+    case 97:  \
+    switch (ch2) {  \
+        case 768: return 224;  \
+        case 769: return 225;  \
+        case 770: return 226;  \
+        case 771: return 227;  \
+        case 772: return 257;  \
+        case 774: return 259;  \
+        case 775: return 551;  \
+        case 776: return 228;  \
+        case 777: return 7843;  \
+        case 778: return 229;  \
+        case 780: return 462;  \
+        case 783: return 513;  \
+        case 785: return 515;  \
+        case 803: return 7841;  \
+        case 805: return 7681;  \
+        case 808: return 261;  \
+    }  \
+    return 0;  \
+    case 98:  \
+    switch (ch2) {  \
+        case 775: return 7683;  \
+        case 803: return 7685;  \
+        case 817: return 7687;  \
+    }  \
+    return 0;  \
+    case 99:  \
+    switch (ch2) {  \
+        case 769: return 263;  \
+        case 770: return 265;  \
+        case 775: return 267;  \
+        case 780: return 269;  \
+        case 807: return 231;  \
+    }  \
+    return 0;  \
+    case 100:  \
+    switch (ch2) {  \
+        case 775: return 7691;  \
+        case 780: return 271;  \
+        case 803: return 7693;  \
+        case 807: return 7697;  \
+        case 813: return 7699;  \
+        case 817: return 7695;  \
+    }  \
+    return 0;  \
+    case 101:  \
+    switch (ch2) {  \
+        case 768: return 232;  \
+        case 769: return 233;  \
+        case 770: return 234;  \
+        case 771: return 7869;  \
+        case 772: return 275;  \
+        case 774: return 277;  \
+        case 775: return 279;  \
+        case 776: return 235;  \
+        case 777: return 7867;  \
+        case 780: return 283;  \
+        case 783: return 517;  \
+        case 785: return 519;  \
+        case 803: return 7865;  \
+        case 807: return 553;  \
+        case 808: return 281;  \
+        case 813: return 7705;  \
+        case 816: return 7707;  \
+    }  \
+    return 0;  \
+    case 102:  \
+    switch (ch2) {  \
+        case 775: return 7711;  \
+    }  \
+    return 0;  \
+    case 103:  \
+    switch (ch2) {  \
+        case 769: return 501;  \
+        case 770: return 285;  \
+        case 772: return 7713;  \
+        case 774: return 287;  \
+        case 775: return 289;  \
+        case 780: return 487;  \
+        case 807: return 291;  \
+    }  \
+    return 0;  \
+    case 104:  \
+    switch (ch2) {  \
+        case 770: return 293;  \
+        case 775: return 7715;  \
+        case 776: return 7719;  \
+        case 780: return 543;  \
+        case 803: return 7717;  \
+        case 807: return 7721;  \
+        case 814: return 7723;  \
+        case 817: return 7830;  \
+    }  \
+    return 0;  \
+    case 105:  \
+    switch (ch2) {  \
+        case 768: return 236;  \
+        case 769: return 237;  \
+        case 770: return 238;  \
+        case 771: return 297;  \
+        case 772: return 299;  \
+        case 774: return 301;  \
+        case 776: return 239;  \
+        case 777: return 7881;  \
+        case 780: return 464;  \
+        case 783: return 521;  \
+        case 785: return 523;  \
+        case 803: return 7883;  \
+        case 808: return 303;  \
+        case 816: return 7725;  \
+    }  \
+    return 0;  \
+    case 106:  \
+    switch (ch2) {  \
+        case 770: return 309;  \
+        case 780: return 496;  \
+    }  \
+    return 0;  \
+    case 107:  \
+    switch (ch2) {  \
+        case 769: return 7729;  \
+        case 780: return 489;  \
+        case 803: return 7731;  \
+        case 807: return 311;  \
+        case 817: return 7733;  \
+    }  \
+    return 0;  \
+    case 108:  \
+    switch (ch2) {  \
+        case 769: return 314;  \
+        case 780: return 318;  \
+        case 803: return 7735;  \
+        case 807: return 316;  \
+        case 813: return 7741;  \
+        case 817: return 7739;  \
+    }  \
+    return 0;  \
+    case 109:  \
+    switch (ch2) {  \
+        case 769: return 7743;  \
+        case 775: return 7745;  \
+        case 803: return 7747;  \
+    }  \
+    return 0;  \
+    case 110:  \
+    switch (ch2) {  \
+        case 768: return 505;  \
+        case 769: return 324;  \
+        case 771: return 241;  \
+        case 775: return 7749;  \
+        case 780: return 328;  \
+        case 803: return 7751;  \
+        case 807: return 326;  \
+        case 813: return 7755;  \
+        case 817: return 7753;  \
+    }  \
+    return 0;  \
+    case 111:  \
+    switch (ch2) {  \
+        case 768: return 242;  \
+        case 769: return 243;  \
+        case 770: return 244;  \
+        case 771: return 245;  \
+        case 772: return 333;  \
+        case 774: return 335;  \
+        case 775: return 559;  \
+        case 776: return 246;  \
+        case 777: return 7887;  \
+        case 779: return 337;  \
+        case 780: return 466;  \
+        case 783: return 525;  \
+        case 785: return 527;  \
+        case 795: return 417;  \
+        case 803: return 7885;  \
+        case 808: return 491;  \
+    }  \
+    return 0;  \
+    case 112:  \
+    switch (ch2) {  \
+        case 769: return 7765;  \
+        case 775: return 7767;  \
+    }  \
+    return 0;  \
+    case 114:  \
+    switch (ch2) {  \
+        case 769: return 341;  \
+        case 775: return 7769;  \
+        case 780: return 345;  \
+        case 783: return 529;  \
+        case 785: return 531;  \
+        case 803: return 7771;  \
+        case 807: return 343;  \
+        case 817: return 7775;  \
+    }  \
+    return 0;  \
+    case 115:  \
+    switch (ch2) {  \
+        case 769: return 347;  \
+        case 770: return 349;  \
+        case 775: return 7777;  \
+        case 780: return 353;  \
+        case 803: return 7779;  \
+        case 806: return 537;  \
+        case 807: return 351;  \
+    }  \
+    return 0;  \
+    case 116:  \
+    switch (ch2) {  \
+        case 775: return 7787;  \
+        case 776: return 7831;  \
+        case 780: return 357;  \
+        case 803: return 7789;  \
+        case 806: return 539;  \
+        case 807: return 355;  \
+        case 813: return 7793;  \
+        case 817: return 7791;  \
+    }  \
+    return 0;  \
+    case 117:  \
+    switch (ch2) {  \
+        case 768: return 249;  \
+        case 769: return 250;  \
+        case 770: return 251;  \
+        case 771: return 361;  \
+        case 772: return 363;  \
+        case 774: return 365;  \
+        case 776: return 252;  \
+        case 777: return 7911;  \
+        case 778: return 367;  \
+        case 779: return 369;  \
+        case 780: return 468;  \
+        case 783: return 533;  \
+        case 785: return 535;  \
+        case 795: return 432;  \
+        case 803: return 7909;  \
+        case 804: return 7795;  \
+        case 808: return 371;  \
+        case 813: return 7799;  \
+        case 816: return 7797;  \
+    }  \
+    return 0;  \
+    case 118:  \
+    switch (ch2) {  \
+        case 771: return 7805;  \
+        case 803: return 7807;  \
+    }  \
+    return 0;  \
+    case 119:  \
+    switch (ch2) {  \
+        case 768: return 7809;  \
+        case 769: return 7811;  \
+        case 770: return 373;  \
+        case 775: return 7815;  \
+        case 776: return 7813;  \
+        case 778: return 7832;  \
+        case 803: return 7817;  \
+    }  \
+    return 0;  \
+    case 120:  \
+    switch (ch2) {  \
+        case 775: return 7819;  \
+        case 776: return 7821;  \
+    }  \
+    return 0;  \
+    case 121:  \
+    switch (ch2) {  \
+        case 768: return 7923;  \
+        case 769: return 253;  \
+        case 770: return 375;  \
+        case 771: return 7929;  \
+        case 772: return 563;  \
+        case 775: return 7823;  \
+        case 776: return 255;  \
+        case 777: return 7927;  \
+        case 778: return 7833;  \
+        case 803: return 7925;  \
+    }  \
+    return 0;  \
+    case 122:  \
+    switch (ch2) {  \
+        case 769: return 378;  \
+        case 770: return 7825;  \
+        case 775: return 380;  \
+        case 780: return 382;  \
+        case 803: return 7827;  \
+        case 817: return 7829;  \
+    }  \
+    return 0;  \
+    case 168:  \
+    switch (ch2) {  \
+        case 768: return 8173;  \
+        case 769: return 901;  \
+        case 834: return 8129;  \
+    }  \
+    return 0;  \
+    case 194:  \
+    switch (ch2) {  \
+        case 768: return 7846;  \
+        case 769: return 7844;  \
+        case 771: return 7850;  \
+        case 777: return 7848;  \
+    }  \
+    return 0;  \
+    case 196:  \
+    switch (ch2) {  \
+        case 772: return 478;  \
+    }  \
+    return 0;  \
+    case 197:  \
+    switch (ch2) {  \
+        case 769: return 506;  \
+    }  \
+    return 0;  \
+    case 198:  \
+    switch (ch2) {  \
+        case 769: return 508;  \
+        case 772: return 482;  \
+    }  \
+    return 0;  \
+    case 199:  \
+    switch (ch2) {  \
+        case 769: return 7688;  \
+    }  \
+    return 0;  \
+    case 202:  \
+    switch (ch2) {  \
+        case 768: return 7872;  \
+        case 769: return 7870;  \
+        case 771: return 7876;  \
+        case 777: return 7874;  \
+    }  \
+    return 0;  \
+    case 207:  \
+    switch (ch2) {  \
+        case 769: return 7726;  \
+    }  \
+    return 0;  \
+    case 212:  \
+    switch (ch2) {  \
+        case 768: return 7890;  \
+        case 769: return 7888;  \
+        case 771: return 7894;  \
+        case 777: return 7892;  \
+    }  \
+    return 0;  \
+    case 213:  \
+    switch (ch2) {  \
+        case 769: return 7756;  \
+        case 772: return 556;  \
+        case 776: return 7758;  \
+    }  \
+    return 0;  \
+    case 214:  \
+    switch (ch2) {  \
+        case 772: return 554;  \
+    }  \
+    return 0;  \
+    case 216:  \
+    switch (ch2) {  \
+        case 769: return 510;  \
+    }  \
+    return 0;  \
+    case 220:  \
+    switch (ch2) {  \
+        case 768: return 475;  \
+        case 769: return 471;  \
+        case 772: return 469;  \
+        case 780: return 473;  \
+    }  \
+    return 0;  \
+    case 226:  \
+    switch (ch2) {  \
+        case 768: return 7847;  \
+        case 769: return 7845;  \
+        case 771: return 7851;  \
+        case 777: return 7849;  \
+    }  \
+    return 0;  \
+    case 228:  \
+    switch (ch2) {  \
+        case 772: return 479;  \
+    }  \
+    return 0;  \
+    case 229:  \
+    switch (ch2) {  \
+        case 769: return 507;  \
+    }  \
+    return 0;  \
+    case 230:  \
+    switch (ch2) {  \
+        case 769: return 509;  \
+        case 772: return 483;  \
+    }  \
+    return 0;  \
+    case 231:  \
+    switch (ch2) {  \
+        case 769: return 7689;  \
+    }  \
+    return 0;  \
+    case 234:  \
+    switch (ch2) {  \
+        case 768: return 7873;  \
+        case 769: return 7871;  \
+        case 771: return 7877;  \
+        case 777: return 7875;  \
+    }  \
+    return 0;  \
+    case 239:  \
+    switch (ch2) {  \
+        case 769: return 7727;  \
+    }  \
+    return 0;  \
+    case 244:  \
+    switch (ch2) {  \
+        case 768: return 7891;  \
+        case 769: return 7889;  \
+        case 771: return 7895;  \
+        case 777: return 7893;  \
+    }  \
+    return 0;  \
+    case 245:  \
+    switch (ch2) {  \
+        case 769: return 7757;  \
+        case 772: return 557;  \
+        case 776: return 7759;  \
+    }  \
+    return 0;  \
+    case 246:  \
+    switch (ch2) {  \
+        case 772: return 555;  \
+    }  \
+    return 0;  \
+    case 248:  \
+    switch (ch2) {  \
+        case 769: return 511;  \
+    }  \
+    return 0;  \
+    case 252:  \
+    switch (ch2) {  \
+        case 768: return 476;  \
+        case 769: return 472;  \
+        case 772: return 470;  \
+        case 780: return 474;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 1:  \
+switch (ch1) {  \
+    case 258:  \
+    switch (ch2) {  \
+        case 768: return 7856;  \
+        case 769: return 7854;  \
+        case 771: return 7860;  \
+        case 777: return 7858;  \
+    }  \
+    return 0;  \
+    case 259:  \
+    switch (ch2) {  \
+        case 768: return 7857;  \
+        case 769: return 7855;  \
+        case 771: return 7861;  \
+        case 777: return 7859;  \
+    }  \
+    return 0;  \
+    case 274:  \
+    switch (ch2) {  \
+        case 768: return 7700;  \
+        case 769: return 7702;  \
+    }  \
+    return 0;  \
+    case 275:  \
+    switch (ch2) {  \
+        case 768: return 7701;  \
+        case 769: return 7703;  \
+    }  \
+    return 0;  \
+    case 332:  \
+    switch (ch2) {  \
+        case 768: return 7760;  \
+        case 769: return 7762;  \
+    }  \
+    return 0;  \
+    case 333:  \
+    switch (ch2) {  \
+        case 768: return 7761;  \
+        case 769: return 7763;  \
+    }  \
+    return 0;  \
+    case 346:  \
+    switch (ch2) {  \
+        case 775: return 7780;  \
+    }  \
+    return 0;  \
+    case 347:  \
+    switch (ch2) {  \
+        case 775: return 7781;  \
+    }  \
+    return 0;  \
+    case 352:  \
+    switch (ch2) {  \
+        case 775: return 7782;  \
+    }  \
+    return 0;  \
+    case 353:  \
+    switch (ch2) {  \
+        case 775: return 7783;  \
+    }  \
+    return 0;  \
+    case 360:  \
+    switch (ch2) {  \
+        case 769: return 7800;  \
+    }  \
+    return 0;  \
+    case 361:  \
+    switch (ch2) {  \
+        case 769: return 7801;  \
+    }  \
+    return 0;  \
+    case 362:  \
+    switch (ch2) {  \
+        case 776: return 7802;  \
+    }  \
+    return 0;  \
+    case 363:  \
+    switch (ch2) {  \
+        case 776: return 7803;  \
+    }  \
+    return 0;  \
+    case 383:  \
+    switch (ch2) {  \
+        case 775: return 7835;  \
+    }  \
+    return 0;  \
+    case 416:  \
+    switch (ch2) {  \
+        case 768: return 7900;  \
+        case 769: return 7898;  \
+        case 771: return 7904;  \
+        case 777: return 7902;  \
+        case 803: return 7906;  \
+    }  \
+    return 0;  \
+    case 417:  \
+    switch (ch2) {  \
+        case 768: return 7901;  \
+        case 769: return 7899;  \
+        case 771: return 7905;  \
+        case 777: return 7903;  \
+        case 803: return 7907;  \
+    }  \
+    return 0;  \
+    case 431:  \
+    switch (ch2) {  \
+        case 768: return 7914;  \
+        case 769: return 7912;  \
+        case 771: return 7918;  \
+        case 777: return 7916;  \
+        case 803: return 7920;  \
+    }  \
+    return 0;  \
+    case 432:  \
+    switch (ch2) {  \
+        case 768: return 7915;  \
+        case 769: return 7913;  \
+        case 771: return 7919;  \
+        case 777: return 7917;  \
+        case 803: return 7921;  \
+    }  \
+    return 0;  \
+    case 439:  \
+    switch (ch2) {  \
+        case 780: return 494;  \
+    }  \
+    return 0;  \
+    case 490:  \
+    switch (ch2) {  \
+        case 772: return 492;  \
+    }  \
+    return 0;  \
+    case 491:  \
+    switch (ch2) {  \
+        case 772: return 493;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 2:  \
+switch (ch1) {  \
+    case 550:  \
+    switch (ch2) {  \
+        case 772: return 480;  \
+    }  \
+    return 0;  \
+    case 551:  \
+    switch (ch2) {  \
+        case 772: return 481;  \
+    }  \
+    return 0;  \
+    case 552:  \
+    switch (ch2) {  \
+        case 774: return 7708;  \
+    }  \
+    return 0;  \
+    case 553:  \
+    switch (ch2) {  \
+        case 774: return 7709;  \
+    }  \
+    return 0;  \
+    case 558:  \
+    switch (ch2) {  \
+        case 772: return 560;  \
+    }  \
+    return 0;  \
+    case 559:  \
+    switch (ch2) {  \
+        case 772: return 561;  \
+    }  \
+    return 0;  \
+    case 658:  \
+    switch (ch2) {  \
+        case 780: return 495;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 3:  \
+switch (ch1) {  \
+    case 776:  \
+    switch (ch2) {  \
+        case 769: return 836;  \
+    }  \
+    return 0;  \
+    case 913:  \
+    switch (ch2) {  \
+        case 768: return 8122;  \
+        case 769: return 902;  \
+        case 772: return 8121;  \
+        case 774: return 8120;  \
+        case 787: return 7944;  \
+        case 788: return 7945;  \
+        case 837: return 8124;  \
+    }  \
+    return 0;  \
+    case 917:  \
+    switch (ch2) {  \
+        case 768: return 8136;  \
+        case 769: return 904;  \
+        case 787: return 7960;  \
+        case 788: return 7961;  \
+    }  \
+    return 0;  \
+    case 919:  \
+    switch (ch2) {  \
+        case 768: return 8138;  \
+        case 769: return 905;  \
+        case 787: return 7976;  \
+        case 788: return 7977;  \
+        case 837: return 8140;  \
+    }  \
+    return 0;  \
+    case 921:  \
+    switch (ch2) {  \
+        case 768: return 8154;  \
+        case 769: return 906;  \
+        case 772: return 8153;  \
+        case 774: return 8152;  \
+        case 776: return 938;  \
+        case 787: return 7992;  \
+        case 788: return 7993;  \
+    }  \
+    return 0;  \
+    case 927:  \
+    switch (ch2) {  \
+        case 768: return 8184;  \
+        case 769: return 908;  \
+        case 787: return 8008;  \
+        case 788: return 8009;  \
+    }  \
+    return 0;  \
+    case 929:  \
+    switch (ch2) {  \
+        case 788: return 8172;  \
+    }  \
+    return 0;  \
+    case 933:  \
+    switch (ch2) {  \
+        case 768: return 8170;  \
+        case 769: return 910;  \
+        case 772: return 8169;  \
+        case 774: return 8168;  \
+        case 776: return 939;  \
+        case 788: return 8025;  \
+    }  \
+    return 0;  \
+    case 937:  \
+    switch (ch2) {  \
+        case 768: return 8186;  \
+        case 769: return 911;  \
+        case 787: return 8040;  \
+        case 788: return 8041;  \
+        case 837: return 8188;  \
+    }  \
+    return 0;  \
+    case 940:  \
+    switch (ch2) {  \
+        case 837: return 8116;  \
+    }  \
+    return 0;  \
+    case 942:  \
+    switch (ch2) {  \
+        case 837: return 8132;  \
+    }  \
+    return 0;  \
+    case 945:  \
+    switch (ch2) {  \
+        case 768: return 8048;  \
+        case 769: return 940;  \
+        case 772: return 8113;  \
+        case 774: return 8112;  \
+        case 787: return 7936;  \
+        case 788: return 7937;  \
+        case 834: return 8118;  \
+        case 837: return 8115;  \
+    }  \
+    return 0;  \
+    case 949:  \
+    switch (ch2) {  \
+        case 768: return 8050;  \
+        case 769: return 941;  \
+        case 787: return 7952;  \
+        case 788: return 7953;  \
+    }  \
+    return 0;  \
+    case 951:  \
+    switch (ch2) {  \
+        case 768: return 8052;  \
+        case 769: return 942;  \
+        case 787: return 7968;  \
+        case 788: return 7969;  \
+        case 834: return 8134;  \
+        case 837: return 8131;  \
+    }  \
+    return 0;  \
+    case 953:  \
+    switch (ch2) {  \
+        case 768: return 8054;  \
+        case 769: return 943;  \
+        case 772: return 8145;  \
+        case 774: return 8144;  \
+        case 776: return 970;  \
+        case 787: return 7984;  \
+        case 788: return 7985;  \
+        case 834: return 8150;  \
+    }  \
+    return 0;  \
+    case 959:  \
+    switch (ch2) {  \
+        case 768: return 8056;  \
+        case 769: return 972;  \
+        case 787: return 8000;  \
+        case 788: return 8001;  \
+    }  \
+    return 0;  \
+    case 961:  \
+    switch (ch2) {  \
+        case 787: return 8164;  \
+        case 788: return 8165;  \
+    }  \
+    return 0;  \
+    case 965:  \
+    switch (ch2) {  \
+        case 768: return 8058;  \
+        case 769: return 973;  \
+        case 772: return 8161;  \
+        case 774: return 8160;  \
+        case 776: return 971;  \
+        case 787: return 8016;  \
+        case 788: return 8017;  \
+        case 834: return 8166;  \
+    }  \
+    return 0;  \
+    case 969:  \
+    switch (ch2) {  \
+        case 768: return 8060;  \
+        case 769: return 974;  \
+        case 787: return 8032;  \
+        case 788: return 8033;  \
+        case 834: return 8182;  \
+        case 837: return 8179;  \
+    }  \
+    return 0;  \
+    case 970:  \
+    switch (ch2) {  \
+        case 768: return 8146;  \
+        case 769: return 912;  \
+        case 834: return 8151;  \
+    }  \
+    return 0;  \
+    case 971:  \
+    switch (ch2) {  \
+        case 768: return 8162;  \
+        case 769: return 944;  \
+        case 834: return 8167;  \
+    }  \
+    return 0;  \
+    case 974:  \
+    switch (ch2) {  \
+        case 837: return 8180;  \
+    }  \
+    return 0;  \
+    case 978:  \
+    switch (ch2) {  \
+        case 769: return 979;  \
+        case 776: return 980;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 4:  \
+switch (ch1) {  \
+    case 1030:  \
+    switch (ch2) {  \
+        case 776: return 1031;  \
+    }  \
+    return 0;  \
+    case 1040:  \
+    switch (ch2) {  \
+        case 774: return 1232;  \
+        case 776: return 1234;  \
+    }  \
+    return 0;  \
+    case 1043:  \
+    switch (ch2) {  \
+        case 769: return 1027;  \
+    }  \
+    return 0;  \
+    case 1045:  \
+    switch (ch2) {  \
+        case 768: return 1024;  \
+        case 774: return 1238;  \
+        case 776: return 1025;  \
+    }  \
+    return 0;  \
+    case 1046:  \
+    switch (ch2) {  \
+        case 774: return 1217;  \
+        case 776: return 1244;  \
+    }  \
+    return 0;  \
+    case 1047:  \
+    switch (ch2) {  \
+        case 776: return 1246;  \
+    }  \
+    return 0;  \
+    case 1048:  \
+    switch (ch2) {  \
+        case 768: return 1037;  \
+        case 772: return 1250;  \
+        case 774: return 1049;  \
+        case 776: return 1252;  \
+    }  \
+    return 0;  \
+    case 1050:  \
+    switch (ch2) {  \
+        case 769: return 1036;  \
+    }  \
+    return 0;  \
+    case 1054:  \
+    switch (ch2) {  \
+        case 776: return 1254;  \
+    }  \
+    return 0;  \
+    case 1059:  \
+    switch (ch2) {  \
+        case 772: return 1262;  \
+        case 774: return 1038;  \
+        case 776: return 1264;  \
+        case 779: return 1266;  \
+    }  \
+    return 0;  \
+    case 1063:  \
+    switch (ch2) {  \
+        case 776: return 1268;  \
+    }  \
+    return 0;  \
+    case 1067:  \
+    switch (ch2) {  \
+        case 776: return 1272;  \
+    }  \
+    return 0;  \
+    case 1069:  \
+    switch (ch2) {  \
+        case 776: return 1260;  \
+    }  \
+    return 0;  \
+    case 1072:  \
+    switch (ch2) {  \
+        case 774: return 1233;  \
+        case 776: return 1235;  \
+    }  \
+    return 0;  \
+    case 1075:  \
+    switch (ch2) {  \
+        case 769: return 1107;  \
+    }  \
+    return 0;  \
+    case 1077:  \
+    switch (ch2) {  \
+        case 768: return 1104;  \
+        case 774: return 1239;  \
+        case 776: return 1105;  \
+    }  \
+    return 0;  \
+    case 1078:  \
+    switch (ch2) {  \
+        case 774: return 1218;  \
+        case 776: return 1245;  \
+    }  \
+    return 0;  \
+    case 1079:  \
+    switch (ch2) {  \
+        case 776: return 1247;  \
+    }  \
+    return 0;  \
+    case 1080:  \
+    switch (ch2) {  \
+        case 768: return 1117;  \
+        case 772: return 1251;  \
+        case 774: return 1081;  \
+        case 776: return 1253;  \
+    }  \
+    return 0;  \
+    case 1082:  \
+    switch (ch2) {  \
+        case 769: return 1116;  \
+    }  \
+    return 0;  \
+    case 1086:  \
+    switch (ch2) {  \
+        case 776: return 1255;  \
+    }  \
+    return 0;  \
+    case 1091:  \
+    switch (ch2) {  \
+        case 772: return 1263;  \
+        case 774: return 1118;  \
+        case 776: return 1265;  \
+        case 779: return 1267;  \
+    }  \
+    return 0;  \
+    case 1095:  \
+    switch (ch2) {  \
+        case 776: return 1269;  \
+    }  \
+    return 0;  \
+    case 1099:  \
+    switch (ch2) {  \
+        case 776: return 1273;  \
+    }  \
+    return 0;  \
+    case 1101:  \
+    switch (ch2) {  \
+        case 776: return 1261;  \
+    }  \
+    return 0;  \
+    case 1110:  \
+    switch (ch2) {  \
+        case 776: return 1111;  \
+    }  \
+    return 0;  \
+    case 1140:  \
+    switch (ch2) {  \
+        case 783: return 1142;  \
+    }  \
+    return 0;  \
+    case 1141:  \
+    switch (ch2) {  \
+        case 783: return 1143;  \
+    }  \
+    return 0;  \
+    case 1240:  \
+    switch (ch2) {  \
+        case 776: return 1242;  \
+    }  \
+    return 0;  \
+    case 1241:  \
+    switch (ch2) {  \
+        case 776: return 1243;  \
+    }  \
+    return 0;  \
+    case 1256:  \
+    switch (ch2) {  \
+        case 776: return 1258;  \
+    }  \
+    return 0;  \
+    case 1257:  \
+    switch (ch2) {  \
+        case 776: return 1259;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 5:  \
+switch (ch1) {  \
+    case 1488:  \
+    switch (ch2) {  \
+        case 1463: return 64302;  \
+        case 1464: return 64303;  \
+        case 1468: return 64304;  \
+    }  \
+    return 0;  \
+    case 1489:  \
+    switch (ch2) {  \
+        case 1468: return 64305;  \
+        case 1471: return 64332;  \
+    }  \
+    return 0;  \
+    case 1490:  \
+    switch (ch2) {  \
+        case 1468: return 64306;  \
+    }  \
+    return 0;  \
+    case 1491:  \
+    switch (ch2) {  \
+        case 1468: return 64307;  \
+    }  \
+    return 0;  \
+    case 1492:  \
+    switch (ch2) {  \
+        case 1468: return 64308;  \
+    }  \
+    return 0;  \
+    case 1493:  \
+    switch (ch2) {  \
+        case 1465: return 64331;  \
+        case 1468: return 64309;  \
+    }  \
+    return 0;  \
+    case 1494:  \
+    switch (ch2) {  \
+        case 1468: return 64310;  \
+    }  \
+    return 0;  \
+    case 1496:  \
+    switch (ch2) {  \
+        case 1468: return 64312;  \
+    }  \
+    return 0;  \
+    case 1497:  \
+    switch (ch2) {  \
+        case 1460: return 64285;  \
+        case 1468: return 64313;  \
+    }  \
+    return 0;  \
+    case 1498:  \
+    switch (ch2) {  \
+        case 1468: return 64314;  \
+    }  \
+    return 0;  \
+    case 1499:  \
+    switch (ch2) {  \
+        case 1468: return 64315;  \
+        case 1471: return 64333;  \
+    }  \
+    return 0;  \
+    case 1500:  \
+    switch (ch2) {  \
+        case 1468: return 64316;  \
+    }  \
+    return 0;  \
+    case 1502:  \
+    switch (ch2) {  \
+        case 1468: return 64318;  \
+    }  \
+    return 0;  \
+    case 1504:  \
+    switch (ch2) {  \
+        case 1468: return 64320;  \
+    }  \
+    return 0;  \
+    case 1505:  \
+    switch (ch2) {  \
+        case 1468: return 64321;  \
+    }  \
+    return 0;  \
+    case 1507:  \
+    switch (ch2) {  \
+        case 1468: return 64323;  \
+    }  \
+    return 0;  \
+    case 1508:  \
+    switch (ch2) {  \
+        case 1468: return 64324;  \
+        case 1471: return 64334;  \
+    }  \
+    return 0;  \
+    case 1510:  \
+    switch (ch2) {  \
+        case 1468: return 64326;  \
+    }  \
+    return 0;  \
+    case 1511:  \
+    switch (ch2) {  \
+        case 1468: return 64327;  \
+    }  \
+    return 0;  \
+    case 1512:  \
+    switch (ch2) {  \
+        case 1468: return 64328;  \
+    }  \
+    return 0;  \
+    case 1513:  \
+    switch (ch2) {  \
+        case 1468: return 64329;  \
+        case 1473: return 64298;  \
+        case 1474: return 64299;  \
+    }  \
+    return 0;  \
+    case 1514:  \
+    switch (ch2) {  \
+        case 1468: return 64330;  \
+    }  \
+    return 0;  \
+    case 1522:  \
+    switch (ch2) {  \
+        case 1463: return 64287;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 6:  \
+switch (ch1) {  \
+    case 1575:  \
+    switch (ch2) {  \
+        case 1619: return 1570;  \
+        case 1620: return 1571;  \
+        case 1621: return 1573;  \
+    }  \
+    return 0;  \
+    case 1608:  \
+    switch (ch2) {  \
+        case 1620: return 1572;  \
+    }  \
+    return 0;  \
+    case 1610:  \
+    switch (ch2) {  \
+        case 1620: return 1574;  \
+    }  \
+    return 0;  \
+    case 1729:  \
+    switch (ch2) {  \
+        case 1620: return 1730;  \
+    }  \
+    return 0;  \
+    case 1746:  \
+    switch (ch2) {  \
+        case 1620: return 1747;  \
+    }  \
+    return 0;  \
+    case 1749:  \
+    switch (ch2) {  \
+        case 1620: return 1728;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 9:  \
+switch (ch1) {  \
+    case 2325:  \
+    switch (ch2) {  \
+        case 2364: return 2392;  \
+    }  \
+    return 0;  \
+    case 2326:  \
+    switch (ch2) {  \
+        case 2364: return 2393;  \
+    }  \
+    return 0;  \
+    case 2327:  \
+    switch (ch2) {  \
+        case 2364: return 2394;  \
+    }  \
+    return 0;  \
+    case 2332:  \
+    switch (ch2) {  \
+        case 2364: return 2395;  \
+    }  \
+    return 0;  \
+    case 2337:  \
+    switch (ch2) {  \
+        case 2364: return 2396;  \
+    }  \
+    return 0;  \
+    case 2338:  \
+    switch (ch2) {  \
+        case 2364: return 2397;  \
+    }  \
+    return 0;  \
+    case 2344:  \
+    switch (ch2) {  \
+        case 2364: return 2345;  \
+    }  \
+    return 0;  \
+    case 2347:  \
+    switch (ch2) {  \
+        case 2364: return 2398;  \
+    }  \
+    return 0;  \
+    case 2351:  \
+    switch (ch2) {  \
+        case 2364: return 2399;  \
+    }  \
+    return 0;  \
+    case 2352:  \
+    switch (ch2) {  \
+        case 2364: return 2353;  \
+    }  \
+    return 0;  \
+    case 2355:  \
+    switch (ch2) {  \
+        case 2364: return 2356;  \
+    }  \
+    return 0;  \
+    case 2465:  \
+    switch (ch2) {  \
+        case 2492: return 2524;  \
+    }  \
+    return 0;  \
+    case 2466:  \
+    switch (ch2) {  \
+        case 2492: return 2525;  \
+    }  \
+    return 0;  \
+    case 2479:  \
+    switch (ch2) {  \
+        case 2492: return 2527;  \
+    }  \
+    return 0;  \
+    case 2503:  \
+    switch (ch2) {  \
+        case 2494: return 2507;  \
+        case 2519: return 2508;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 10:  \
+switch (ch1) {  \
+    case 2582:  \
+    switch (ch2) {  \
+        case 2620: return 2649;  \
+    }  \
+    return 0;  \
+    case 2583:  \
+    switch (ch2) {  \
+        case 2620: return 2650;  \
+    }  \
+    return 0;  \
+    case 2588:  \
+    switch (ch2) {  \
+        case 2620: return 2651;  \
+    }  \
+    return 0;  \
+    case 2603:  \
+    switch (ch2) {  \
+        case 2620: return 2654;  \
+    }  \
+    return 0;  \
+    case 2610:  \
+    switch (ch2) {  \
+        case 2620: return 2611;  \
+    }  \
+    return 0;  \
+    case 2616:  \
+    switch (ch2) {  \
+        case 2620: return 2614;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 11:  \
+switch (ch1) {  \
+    case 2849:  \
+    switch (ch2) {  \
+        case 2876: return 2908;  \
+    }  \
+    return 0;  \
+    case 2850:  \
+    switch (ch2) {  \
+        case 2876: return 2909;  \
+    }  \
+    return 0;  \
+    case 2887:  \
+    switch (ch2) {  \
+        case 2878: return 2891;  \
+        case 2902: return 2888;  \
+        case 2903: return 2892;  \
+    }  \
+    return 0;  \
+    case 2962:  \
+    switch (ch2) {  \
+        case 3031: return 2964;  \
+    }  \
+    return 0;  \
+    case 3014:  \
+    switch (ch2) {  \
+        case 3006: return 3018;  \
+        case 3031: return 3020;  \
+    }  \
+    return 0;  \
+    case 3015:  \
+    switch (ch2) {  \
+        case 3006: return 3019;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 12:  \
+switch (ch1) {  \
+    case 3142:  \
+    switch (ch2) {  \
+        case 3158: return 3144;  \
+    }  \
+    return 0;  \
+    case 3263:  \
+    switch (ch2) {  \
+        case 3285: return 3264;  \
+    }  \
+    return 0;  \
+    case 3270:  \
+    switch (ch2) {  \
+        case 3266: return 3274;  \
+        case 3285: return 3271;  \
+        case 3286: return 3272;  \
+    }  \
+    return 0;  \
+    case 3274:  \
+    switch (ch2) {  \
+        case 3285: return 3275;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 13:  \
+switch (ch1) {  \
+    case 3398:  \
+    switch (ch2) {  \
+        case 3390: return 3402;  \
+        case 3415: return 3404;  \
+    }  \
+    return 0;  \
+    case 3399:  \
+    switch (ch2) {  \
+        case 3390: return 3403;  \
+    }  \
+    return 0;  \
+    case 3545:  \
+    switch (ch2) {  \
+        case 3530: return 3546;  \
+        case 3535: return 3548;  \
+        case 3551: return 3550;  \
+    }  \
+    return 0;  \
+    case 3548:  \
+    switch (ch2) {  \
+        case 3530: return 3549;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 15:  \
+switch (ch1) {  \
+    case 3904:  \
+    switch (ch2) {  \
+        case 4021: return 3945;  \
+    }  \
+    return 0;  \
+    case 3906:  \
+    switch (ch2) {  \
+        case 4023: return 3907;  \
+    }  \
+    return 0;  \
+    case 3916:  \
+    switch (ch2) {  \
+        case 4023: return 3917;  \
+    }  \
+    return 0;  \
+    case 3921:  \
+    switch (ch2) {  \
+        case 4023: return 3922;  \
+    }  \
+    return 0;  \
+    case 3926:  \
+    switch (ch2) {  \
+        case 4023: return 3927;  \
+    }  \
+    return 0;  \
+    case 3931:  \
+    switch (ch2) {  \
+        case 4023: return 3932;  \
+    }  \
+    return 0;  \
+    case 3953:  \
+    switch (ch2) {  \
+        case 3954: return 3955;  \
+        case 3956: return 3957;  \
+        case 3968: return 3969;  \
+    }  \
+    return 0;  \
+    case 3984:  \
+    switch (ch2) {  \
+        case 4021: return 4025;  \
+    }  \
+    return 0;  \
+    case 3986:  \
+    switch (ch2) {  \
+        case 4023: return 3987;  \
+    }  \
+    return 0;  \
+    case 3996:  \
+    switch (ch2) {  \
+        case 4023: return 3997;  \
+    }  \
+    return 0;  \
+    case 4001:  \
+    switch (ch2) {  \
+        case 4023: return 4002;  \
+    }  \
+    return 0;  \
+    case 4006:  \
+    switch (ch2) {  \
+        case 4023: return 4007;  \
+    }  \
+    return 0;  \
+    case 4011:  \
+    switch (ch2) {  \
+        case 4023: return 4012;  \
+    }  \
+    return 0;  \
+    case 4018:  \
+    switch (ch2) {  \
+        case 3968: return 3958;  \
+    }  \
+    return 0;  \
+    case 4019:  \
+    switch (ch2) {  \
+        case 3968: return 3960;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 16:  \
+switch (ch1) {  \
+    case 4133:  \
+    switch (ch2) {  \
+        case 4142: return 4134;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 30:  \
+switch (ch1) {  \
+    case 7734:  \
+    switch (ch2) {  \
+        case 772: return 7736;  \
+    }  \
+    return 0;  \
+    case 7735:  \
+    switch (ch2) {  \
+        case 772: return 7737;  \
+    }  \
+    return 0;  \
+    case 7770:  \
+    switch (ch2) {  \
+        case 772: return 7772;  \
+    }  \
+    return 0;  \
+    case 7771:  \
+    switch (ch2) {  \
+        case 772: return 7773;  \
+    }  \
+    return 0;  \
+    case 7778:  \
+    switch (ch2) {  \
+        case 775: return 7784;  \
+    }  \
+    return 0;  \
+    case 7779:  \
+    switch (ch2) {  \
+        case 775: return 7785;  \
+    }  \
+    return 0;  \
+    case 7840:  \
+    switch (ch2) {  \
+        case 770: return 7852;  \
+        case 774: return 7862;  \
+    }  \
+    return 0;  \
+    case 7841:  \
+    switch (ch2) {  \
+        case 770: return 7853;  \
+        case 774: return 7863;  \
+    }  \
+    return 0;  \
+    case 7864:  \
+    switch (ch2) {  \
+        case 770: return 7878;  \
+    }  \
+    return 0;  \
+    case 7865:  \
+    switch (ch2) {  \
+        case 770: return 7879;  \
+    }  \
+    return 0;  \
+    case 7884:  \
+    switch (ch2) {  \
+        case 770: return 7896;  \
+    }  \
+    return 0;  \
+    case 7885:  \
+    switch (ch2) {  \
+        case 770: return 7897;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 31:  \
+switch (ch1) {  \
+    case 7936:  \
+    switch (ch2) {  \
+        case 768: return 7938;  \
+        case 769: return 7940;  \
+        case 834: return 7942;  \
+        case 837: return 8064;  \
+    }  \
+    return 0;  \
+    case 7937:  \
+    switch (ch2) {  \
+        case 768: return 7939;  \
+        case 769: return 7941;  \
+        case 834: return 7943;  \
+        case 837: return 8065;  \
+    }  \
+    return 0;  \
+    case 7938:  \
+    switch (ch2) {  \
+        case 837: return 8066;  \
+    }  \
+    return 0;  \
+    case 7939:  \
+    switch (ch2) {  \
+        case 837: return 8067;  \
+    }  \
+    return 0;  \
+    case 7940:  \
+    switch (ch2) {  \
+        case 837: return 8068;  \
+    }  \
+    return 0;  \
+    case 7941:  \
+    switch (ch2) {  \
+        case 837: return 8069;  \
+    }  \
+    return 0;  \
+    case 7942:  \
+    switch (ch2) {  \
+        case 837: return 8070;  \
+    }  \
+    return 0;  \
+    case 7943:  \
+    switch (ch2) {  \
+        case 837: return 8071;  \
+    }  \
+    return 0;  \
+    case 7944:  \
+    switch (ch2) {  \
+        case 768: return 7946;  \
+        case 769: return 7948;  \
+        case 834: return 7950;  \
+        case 837: return 8072;  \
+    }  \
+    return 0;  \
+    case 7945:  \
+    switch (ch2) {  \
+        case 768: return 7947;  \
+        case 769: return 7949;  \
+        case 834: return 7951;  \
+        case 837: return 8073;  \
+    }  \
+    return 0;  \
+    case 7946:  \
+    switch (ch2) {  \
+        case 837: return 8074;  \
+    }  \
+    return 0;  \
+    case 7947:  \
+    switch (ch2) {  \
+        case 837: return 8075;  \
+    }  \
+    return 0;  \
+    case 7948:  \
+    switch (ch2) {  \
+        case 837: return 8076;  \
+    }  \
+    return 0;  \
+    case 7949:  \
+    switch (ch2) {  \
+        case 837: return 8077;  \
+    }  \
+    return 0;  \
+    case 7950:  \
+    switch (ch2) {  \
+        case 837: return 8078;  \
+    }  \
+    return 0;  \
+    case 7951:  \
+    switch (ch2) {  \
+        case 837: return 8079;  \
+    }  \
+    return 0;  \
+    case 7952:  \
+    switch (ch2) {  \
+        case 768: return 7954;  \
+        case 769: return 7956;  \
+    }  \
+    return 0;  \
+    case 7953:  \
+    switch (ch2) {  \
+        case 768: return 7955;  \
+        case 769: return 7957;  \
+    }  \
+    return 0;  \
+    case 7960:  \
+    switch (ch2) {  \
+        case 768: return 7962;  \
+        case 769: return 7964;  \
+    }  \
+    return 0;  \
+    case 7961:  \
+    switch (ch2) {  \
+        case 768: return 7963;  \
+        case 769: return 7965;  \
+    }  \
+    return 0;  \
+    case 7968:  \
+    switch (ch2) {  \
+        case 768: return 7970;  \
+        case 769: return 7972;  \
+        case 834: return 7974;  \
+        case 837: return 8080;  \
+    }  \
+    return 0;  \
+    case 7969:  \
+    switch (ch2) {  \
+        case 768: return 7971;  \
+        case 769: return 7973;  \
+        case 834: return 7975;  \
+        case 837: return 8081;  \
+    }  \
+    return 0;  \
+    case 7970:  \
+    switch (ch2) {  \
+        case 837: return 8082;  \
+    }  \
+    return 0;  \
+    case 7971:  \
+    switch (ch2) {  \
+        case 837: return 8083;  \
+    }  \
+    return 0;  \
+    case 7972:  \
+    switch (ch2) {  \
+        case 837: return 8084;  \
+    }  \
+    return 0;  \
+    case 7973:  \
+    switch (ch2) {  \
+        case 837: return 8085;  \
+    }  \
+    return 0;  \
+    case 7974:  \
+    switch (ch2) {  \
+        case 837: return 8086;  \
+    }  \
+    return 0;  \
+    case 7975:  \
+    switch (ch2) {  \
+        case 837: return 8087;  \
+    }  \
+    return 0;  \
+    case 7976:  \
+    switch (ch2) {  \
+        case 768: return 7978;  \
+        case 769: return 7980;  \
+        case 834: return 7982;  \
+        case 837: return 8088;  \
+    }  \
+    return 0;  \
+    case 7977:  \
+    switch (ch2) {  \
+        case 768: return 7979;  \
+        case 769: return 7981;  \
+        case 834: return 7983;  \
+        case 837: return 8089;  \
+    }  \
+    return 0;  \
+    case 7978:  \
+    switch (ch2) {  \
+        case 837: return 8090;  \
+    }  \
+    return 0;  \
+    case 7979:  \
+    switch (ch2) {  \
+        case 837: return 8091;  \
+    }  \
+    return 0;  \
+    case 7980:  \
+    switch (ch2) {  \
+        case 837: return 8092;  \
+    }  \
+    return 0;  \
+    case 7981:  \
+    switch (ch2) {  \
+        case 837: return 8093;  \
+    }  \
+    return 0;  \
+    case 7982:  \
+    switch (ch2) {  \
+        case 837: return 8094;  \
+    }  \
+    return 0;  \
+    case 7983:  \
+    switch (ch2) {  \
+        case 837: return 8095;  \
+    }  \
+    return 0;  \
+    case 7984:  \
+    switch (ch2) {  \
+        case 768: return 7986;  \
+        case 769: return 7988;  \
+        case 834: return 7990;  \
+    }  \
+    return 0;  \
+    case 7985:  \
+    switch (ch2) {  \
+        case 768: return 7987;  \
+        case 769: return 7989;  \
+        case 834: return 7991;  \
+    }  \
+    return 0;  \
+    case 7992:  \
+    switch (ch2) {  \
+        case 768: return 7994;  \
+        case 769: return 7996;  \
+        case 834: return 7998;  \
+    }  \
+    return 0;  \
+    case 7993:  \
+    switch (ch2) {  \
+        case 768: return 7995;  \
+        case 769: return 7997;  \
+        case 834: return 7999;  \
+    }  \
+    return 0;  \
+    case 8000:  \
+    switch (ch2) {  \
+        case 768: return 8002;  \
+        case 769: return 8004;  \
+    }  \
+    return 0;  \
+    case 8001:  \
+    switch (ch2) {  \
+        case 768: return 8003;  \
+        case 769: return 8005;  \
+    }  \
+    return 0;  \
+    case 8008:  \
+    switch (ch2) {  \
+        case 768: return 8010;  \
+        case 769: return 8012;  \
+    }  \
+    return 0;  \
+    case 8009:  \
+    switch (ch2) {  \
+        case 768: return 8011;  \
+        case 769: return 8013;  \
+    }  \
+    return 0;  \
+    case 8016:  \
+    switch (ch2) {  \
+        case 768: return 8018;  \
+        case 769: return 8020;  \
+        case 834: return 8022;  \
+    }  \
+    return 0;  \
+    case 8017:  \
+    switch (ch2) {  \
+        case 768: return 8019;  \
+        case 769: return 8021;  \
+        case 834: return 8023;  \
+    }  \
+    return 0;  \
+    case 8025:  \
+    switch (ch2) {  \
+        case 768: return 8027;  \
+        case 769: return 8029;  \
+        case 834: return 8031;  \
+    }  \
+    return 0;  \
+    case 8032:  \
+    switch (ch2) {  \
+        case 768: return 8034;  \
+        case 769: return 8036;  \
+        case 834: return 8038;  \
+        case 837: return 8096;  \
+    }  \
+    return 0;  \
+    case 8033:  \
+    switch (ch2) {  \
+        case 768: return 8035;  \
+        case 769: return 8037;  \
+        case 834: return 8039;  \
+        case 837: return 8097;  \
+    }  \
+    return 0;  \
+    case 8034:  \
+    switch (ch2) {  \
+        case 837: return 8098;  \
+    }  \
+    return 0;  \
+    case 8035:  \
+    switch (ch2) {  \
+        case 837: return 8099;  \
+    }  \
+    return 0;  \
+    case 8036:  \
+    switch (ch2) {  \
+        case 837: return 8100;  \
+    }  \
+    return 0;  \
+    case 8037:  \
+    switch (ch2) {  \
+        case 837: return 8101;  \
+    }  \
+    return 0;  \
+    case 8038:  \
+    switch (ch2) {  \
+        case 837: return 8102;  \
+    }  \
+    return 0;  \
+    case 8039:  \
+    switch (ch2) {  \
+        case 837: return 8103;  \
+    }  \
+    return 0;  \
+    case 8040:  \
+    switch (ch2) {  \
+        case 768: return 8042;  \
+        case 769: return 8044;  \
+        case 834: return 8046;  \
+        case 837: return 8104;  \
+    }  \
+    return 0;  \
+    case 8041:  \
+    switch (ch2) {  \
+        case 768: return 8043;  \
+        case 769: return 8045;  \
+        case 834: return 8047;  \
+        case 837: return 8105;  \
+    }  \
+    return 0;  \
+    case 8042:  \
+    switch (ch2) {  \
+        case 837: return 8106;  \
+    }  \
+    return 0;  \
+    case 8043:  \
+    switch (ch2) {  \
+        case 837: return 8107;  \
+    }  \
+    return 0;  \
+    case 8044:  \
+    switch (ch2) {  \
+        case 837: return 8108;  \
+    }  \
+    return 0;  \
+    case 8045:  \
+    switch (ch2) {  \
+        case 837: return 8109;  \
+    }  \
+    return 0;  \
+    case 8046:  \
+    switch (ch2) {  \
+        case 837: return 8110;  \
+    }  \
+    return 0;  \
+    case 8047:  \
+    switch (ch2) {  \
+        case 837: return 8111;  \
+    }  \
+    return 0;  \
+    case 8048:  \
+    switch (ch2) {  \
+        case 837: return 8114;  \
+    }  \
+    return 0;  \
+    case 8052:  \
+    switch (ch2) {  \
+        case 837: return 8130;  \
+    }  \
+    return 0;  \
+    case 8060:  \
+    switch (ch2) {  \
+        case 837: return 8178;  \
+    }  \
+    return 0;  \
+    case 8118:  \
+    switch (ch2) {  \
+        case 837: return 8119;  \
+    }  \
+    return 0;  \
+    case 8127:  \
+    switch (ch2) {  \
+        case 768: return 8141;  \
+        case 769: return 8142;  \
+        case 834: return 8143;  \
+    }  \
+    return 0;  \
+    case 8134:  \
+    switch (ch2) {  \
+        case 837: return 8135;  \
+    }  \
+    return 0;  \
+    case 8182:  \
+    switch (ch2) {  \
+        case 837: return 8183;  \
+    }  \
+    return 0;  \
+    case 8190:  \
+    switch (ch2) {  \
+        case 768: return 8157;  \
+        case 769: return 8158;  \
+        case 834: return 8159;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 33:  \
+switch (ch1) {  \
+    case 8592:  \
+    switch (ch2) {  \
+        case 824: return 8602;  \
+    }  \
+    return 0;  \
+    case 8594:  \
+    switch (ch2) {  \
+        case 824: return 8603;  \
+    }  \
+    return 0;  \
+    case 8596:  \
+    switch (ch2) {  \
+        case 824: return 8622;  \
+    }  \
+    return 0;  \
+    case 8656:  \
+    switch (ch2) {  \
+        case 824: return 8653;  \
+    }  \
+    return 0;  \
+    case 8658:  \
+    switch (ch2) {  \
+        case 824: return 8655;  \
+    }  \
+    return 0;  \
+    case 8660:  \
+    switch (ch2) {  \
+        case 824: return 8654;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 34:  \
+switch (ch1) {  \
+    case 8707:  \
+    switch (ch2) {  \
+        case 824: return 8708;  \
+    }  \
+    return 0;  \
+    case 8712:  \
+    switch (ch2) {  \
+        case 824: return 8713;  \
+    }  \
+    return 0;  \
+    case 8715:  \
+    switch (ch2) {  \
+        case 824: return 8716;  \
+    }  \
+    return 0;  \
+    case 8739:  \
+    switch (ch2) {  \
+        case 824: return 8740;  \
+    }  \
+    return 0;  \
+    case 8741:  \
+    switch (ch2) {  \
+        case 824: return 8742;  \
+    }  \
+    return 0;  \
+    case 8764:  \
+    switch (ch2) {  \
+        case 824: return 8769;  \
+    }  \
+    return 0;  \
+    case 8771:  \
+    switch (ch2) {  \
+        case 824: return 8772;  \
+    }  \
+    return 0;  \
+    case 8773:  \
+    switch (ch2) {  \
+        case 824: return 8775;  \
+    }  \
+    return 0;  \
+    case 8776:  \
+    switch (ch2) {  \
+        case 824: return 8777;  \
+    }  \
+    return 0;  \
+    case 8781:  \
+    switch (ch2) {  \
+        case 824: return 8813;  \
+    }  \
+    return 0;  \
+    case 8801:  \
+    switch (ch2) {  \
+        case 824: return 8802;  \
+    }  \
+    return 0;  \
+    case 8804:  \
+    switch (ch2) {  \
+        case 824: return 8816;  \
+    }  \
+    return 0;  \
+    case 8805:  \
+    switch (ch2) {  \
+        case 824: return 8817;  \
+    }  \
+    return 0;  \
+    case 8818:  \
+    switch (ch2) {  \
+        case 824: return 8820;  \
+    }  \
+    return 0;  \
+    case 8819:  \
+    switch (ch2) {  \
+        case 824: return 8821;  \
+    }  \
+    return 0;  \
+    case 8822:  \
+    switch (ch2) {  \
+        case 824: return 8824;  \
+    }  \
+    return 0;  \
+    case 8823:  \
+    switch (ch2) {  \
+        case 824: return 8825;  \
+    }  \
+    return 0;  \
+    case 8826:  \
+    switch (ch2) {  \
+        case 824: return 8832;  \
+    }  \
+    return 0;  \
+    case 8827:  \
+    switch (ch2) {  \
+        case 824: return 8833;  \
+    }  \
+    return 0;  \
+    case 8828:  \
+    switch (ch2) {  \
+        case 824: return 8928;  \
+    }  \
+    return 0;  \
+    case 8829:  \
+    switch (ch2) {  \
+        case 824: return 8929;  \
+    }  \
+    return 0;  \
+    case 8834:  \
+    switch (ch2) {  \
+        case 824: return 8836;  \
+    }  \
+    return 0;  \
+    case 8835:  \
+    switch (ch2) {  \
+        case 824: return 8837;  \
+    }  \
+    return 0;  \
+    case 8838:  \
+    switch (ch2) {  \
+        case 824: return 8840;  \
+    }  \
+    return 0;  \
+    case 8839:  \
+    switch (ch2) {  \
+        case 824: return 8841;  \
+    }  \
+    return 0;  \
+    case 8849:  \
+    switch (ch2) {  \
+        case 824: return 8930;  \
+    }  \
+    return 0;  \
+    case 8850:  \
+    switch (ch2) {  \
+        case 824: return 8931;  \
+    }  \
+    return 0;  \
+    case 8866:  \
+    switch (ch2) {  \
+        case 824: return 8876;  \
+    }  \
+    return 0;  \
+    case 8872:  \
+    switch (ch2) {  \
+        case 824: return 8877;  \
+    }  \
+    return 0;  \
+    case 8873:  \
+    switch (ch2) {  \
+        case 824: return 8878;  \
+    }  \
+    return 0;  \
+    case 8875:  \
+    switch (ch2) {  \
+        case 824: return 8879;  \
+    }  \
+    return 0;  \
+    case 8882:  \
+    switch (ch2) {  \
+        case 824: return 8938;  \
+    }  \
+    return 0;  \
+    case 8883:  \
+    switch (ch2) {  \
+        case 824: return 8939;  \
+    }  \
+    return 0;  \
+    case 8884:  \
+    switch (ch2) {  \
+        case 824: return 8940;  \
+    }  \
+    return 0;  \
+    case 8885:  \
+    switch (ch2) {  \
+        case 824: return 8941;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 42:  \
+switch (ch1) {  \
+    case 10973:  \
+    switch (ch2) {  \
+        case 824: return 10972;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 48:  \
+switch (ch1) {  \
+    case 12358:  \
+    switch (ch2) {  \
+        case 12441: return 12436;  \
+    }  \
+    return 0;  \
+    case 12363:  \
+    switch (ch2) {  \
+        case 12441: return 12364;  \
+    }  \
+    return 0;  \
+    case 12365:  \
+    switch (ch2) {  \
+        case 12441: return 12366;  \
+    }  \
+    return 0;  \
+    case 12367:  \
+    switch (ch2) {  \
+        case 12441: return 12368;  \
+    }  \
+    return 0;  \
+    case 12369:  \
+    switch (ch2) {  \
+        case 12441: return 12370;  \
+    }  \
+    return 0;  \
+    case 12371:  \
+    switch (ch2) {  \
+        case 12441: return 12372;  \
+    }  \
+    return 0;  \
+    case 12373:  \
+    switch (ch2) {  \
+        case 12441: return 12374;  \
+    }  \
+    return 0;  \
+    case 12375:  \
+    switch (ch2) {  \
+        case 12441: return 12376;  \
+    }  \
+    return 0;  \
+    case 12377:  \
+    switch (ch2) {  \
+        case 12441: return 12378;  \
+    }  \
+    return 0;  \
+    case 12379:  \
+    switch (ch2) {  \
+        case 12441: return 12380;  \
+    }  \
+    return 0;  \
+    case 12381:  \
+    switch (ch2) {  \
+        case 12441: return 12382;  \
+    }  \
+    return 0;  \
+    case 12383:  \
+    switch (ch2) {  \
+        case 12441: return 12384;  \
+    }  \
+    return 0;  \
+    case 12385:  \
+    switch (ch2) {  \
+        case 12441: return 12386;  \
+    }  \
+    return 0;  \
+    case 12388:  \
+    switch (ch2) {  \
+        case 12441: return 12389;  \
+    }  \
+    return 0;  \
+    case 12390:  \
+    switch (ch2) {  \
+        case 12441: return 12391;  \
+    }  \
+    return 0;  \
+    case 12392:  \
+    switch (ch2) {  \
+        case 12441: return 12393;  \
+    }  \
+    return 0;  \
+    case 12399:  \
+    switch (ch2) {  \
+        case 12441: return 12400;  \
+        case 12442: return 12401;  \
+    }  \
+    return 0;  \
+    case 12402:  \
+    switch (ch2) {  \
+        case 12441: return 12403;  \
+        case 12442: return 12404;  \
+    }  \
+    return 0;  \
+    case 12405:  \
+    switch (ch2) {  \
+        case 12441: return 12406;  \
+        case 12442: return 12407;  \
+    }  \
+    return 0;  \
+    case 12408:  \
+    switch (ch2) {  \
+        case 12441: return 12409;  \
+        case 12442: return 12410;  \
+    }  \
+    return 0;  \
+    case 12411:  \
+    switch (ch2) {  \
+        case 12441: return 12412;  \
+        case 12442: return 12413;  \
+    }  \
+    return 0;  \
+    case 12445:  \
+    switch (ch2) {  \
+        case 12441: return 12446;  \
+    }  \
+    return 0;  \
+    case 12454:  \
+    switch (ch2) {  \
+        case 12441: return 12532;  \
+    }  \
+    return 0;  \
+    case 12459:  \
+    switch (ch2) {  \
+        case 12441: return 12460;  \
+    }  \
+    return 0;  \
+    case 12461:  \
+    switch (ch2) {  \
+        case 12441: return 12462;  \
+    }  \
+    return 0;  \
+    case 12463:  \
+    switch (ch2) {  \
+        case 12441: return 12464;  \
+    }  \
+    return 0;  \
+    case 12465:  \
+    switch (ch2) {  \
+        case 12441: return 12466;  \
+    }  \
+    return 0;  \
+    case 12467:  \
+    switch (ch2) {  \
+        case 12441: return 12468;  \
+    }  \
+    return 0;  \
+    case 12469:  \
+    switch (ch2) {  \
+        case 12441: return 12470;  \
+    }  \
+    return 0;  \
+    case 12471:  \
+    switch (ch2) {  \
+        case 12441: return 12472;  \
+    }  \
+    return 0;  \
+    case 12473:  \
+    switch (ch2) {  \
+        case 12441: return 12474;  \
+    }  \
+    return 0;  \
+    case 12475:  \
+    switch (ch2) {  \
+        case 12441: return 12476;  \
+    }  \
+    return 0;  \
+    case 12477:  \
+    switch (ch2) {  \
+        case 12441: return 12478;  \
+    }  \
+    return 0;  \
+    case 12479:  \
+    switch (ch2) {  \
+        case 12441: return 12480;  \
+    }  \
+    return 0;  \
+    case 12481:  \
+    switch (ch2) {  \
+        case 12441: return 12482;  \
+    }  \
+    return 0;  \
+    case 12484:  \
+    switch (ch2) {  \
+        case 12441: return 12485;  \
+    }  \
+    return 0;  \
+    case 12486:  \
+    switch (ch2) {  \
+        case 12441: return 12487;  \
+    }  \
+    return 0;  \
+    case 12488:  \
+    switch (ch2) {  \
+        case 12441: return 12489;  \
+    }  \
+    return 0;  \
+    case 12495:  \
+    switch (ch2) {  \
+        case 12441: return 12496;  \
+        case 12442: return 12497;  \
+    }  \
+    return 0;  \
+    case 12498:  \
+    switch (ch2) {  \
+        case 12441: return 12499;  \
+        case 12442: return 12500;  \
+    }  \
+    return 0;  \
+    case 12501:  \
+    switch (ch2) {  \
+        case 12441: return 12502;  \
+        case 12442: return 12503;  \
+    }  \
+    return 0;  \
+    case 12504:  \
+    switch (ch2) {  \
+        case 12441: return 12505;  \
+        case 12442: return 12506;  \
+    }  \
+    return 0;  \
+    case 12507:  \
+    switch (ch2) {  \
+        case 12441: return 12508;  \
+        case 12442: return 12509;  \
+    }  \
+    return 0;  \
+    case 12527:  \
+    switch (ch2) {  \
+        case 12441: return 12535;  \
+    }  \
+    return 0;  \
+    case 12528:  \
+    switch (ch2) {  \
+        case 12441: return 12536;  \
+    }  \
+    return 0;  \
+    case 12529:  \
+    switch (ch2) {  \
+        case 12441: return 12537;  \
+    }  \
+    return 0;  \
+    case 12530:  \
+    switch (ch2) {  \
+        case 12441: return 12538;  \
+    }  \
+    return 0;  \
+    case 12541:  \
+    switch (ch2) {  \
+        case 12441: return 12542;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 251:  \
+switch (ch1) {  \
+    case 64329:  \
+    switch (ch2) {  \
+        case 1473: return 64300;  \
+        case 1474: return 64301;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+case 465:  \
+switch (ch1) {  \
+    case 119127:  \
+    switch (ch2) {  \
+        case 119141: return 119134;  \
+    }  \
+    return 0;  \
+    case 119128:  \
+    switch (ch2) {  \
+        case 119141: return 119135;  \
+    }  \
+    return 0;  \
+    case 119135:  \
+    switch (ch2) {  \
+        case 119150: return 119136;  \
+        case 119151: return 119137;  \
+        case 119152: return 119138;  \
+        case 119153: return 119139;  \
+        case 119154: return 119140;  \
+    }  \
+    return 0;  \
+    case 119225:  \
+    switch (ch2) {  \
+        case 119141: return 119227;  \
+    }  \
+    return 0;  \
+    case 119226:  \
+    switch (ch2) {  \
+        case 119141: return 119228;  \
+    }  \
+    return 0;  \
+    case 119227:  \
+    switch (ch2) {  \
+        case 119150: return 119229;  \
+        case 119151: return 119231;  \
+    }  \
+    return 0;  \
+    case 119228:  \
+    switch (ch2) {  \
+        case 119150: return 119230;  \
+        case 119151: return 119232;  \
+    }  \
+    return 0;  \
+}  \
+return 0;  \
+}  \
+return 0;
+
+glui32 unigen_decomp_data[3247] = {
+	0x41, 0x300, 0x41, 0x301, 0x41, 0x302, 0x41, 0x303,
+	0x41, 0x308, 0x41, 0x30a, 0x43, 0x327, 0x45, 0x300,
+	0x45, 0x301, 0x45, 0x302, 0x45, 0x308, 0x49, 0x300,
+	0x49, 0x301, 0x49, 0x302, 0x49, 0x308, 0x4e, 0x303,
+	0x4f, 0x300, 0x4f, 0x301, 0x4f, 0x302, 0x4f, 0x303,
+	0x4f, 0x308, 0x55, 0x300, 0x55, 0x301, 0x55, 0x302,
+	0x55, 0x308, 0x59, 0x301, 0x61, 0x300, 0x61, 0x301,
+	0x61, 0x302, 0x61, 0x303, 0x61, 0x308, 0x61, 0x30a,
+	0x63, 0x327, 0x65, 0x300, 0x65, 0x301, 0x65, 0x302,
+	0x65, 0x308, 0x69, 0x300, 0x69, 0x301, 0x69, 0x302,
+	0x69, 0x308, 0x6e, 0x303, 0x6f, 0x300, 0x6f, 0x301,
+	0x6f, 0x302, 0x6f, 0x303, 0x6f, 0x308, 0x75, 0x300,
+	0x75, 0x301, 0x75, 0x302, 0x75, 0x308, 0x79, 0x301,
+	0x79, 0x308, 0x41, 0x304, 0x61, 0x304, 0x41, 0x306,
+	0x61, 0x306, 0x41, 0x328, 0x61, 0x328, 0x43, 0x301,
+	0x63, 0x301, 0x43, 0x302, 0x63, 0x302, 0x43, 0x307,
+	0x63, 0x307, 0x43, 0x30c, 0x63, 0x30c, 0x44, 0x30c,
+	0x64, 0x30c, 0x45, 0x304, 0x65, 0x304, 0x45, 0x306,
+	0x65, 0x306, 0x45, 0x307, 0x65, 0x307, 0x45, 0x328,
+	0x65, 0x328, 0x45, 0x30c, 0x65, 0x30c, 0x47, 0x302,
+	0x67, 0x302, 0x47, 0x306, 0x67, 0x306, 0x47, 0x307,
+	0x67, 0x307, 0x47, 0x327, 0x67, 0x327, 0x48, 0x302,
+	0x68, 0x302, 0x49, 0x303, 0x69, 0x303, 0x49, 0x304,
+	0x69, 0x304, 0x49, 0x306, 0x69, 0x306, 0x49, 0x328,
+	0x69, 0x328, 0x49, 0x307, 0x4a, 0x302, 0x6a, 0x302,
+	0x4b, 0x327, 0x6b, 0x327, 0x4c, 0x301, 0x6c, 0x301,
+	0x4c, 0x327, 0x6c, 0x327, 0x4c, 0x30c, 0x6c, 0x30c,
+	0x4e, 0x301, 0x6e, 0x301, 0x4e, 0x327, 0x6e, 0x327,
+	0x4e, 0x30c, 0x6e, 0x30c, 0x4f, 0x304, 0x6f, 0x304,
+	0x4f, 0x306, 0x6f, 0x306, 0x4f, 0x30b, 0x6f, 0x30b,
+	0x52, 0x301, 0x72, 0x301, 0x52, 0x327, 0x72, 0x327,
+	0x52, 0x30c, 0x72, 0x30c, 0x53, 0x301, 0x73, 0x301,
+	0x53, 0x302, 0x73, 0x302, 0x53, 0x327, 0x73, 0x327,
+	0x53, 0x30c, 0x73, 0x30c, 0x54, 0x327, 0x74, 0x327,
+	0x54, 0x30c, 0x74, 0x30c, 0x55, 0x303, 0x75, 0x303,
+	0x55, 0x304, 0x75, 0x304, 0x55, 0x306, 0x75, 0x306,
+	0x55, 0x30a, 0x75, 0x30a, 0x55, 0x30b, 0x75, 0x30b,
+	0x55, 0x328, 0x75, 0x328, 0x57, 0x302, 0x77, 0x302,
+	0x59, 0x302, 0x79, 0x302, 0x59, 0x308, 0x5a, 0x301,
+	0x7a, 0x301, 0x5a, 0x307, 0x7a, 0x307, 0x5a, 0x30c,
+	0x7a, 0x30c, 0x4f, 0x31b, 0x6f, 0x31b, 0x55, 0x31b,
+	0x75, 0x31b, 0x41, 0x30c, 0x61, 0x30c, 0x49, 0x30c,
+	0x69, 0x30c, 0x4f, 0x30c, 0x6f, 0x30c, 0x55, 0x30c,
+	0x75, 0x30c, 0x55, 0x308, 0x304, 0x75, 0x308, 0x304,
+	0x55, 0x308, 0x301, 0x75, 0x308, 0x301, 0x55, 0x308,
+	0x30c, 0x75, 0x308, 0x30c, 0x55, 0x308, 0x300, 0x75,
+	0x308, 0x300, 0x41, 0x308, 0x304, 0x61, 0x308, 0x304,
+	0x41, 0x307, 0x304, 0x61, 0x307, 0x304, 0xc6, 0x304,
+	0xe6, 0x304, 0x47, 0x30c, 0x67, 0x30c, 0x4b, 0x30c,
+	0x6b, 0x30c, 0x4f, 0x328, 0x6f, 0x328, 0x4f, 0x328,
+	0x304, 0x6f, 0x328, 0x304, 0x1b7, 0x30c, 0x292, 0x30c,
+	0x6a, 0x30c, 0x47, 0x301, 0x67, 0x301, 0x4e, 0x300,
+	0x6e, 0x300, 0x41, 0x30a, 0x301, 0x61, 0x30a, 0x301,
+	0xc6, 0x301, 0xe6, 0x301, 0xd8, 0x301, 0xf8, 0x301,
+	0x41, 0x30f, 0x61, 0x30f, 0x41, 0x311, 0x61, 0x311,
+	0x45, 0x30f, 0x65, 0x30f, 0x45, 0x311, 0x65, 0x311,
+	0x49, 0x30f, 0x69, 0x30f, 0x49, 0x311, 0x69, 0x311,
+	0x4f, 0x30f, 0x6f, 0x30f, 0x4f, 0x311, 0x6f, 0x311,
+	0x52, 0x30f, 0x72, 0x30f, 0x52, 0x311, 0x72, 0x311,
+	0x55, 0x30f, 0x75, 0x30f, 0x55, 0x311, 0x75, 0x311,
+	0x53, 0x326, 0x73, 0x326, 0x54, 0x326, 0x74, 0x326,
+	0x48, 0x30c, 0x68, 0x30c, 0x41, 0x307, 0x61, 0x307,
+	0x45, 0x327, 0x65, 0x327, 0x4f, 0x308, 0x304, 0x6f,
+	0x308, 0x304, 0x4f, 0x303, 0x304, 0x6f, 0x303, 0x304,
+	0x4f, 0x307, 0x6f, 0x307, 0x4f, 0x307, 0x304, 0x6f,
+	0x307, 0x304, 0x59, 0x304, 0x79, 0x304, 0x300, 0x301,
+	0x313, 0x308, 0x301, 0x2b9, 0x3b, 0xa8, 0x301, 0x391,
+	0x301, 0xb7, 0x395, 0x301, 0x397, 0x301, 0x399, 0x301,
+	0x39f, 0x301, 0x3a5, 0x301, 0x3a9, 0x301, 0x3b9, 0x308,
+	0x301, 0x399, 0x308, 0x3a5, 0x308, 0x3b1, 0x301, 0x3b5,
+	0x301, 0x3b7, 0x301, 0x3b9, 0x301, 0x3c5, 0x308, 0x301,
+	0x3b9, 0x308, 0x3c5, 0x308, 0x3bf, 0x301, 0x3c5, 0x301,
+	0x3c9, 0x301, 0x3d2, 0x301, 0x3d2, 0x308, 0x415, 0x300,
+	0x415, 0x308, 0x413, 0x301, 0x406, 0x308, 0x41a, 0x301,
+	0x418, 0x300, 0x423, 0x306, 0x418, 0x306, 0x438, 0x306,
+	0x435, 0x300, 0x435, 0x308, 0x433, 0x301, 0x456, 0x308,
+	0x43a, 0x301, 0x438, 0x300, 0x443, 0x306, 0x474, 0x30f,
+	0x475, 0x30f, 0x416, 0x306, 0x436, 0x306, 0x410, 0x306,
+	0x430, 0x306, 0x410, 0x308, 0x430, 0x308, 0x415, 0x306,
+	0x435, 0x306, 0x4d8, 0x308, 0x4d9, 0x308, 0x416, 0x308,
+	0x436, 0x308, 0x417, 0x308, 0x437, 0x308, 0x418, 0x304,
+	0x438, 0x304, 0x418, 0x308, 0x438, 0x308, 0x41e, 0x308,
+	0x43e, 0x308, 0x4e8, 0x308, 0x4e9, 0x308, 0x42d, 0x308,
+	0x44d, 0x308, 0x423, 0x304, 0x443, 0x304, 0x423, 0x308,
+	0x443, 0x308, 0x423, 0x30b, 0x443, 0x30b, 0x427, 0x308,
+	0x447, 0x308, 0x42b, 0x308, 0x44b, 0x308, 0x627, 0x653,
+	0x627, 0x654, 0x648, 0x654, 0x627, 0x655, 0x64a, 0x654,
+	0x6d5, 0x654, 0x6c1, 0x654, 0x6d2, 0x654, 0x928, 0x93c,
+	0x930, 0x93c, 0x933, 0x93c, 0x915, 0x93c, 0x916, 0x93c,
+	0x917, 0x93c, 0x91c, 0x93c, 0x921, 0x93c, 0x922, 0x93c,
+	0x92b, 0x93c, 0x92f, 0x93c, 0x9c7, 0x9be, 0x9c7, 0x9d7,
+	0x9a1, 0x9bc, 0x9a2, 0x9bc, 0x9af, 0x9bc, 0xa32, 0xa3c,
+	0xa38, 0xa3c, 0xa16, 0xa3c, 0xa17, 0xa3c, 0xa1c, 0xa3c,
+	0xa2b, 0xa3c, 0xb47, 0xb56, 0xb47, 0xb3e, 0xb47, 0xb57,
+	0xb21, 0xb3c, 0xb22, 0xb3c, 0xb92, 0xbd7, 0xbc6, 0xbbe,
+	0xbc7, 0xbbe, 0xbc6, 0xbd7, 0xc46, 0xc56, 0xcbf, 0xcd5,
+	0xcc6, 0xcd5, 0xcc6, 0xcd6, 0xcc6, 0xcc2, 0xcc6, 0xcc2,
+	0xcd5, 0xd46, 0xd3e, 0xd47, 0xd3e, 0xd46, 0xd57, 0xdd9,
+	0xdca, 0xdd9, 0xdcf, 0xdd9, 0xdcf, 0xdca, 0xdd9, 0xddf,
+	0xf42, 0xfb7, 0xf4c, 0xfb7, 0xf51, 0xfb7, 0xf56, 0xfb7,
+	0xf5b, 0xfb7, 0xf40, 0xfb5, 0xf71, 0xf72, 0xf71, 0xf74,
+	0xfb2, 0xf80, 0xfb3, 0xf80, 0xf71, 0xf80, 0xf92, 0xfb7,
+	0xf9c, 0xfb7, 0xfa1, 0xfb7, 0xfa6, 0xfb7, 0xfab, 0xfb7,
+	0xf90, 0xfb5, 0x1025, 0x102e, 0x41, 0x325, 0x61, 0x325,
+	0x42, 0x307, 0x62, 0x307, 0x42, 0x323, 0x62, 0x323,
+	0x42, 0x331, 0x62, 0x331, 0x43, 0x327, 0x301, 0x63,
+	0x327, 0x301, 0x44, 0x307, 0x64, 0x307, 0x44, 0x323,
+	0x64, 0x323, 0x44, 0x331, 0x64, 0x331, 0x44, 0x327,
+	0x64, 0x327, 0x44, 0x32d, 0x64, 0x32d, 0x45, 0x304,
+	0x300, 0x65, 0x304, 0x300, 0x45, 0x304, 0x301, 0x65,
+	0x304, 0x301, 0x45, 0x32d, 0x65, 0x32d, 0x45, 0x330,
+	0x65, 0x330, 0x45, 0x327, 0x306, 0x65, 0x327, 0x306,
+	0x46, 0x307, 0x66, 0x307, 0x47, 0x304, 0x67, 0x304,
+	0x48, 0x307, 0x68, 0x307, 0x48, 0x323, 0x68, 0x323,
+	0x48, 0x308, 0x68, 0x308, 0x48, 0x327, 0x68, 0x327,
+	0x48, 0x32e, 0x68, 0x32e, 0x49, 0x330, 0x69, 0x330,
+	0x49, 0x308, 0x301, 0x69, 0x308, 0x301, 0x4b, 0x301,
+	0x6b, 0x301, 0x4b, 0x323, 0x6b, 0x323, 0x4b, 0x331,
+	0x6b, 0x331, 0x4c, 0x323, 0x6c, 0x323, 0x4c, 0x323,
+	0x304, 0x6c, 0x323, 0x304, 0x4c, 0x331, 0x6c, 0x331,
+	0x4c, 0x32d, 0x6c, 0x32d, 0x4d, 0x301, 0x6d, 0x301,
+	0x4d, 0x307, 0x6d, 0x307, 0x4d, 0x323, 0x6d, 0x323,
+	0x4e, 0x307, 0x6e, 0x307, 0x4e, 0x323, 0x6e, 0x323,
+	0x4e, 0x331, 0x6e, 0x331, 0x4e, 0x32d, 0x6e, 0x32d,
+	0x4f, 0x303, 0x301, 0x6f, 0x303, 0x301, 0x4f, 0x303,
+	0x308, 0x6f, 0x303, 0x308, 0x4f, 0x304, 0x300, 0x6f,
+	0x304, 0x300, 0x4f, 0x304, 0x301, 0x6f, 0x304, 0x301,
+	0x50, 0x301, 0x70, 0x301, 0x50, 0x307, 0x70, 0x307,
+	0x52, 0x307, 0x72, 0x307, 0x52, 0x323, 0x72, 0x323,
+	0x52, 0x323, 0x304, 0x72, 0x323, 0x304, 0x52, 0x331,
+	0x72, 0x331, 0x53, 0x307, 0x73, 0x307, 0x53, 0x323,
+	0x73, 0x323, 0x53, 0x301, 0x307, 0x73, 0x301, 0x307,
+	0x53, 0x30c, 0x307, 0x73, 0x30c, 0x307, 0x53, 0x323,
+	0x307, 0x73, 0x323, 0x307, 0x54, 0x307, 0x74, 0x307,
+	0x54, 0x323, 0x74, 0x323, 0x54, 0x331, 0x74, 0x331,
+	0x54, 0x32d, 0x74, 0x32d, 0x55, 0x324, 0x75, 0x324,
+	0x55, 0x330, 0x75, 0x330, 0x55, 0x32d, 0x75, 0x32d,
+	0x55, 0x303, 0x301, 0x75, 0x303, 0x301, 0x55, 0x304,
+	0x308, 0x75, 0x304, 0x308, 0x56, 0x303, 0x76, 0x303,
+	0x56, 0x323, 0x76, 0x323, 0x57, 0x300, 0x77, 0x300,
+	0x57, 0x301, 0x77, 0x301, 0x57, 0x308, 0x77, 0x308,
+	0x57, 0x307, 0x77, 0x307, 0x57, 0x323, 0x77, 0x323,
+	0x58, 0x307, 0x78, 0x307, 0x58, 0x308, 0x78, 0x308,
+	0x59, 0x307, 0x79, 0x307, 0x5a, 0x302, 0x7a, 0x302,
+	0x5a, 0x323, 0x7a, 0x323, 0x5a, 0x331, 0x7a, 0x331,
+	0x68, 0x331, 0x74, 0x308, 0x77, 0x30a, 0x79, 0x30a,
+	0x17f, 0x307, 0x41, 0x323, 0x61, 0x323, 0x41, 0x309,
+	0x61, 0x309, 0x41, 0x302, 0x301, 0x61, 0x302, 0x301,
+	0x41, 0x302, 0x300, 0x61, 0x302, 0x300, 0x41, 0x302,
+	0x309, 0x61, 0x302, 0x309, 0x41, 0x302, 0x303, 0x61,
+	0x302, 0x303, 0x41, 0x323, 0x302, 0x61, 0x323, 0x302,
+	0x41, 0x306, 0x301, 0x61, 0x306, 0x301, 0x41, 0x306,
+	0x300, 0x61, 0x306, 0x300, 0x41, 0x306, 0x309, 0x61,
+	0x306, 0x309, 0x41, 0x306, 0x303, 0x61, 0x306, 0x303,
+	0x41, 0x323, 0x306, 0x61, 0x323, 0x306, 0x45, 0x323,
+	0x65, 0x323, 0x45, 0x309, 0x65, 0x309, 0x45, 0x303,
+	0x65, 0x303, 0x45, 0x302, 0x301, 0x65, 0x302, 0x301,
+	0x45, 0x302, 0x300, 0x65, 0x302, 0x300, 0x45, 0x302,
+	0x309, 0x65, 0x302, 0x309, 0x45, 0x302, 0x303, 0x65,
+	0x302, 0x303, 0x45, 0x323, 0x302, 0x65, 0x323, 0x302,
+	0x49, 0x309, 0x69, 0x309, 0x49, 0x323, 0x69, 0x323,
+	0x4f, 0x323, 0x6f, 0x323, 0x4f, 0x309, 0x6f, 0x309,
+	0x4f, 0x302, 0x301, 0x6f, 0x302, 0x301, 0x4f, 0x302,
+	0x300, 0x6f, 0x302, 0x300, 0x4f, 0x302, 0x309, 0x6f,
+	0x302, 0x309, 0x4f, 0x302, 0x303, 0x6f, 0x302, 0x303,
+	0x4f, 0x323, 0x302, 0x6f, 0x323, 0x302, 0x4f, 0x31b,
+	0x301, 0x6f, 0x31b, 0x301, 0x4f, 0x31b, 0x300, 0x6f,
+	0x31b, 0x300, 0x4f, 0x31b, 0x309, 0x6f, 0x31b, 0x309,
+	0x4f, 0x31b, 0x303, 0x6f, 0x31b, 0x303, 0x4f, 0x31b,
+	0x323, 0x6f, 0x31b, 0x323, 0x55, 0x323, 0x75, 0x323,
+	0x55, 0x309, 0x75, 0x309, 0x55, 0x31b, 0x301, 0x75,
+	0x31b, 0x301, 0x55, 0x31b, 0x300, 0x75, 0x31b, 0x300,
+	0x55, 0x31b, 0x309, 0x75, 0x31b, 0x309, 0x55, 0x31b,
+	0x303, 0x75, 0x31b, 0x303, 0x55, 0x31b, 0x323, 0x75,
+	0x31b, 0x323, 0x59, 0x300, 0x79, 0x300, 0x59, 0x323,
+	0x79, 0x323, 0x59, 0x309, 0x79, 0x309, 0x59, 0x303,
+	0x79, 0x303, 0x3b1, 0x313, 0x3b1, 0x314, 0x3b1, 0x313,
+	0x300, 0x3b1, 0x314, 0x300, 0x3b1, 0x313, 0x301, 0x3b1,
+	0x314, 0x301, 0x3b1, 0x313, 0x342, 0x3b1, 0x314, 0x342,
+	0x391, 0x313, 0x391, 0x314, 0x391, 0x313, 0x300, 0x391,
+	0x314, 0x300, 0x391, 0x313, 0x301, 0x391, 0x314, 0x301,
+	0x391, 0x313, 0x342, 0x391, 0x314, 0x342, 0x3b5, 0x313,
+	0x3b5, 0x314, 0x3b5, 0x313, 0x300, 0x3b5, 0x314, 0x300,
+	0x3b5, 0x313, 0x301, 0x3b5, 0x314, 0x301, 0x395, 0x313,
+	0x395, 0x314, 0x395, 0x313, 0x300, 0x395, 0x314, 0x300,
+	0x395, 0x313, 0x301, 0x395, 0x314, 0x301, 0x3b7, 0x313,
+	0x3b7, 0x314, 0x3b7, 0x313, 0x300, 0x3b7, 0x314, 0x300,
+	0x3b7, 0x313, 0x301, 0x3b7, 0x314, 0x301, 0x3b7, 0x313,
+	0x342, 0x3b7, 0x314, 0x342, 0x397, 0x313, 0x397, 0x314,
+	0x397, 0x313, 0x300, 0x397, 0x314, 0x300, 0x397, 0x313,
+	0x301, 0x397, 0x314, 0x301, 0x397, 0x313, 0x342, 0x397,
+	0x314, 0x342, 0x3b9, 0x313, 0x3b9, 0x314, 0x3b9, 0x313,
+	0x300, 0x3b9, 0x314, 0x300, 0x3b9, 0x313, 0x301, 0x3b9,
+	0x314, 0x301, 0x3b9, 0x313, 0x342, 0x3b9, 0x314, 0x342,
+	0x399, 0x313, 0x399, 0x314, 0x399, 0x313, 0x300, 0x399,
+	0x314, 0x300, 0x399, 0x313, 0x301, 0x399, 0x314, 0x301,
+	0x399, 0x313, 0x342, 0x399, 0x314, 0x342, 0x3bf, 0x313,
+	0x3bf, 0x314, 0x3bf, 0x313, 0x300, 0x3bf, 0x314, 0x300,
+	0x3bf, 0x313, 0x301, 0x3bf, 0x314, 0x301, 0x39f, 0x313,
+	0x39f, 0x314, 0x39f, 0x313, 0x300, 0x39f, 0x314, 0x300,
+	0x39f, 0x313, 0x301, 0x39f, 0x314, 0x301, 0x3c5, 0x313,
+	0x3c5, 0x314, 0x3c5, 0x313, 0x300, 0x3c5, 0x314, 0x300,
+	0x3c5, 0x313, 0x301, 0x3c5, 0x314, 0x301, 0x3c5, 0x313,
+	0x342, 0x3c5, 0x314, 0x342, 0x3a5, 0x314, 0x3a5, 0x314,
+	0x300, 0x3a5, 0x314, 0x301, 0x3a5, 0x314, 0x342, 0x3c9,
+	0x313, 0x3c9, 0x314, 0x3c9, 0x313, 0x300, 0x3c9, 0x314,
+	0x300, 0x3c9, 0x313, 0x301, 0x3c9, 0x314, 0x301, 0x3c9,
+	0x313, 0x342, 0x3c9, 0x314, 0x342, 0x3a9, 0x313, 0x3a9,
+	0x314, 0x3a9, 0x313, 0x300, 0x3a9, 0x314, 0x300, 0x3a9,
+	0x313, 0x301, 0x3a9, 0x314, 0x301, 0x3a9, 0x313, 0x342,
+	0x3a9, 0x314, 0x342, 0x3b1, 0x300, 0x3b1, 0x301, 0x3b5,
+	0x300, 0x3b5, 0x301, 0x3b7, 0x300, 0x3b7, 0x301, 0x3b9,
+	0x300, 0x3b9, 0x301, 0x3bf, 0x300, 0x3bf, 0x301, 0x3c5,
+	0x300, 0x3c5, 0x301, 0x3c9, 0x300, 0x3c9, 0x301, 0x3b1,
+	0x313, 0x345, 0x3b1, 0x314, 0x345, 0x3b1, 0x313, 0x300,
+	0x345, 0x3b1, 0x314, 0x300, 0x345, 0x3b1, 0x313, 0x301,
+	0x345, 0x3b1, 0x314, 0x301, 0x345, 0x3b1, 0x313, 0x342,
+	0x345, 0x3b1, 0x314, 0x342, 0x345, 0x391, 0x313, 0x345,
+	0x391, 0x314, 0x345, 0x391, 0x313, 0x300, 0x345, 0x391,
+	0x314, 0x300, 0x345, 0x391, 0x313, 0x301, 0x345, 0x391,
+	0x314, 0x301, 0x345, 0x391, 0x313, 0x342, 0x345, 0x391,
+	0x314, 0x342, 0x345, 0x3b7, 0x313, 0x345, 0x3b7, 0x314,
+	0x345, 0x3b7, 0x313, 0x300, 0x345, 0x3b7, 0x314, 0x300,
+	0x345, 0x3b7, 0x313, 0x301, 0x345, 0x3b7, 0x314, 0x301,
+	0x345, 0x3b7, 0x313, 0x342, 0x345, 0x3b7, 0x314, 0x342,
+	0x345, 0x397, 0x313, 0x345, 0x397, 0x314, 0x345, 0x397,
+	0x313, 0x300, 0x345, 0x397, 0x314, 0x300, 0x345, 0x397,
+	0x313, 0x301, 0x345, 0x397, 0x314, 0x301, 0x345, 0x397,
+	0x313, 0x342, 0x345, 0x397, 0x314, 0x342, 0x345, 0x3c9,
+	0x313, 0x345, 0x3c9, 0x314, 0x345, 0x3c9, 0x313, 0x300,
+	0x345, 0x3c9, 0x314, 0x300, 0x345, 0x3c9, 0x313, 0x301,
+	0x345, 0x3c9, 0x314, 0x301, 0x345, 0x3c9, 0x313, 0x342,
+	0x345, 0x3c9, 0x314, 0x342, 0x345, 0x3a9, 0x313, 0x345,
+	0x3a9, 0x314, 0x345, 0x3a9, 0x313, 0x300, 0x345, 0x3a9,
+	0x314, 0x300, 0x345, 0x3a9, 0x313, 0x301, 0x345, 0x3a9,
+	0x314, 0x301, 0x345, 0x3a9, 0x313, 0x342, 0x345, 0x3a9,
+	0x314, 0x342, 0x345, 0x3b1, 0x306, 0x3b1, 0x304, 0x3b1,
+	0x300, 0x345, 0x3b1, 0x345, 0x3b1, 0x301, 0x345, 0x3b1,
+	0x342, 0x3b1, 0x342, 0x345, 0x391, 0x306, 0x391, 0x304,
+	0x391, 0x300, 0x391, 0x301, 0x391, 0x345, 0x3b9, 0xa8,
+	0x342, 0x3b7, 0x300, 0x345, 0x3b7, 0x345, 0x3b7, 0x301,
+	0x345, 0x3b7, 0x342, 0x3b7, 0x342, 0x345, 0x395, 0x300,
+	0x395, 0x301, 0x397, 0x300, 0x397, 0x301, 0x397, 0x345,
+	0x1fbf, 0x300, 0x1fbf, 0x301, 0x1fbf, 0x342, 0x3b9, 0x306,
+	0x3b9, 0x304, 0x3b9, 0x308, 0x300, 0x3b9, 0x308, 0x301,
+	0x3b9, 0x342, 0x3b9, 0x308, 0x342, 0x399, 0x306, 0x399,
+	0x304, 0x399, 0x300, 0x399, 0x301, 0x1ffe, 0x300, 0x1ffe,
+	0x301, 0x1ffe, 0x342, 0x3c5, 0x306, 0x3c5, 0x304, 0x3c5,
+	0x308, 0x300, 0x3c5, 0x308, 0x301, 0x3c1, 0x313, 0x3c1,
+	0x314, 0x3c5, 0x342, 0x3c5, 0x308, 0x342, 0x3a5, 0x306,
+	0x3a5, 0x304, 0x3a5, 0x300, 0x3a5, 0x301, 0x3a1, 0x314,
+	0xa8, 0x300, 0xa8, 0x301, 0x60, 0x3c9, 0x300, 0x345,
+	0x3c9, 0x345, 0x3c9, 0x301, 0x345, 0x3c9, 0x342, 0x3c9,
+	0x342, 0x345, 0x39f, 0x300, 0x39f, 0x301, 0x3a9, 0x300,
+	0x3a9, 0x301, 0x3a9, 0x345, 0xb4, 0x2002, 0x2003, 0x3a9,
+	0x4b, 0x41, 0x30a, 0x2190, 0x338, 0x2192, 0x338, 0x2194,
+	0x338, 0x21d0, 0x338, 0x21d4, 0x338, 0x21d2, 0x338, 0x2203,
+	0x338, 0x2208, 0x338, 0x220b, 0x338, 0x2223, 0x338, 0x2225,
+	0x338, 0x223c, 0x338, 0x2243, 0x338, 0x2245, 0x338, 0x2248,
+	0x338, 0x3d, 0x338, 0x2261, 0x338, 0x224d, 0x338, 0x3c,
+	0x338, 0x3e, 0x338, 0x2264, 0x338, 0x2265, 0x338, 0x2272,
+	0x338, 0x2273, 0x338, 0x2276, 0x338, 0x2277, 0x338, 0x227a,
+	0x338, 0x227b, 0x338, 0x2282, 0x338, 0x2283, 0x338, 0x2286,
+	0x338, 0x2287, 0x338, 0x22a2, 0x338, 0x22a8, 0x338, 0x22a9,
+	0x338, 0x22ab, 0x338, 0x227c, 0x338, 0x227d, 0x338, 0x2291,
+	0x338, 0x2292, 0x338, 0x22b2, 0x338, 0x22b3, 0x338, 0x22b4,
+	0x338, 0x22b5, 0x338, 0x3008, 0x3009, 0x2add, 0x338, 0x304b,
+	0x3099, 0x304d, 0x3099, 0x304f, 0x3099, 0x3051, 0x3099, 0x3053,
+	0x3099, 0x3055, 0x3099, 0x3057, 0x3099, 0x3059, 0x3099, 0x305b,
+	0x3099, 0x305d, 0x3099, 0x305f, 0x3099, 0x3061, 0x3099, 0x3064,
+	0x3099, 0x3066, 0x3099, 0x3068, 0x3099, 0x306f, 0x3099, 0x306f,
+	0x309a, 0x3072, 0x3099, 0x3072, 0x309a, 0x3075, 0x3099, 0x3075,
+	0x309a, 0x3078, 0x3099, 0x3078, 0x309a, 0x307b, 0x3099, 0x307b,
+	0x309a, 0x3046, 0x3099, 0x309d, 0x3099, 0x30ab, 0x3099, 0x30ad,
+	0x3099, 0x30af, 0x3099, 0x30b1, 0x3099, 0x30b3, 0x3099, 0x30b5,
+	0x3099, 0x30b7, 0x3099, 0x30b9, 0x3099, 0x30bb, 0x3099, 0x30bd,
+	0x3099, 0x30bf, 0x3099, 0x30c1, 0x3099, 0x30c4, 0x3099, 0x30c6,
+	0x3099, 0x30c8, 0x3099, 0x30cf, 0x3099, 0x30cf, 0x309a, 0x30d2,
+	0x3099, 0x30d2, 0x309a, 0x30d5, 0x3099, 0x30d5, 0x309a, 0x30d8,
+	0x3099, 0x30d8, 0x309a, 0x30db, 0x3099, 0x30db, 0x309a, 0x30a6,
+	0x3099, 0x30ef, 0x3099, 0x30f0, 0x3099, 0x30f1, 0x3099, 0x30f2,
+	0x3099, 0x30fd, 0x3099, 0x8c48, 0x66f4, 0x8eca, 0x8cc8, 0x6ed1,
+	0x4e32, 0x53e5, 0x9f9c, 0x9f9c, 0x5951, 0x91d1, 0x5587, 0x5948,
+	0x61f6, 0x7669, 0x7f85, 0x863f, 0x87ba, 0x88f8, 0x908f, 0x6a02,
+	0x6d1b, 0x70d9, 0x73de, 0x843d, 0x916a, 0x99f1, 0x4e82, 0x5375,
+	0x6b04, 0x721b, 0x862d, 0x9e1e, 0x5d50, 0x6feb, 0x85cd, 0x8964,
+	0x62c9, 0x81d8, 0x881f, 0x5eca, 0x6717, 0x6d6a, 0x72fc, 0x90ce,
+	0x4f86, 0x51b7, 0x52de, 0x64c4, 0x6ad3, 0x7210, 0x76e7, 0x8001,
+	0x8606, 0x865c, 0x8def, 0x9732, 0x9b6f, 0x9dfa, 0x788c, 0x797f,
+	0x7da0, 0x83c9, 0x9304, 0x9e7f, 0x8ad6, 0x58df, 0x5f04, 0x7c60,
+	0x807e, 0x7262, 0x78ca, 0x8cc2, 0x96f7, 0x58d8, 0x5c62, 0x6a13,
+	0x6dda, 0x6f0f, 0x7d2f, 0x7e37, 0x964b, 0x52d2, 0x808b, 0x51dc,
+	0x51cc, 0x7a1c, 0x7dbe, 0x83f1, 0x9675, 0x8b80, 0x62cf, 0x6a02,
+	0x8afe, 0x4e39, 0x5be7, 0x6012, 0x7387, 0x7570, 0x5317, 0x78fb,
+	0x4fbf, 0x5fa9, 0x4e0d, 0x6ccc, 0x6578, 0x7d22, 0x53c3, 0x585e,
+	0x7701, 0x8449, 0x8aaa, 0x6bba, 0x8fb0, 0x6c88, 0x62fe, 0x82e5,
+	0x63a0, 0x7565, 0x4eae, 0x5169, 0x51c9, 0x6881, 0x7ce7, 0x826f,
+	0x8ad2, 0x91cf, 0x52f5, 0x5442, 0x5973, 0x5eec, 0x65c5, 0x6ffe,
+	0x792a, 0x95ad, 0x9a6a, 0x9e97, 0x9ece, 0x529b, 0x66c6, 0x6b77,
+	0x8f62, 0x5e74, 0x6190, 0x6200, 0x649a, 0x6f23, 0x7149, 0x7489,
+	0x79ca, 0x7df4, 0x806f, 0x8f26, 0x84ee, 0x9023, 0x934a, 0x5217,
+	0x52a3, 0x54bd, 0x70c8, 0x88c2, 0x8aaa, 0x5ec9, 0x5ff5, 0x637b,
+	0x6bae, 0x7c3e, 0x7375, 0x4ee4, 0x56f9, 0x5be7, 0x5dba, 0x601c,
+	0x73b2, 0x7469, 0x7f9a, 0x8046, 0x9234, 0x96f6, 0x9748, 0x9818,
+	0x4f8b, 0x79ae, 0x91b4, 0x96b8, 0x60e1, 0x4e86, 0x50da, 0x5bee,
+	0x5c3f, 0x6599, 0x6a02, 0x71ce, 0x7642, 0x84fc, 0x907c, 0x9f8d,
+	0x6688, 0x962e, 0x5289, 0x677b, 0x67f3, 0x6d41, 0x6e9c, 0x7409,
+	0x7559, 0x786b, 0x7d10, 0x985e, 0x516d, 0x622e, 0x9678, 0x502b,
+	0x5d19, 0x6dea, 0x8f2a, 0x5f8b, 0x6144, 0x6817, 0x7387, 0x9686,
+	0x5229, 0x540f, 0x5c65, 0x6613, 0x674e, 0x68a8, 0x6ce5, 0x7406,
+	0x75e2, 0x7f79, 0x88cf, 0x88e1, 0x91cc, 0x96e2, 0x533f, 0x6eba,
+	0x541d, 0x71d0, 0x7498, 0x85fa, 0x96a3, 0x9c57, 0x9e9f, 0x6797,
+	0x6dcb, 0x81e8, 0x7acb, 0x7b20, 0x7c92, 0x72c0, 0x7099, 0x8b58,
+	0x4ec0, 0x8336, 0x523a, 0x5207, 0x5ea6, 0x62d3, 0x7cd6, 0x5b85,
+	0x6d1e, 0x66b4, 0x8f3b, 0x884c, 0x964d, 0x898b, 0x5ed3, 0x5140,
+	0x55c0, 0x585a, 0x6674, 0x51de, 0x732a, 0x76ca, 0x793c, 0x795e,
+	0x7965, 0x798f, 0x9756, 0x7cbe, 0x7fbd, 0x8612, 0x8af8, 0x9038,
+	0x90fd, 0x98ef, 0x98fc, 0x9928, 0x9db4, 0x4fae, 0x50e7, 0x514d,
+	0x52c9, 0x52e4, 0x5351, 0x559d, 0x5606, 0x5668, 0x5840, 0x58a8,
+	0x5c64, 0x5c6e, 0x6094, 0x6168, 0x618e, 0x61f2, 0x654f, 0x65e2,
+	0x6691, 0x6885, 0x6d77, 0x6e1a, 0x6f22, 0x716e, 0x722b, 0x7422,
+	0x7891, 0x793e, 0x7949, 0x7948, 0x7950, 0x7956, 0x795d, 0x798d,
+	0x798e, 0x7a40, 0x7a81, 0x7bc0, 0x7df4, 0x7e09, 0x7e41, 0x7f72,
+	0x8005, 0x81ed, 0x8279, 0x8279, 0x8457, 0x8910, 0x8996, 0x8b01,
+	0x8b39, 0x8cd3, 0x8d08, 0x8fb6, 0x9038, 0x96e3, 0x97ff, 0x983b,
+	0x5d9, 0x5b4, 0x5f2, 0x5b7, 0x5e9, 0x5c1, 0x5e9, 0x5c2,
+	0x5e9, 0x5bc, 0x5c1, 0x5e9, 0x5bc, 0x5c2, 0x5d0, 0x5b7,
+	0x5d0, 0x5b8, 0x5d0, 0x5bc, 0x5d1, 0x5bc, 0x5d2, 0x5bc,
+	0x5d3, 0x5bc, 0x5d4, 0x5bc, 0x5d5, 0x5bc, 0x5d6, 0x5bc,
+	0x5d8, 0x5bc, 0x5d9, 0x5bc, 0x5da, 0x5bc, 0x5db, 0x5bc,
+	0x5dc, 0x5bc, 0x5de, 0x5bc, 0x5e0, 0x5bc, 0x5e1, 0x5bc,
+	0x5e3, 0x5bc, 0x5e4, 0x5bc, 0x5e6, 0x5bc, 0x5e7, 0x5bc,
+	0x5e8, 0x5bc, 0x5e9, 0x5bc, 0x5ea, 0x5bc, 0x5d5, 0x5b9,
+	0x5d1, 0x5bf, 0x5db, 0x5bf, 0x5e4, 0x5bf, 0x1d157, 0x1d165,
+	0x1d158, 0x1d165, 0x1d158, 0x1d165, 0x1d16e, 0x1d158, 0x1d165, 0x1d16f,
+	0x1d158, 0x1d165, 0x1d170, 0x1d158, 0x1d165, 0x1d171, 0x1d158, 0x1d165,
+	0x1d172, 0x1d1b9, 0x1d165, 0x1d1ba, 0x1d165, 0x1d1b9, 0x1d165, 0x1d16e,
+	0x1d1ba, 0x1d165, 0x1d16e, 0x1d1b9, 0x1d165, 0x1d16f, 0x1d1ba, 0x1d165,
+	0x1d16f, 0x4e3d, 0x4e38, 0x4e41, 0x20122, 0x4f60, 0x4fae, 0x4fbb,
+	0x5002, 0x507a, 0x5099, 0x50e7, 0x50cf, 0x349e, 0x2063a, 0x514d,
+	0x5154, 0x5164, 0x5177, 0x2051c, 0x34b9, 0x5167, 0x518d, 0x2054b,
+	0x5197, 0x51a4, 0x4ecc, 0x51ac, 0x51b5, 0x291df, 0x51f5, 0x5203,
+	0x34df, 0x523b, 0x5246, 0x5272, 0x5277, 0x3515, 0x52c7, 0x52c9,
+	0x52e4, 0x52fa, 0x5305, 0x5306, 0x5317, 0x5349, 0x5351, 0x535a,
+	0x5373, 0x537d, 0x537f, 0x537f, 0x537f, 0x20a2c, 0x7070, 0x53ca,
+	0x53df, 0x20b63, 0x53eb, 0x53f1, 0x5406, 0x549e, 0x5438, 0x5448,
+	0x5468, 0x54a2, 0x54f6, 0x5510, 0x5553, 0x5563, 0x5584, 0x5584,
+	0x5599, 0x55ab, 0x55b3, 0x55c2, 0x5716, 0x5606, 0x5717, 0x5651,
+	0x5674, 0x5207, 0x58ee, 0x57ce, 0x57f4, 0x580d, 0x578b, 0x5832,
+	0x5831, 0x58ac, 0x214e4, 0x58f2, 0x58f7, 0x5906, 0x591a, 0x5922,
+	0x5962, 0x216a8, 0x216ea, 0x59ec, 0x5a1b, 0x5a27, 0x59d8, 0x5a66,
+	0x36ee, 0x36fc, 0x5b08, 0x5b3e, 0x5b3e, 0x219c8, 0x5bc3, 0x5bd8,
+	0x5be7, 0x5bf3, 0x21b18, 0x5bff, 0x5c06, 0x5f53, 0x5c22, 0x3781,
+	0x5c60, 0x5c6e, 0x5cc0, 0x5c8d, 0x21de4, 0x5d43, 0x21de6, 0x5d6e,
+	0x5d6b, 0x5d7c, 0x5de1, 0x5de2, 0x382f, 0x5dfd, 0x5e28, 0x5e3d,
+	0x5e69, 0x3862, 0x22183, 0x387c, 0x5eb0, 0x5eb3, 0x5eb6, 0x5eca,
+	0x2a392, 0x5efe, 0x22331, 0x22331, 0x8201, 0x5f22, 0x5f22, 0x38c7,
+	0x232b8, 0x261da, 0x5f62, 0x5f6b, 0x38e3, 0x5f9a, 0x5fcd, 0x5fd7,
+	0x5ff9, 0x6081, 0x393a, 0x391c, 0x6094, 0x226d4, 0x60c7, 0x6148,
+	0x614c, 0x614e, 0x614c, 0x617a, 0x618e, 0x61b2, 0x61a4, 0x61af,
+	0x61de, 0x61f2, 0x61f6, 0x6210, 0x621b, 0x625d, 0x62b1, 0x62d4,
+	0x6350, 0x22b0c, 0x633d, 0x62fc, 0x6368, 0x6383, 0x63e4, 0x22bf1,
+	0x6422, 0x63c5, 0x63a9, 0x3a2e, 0x6469, 0x647e, 0x649d, 0x6477,
+	0x3a6c, 0x654f, 0x656c, 0x2300a, 0x65e3, 0x66f8, 0x6649, 0x3b19,
+	0x6691, 0x3b08, 0x3ae4, 0x5192, 0x5195, 0x6700, 0x669c, 0x80ad,
+	0x43d9, 0x6717, 0x671b, 0x6721, 0x675e, 0x6753, 0x233c3, 0x3b49,
+	0x67fa, 0x6785, 0x6852, 0x6885, 0x2346d, 0x688e, 0x681f, 0x6914,
+	0x3b9d, 0x6942, 0x69a3, 0x69ea, 0x6aa8, 0x236a3, 0x6adb, 0x3c18,
+	0x6b21, 0x238a7, 0x6b54, 0x3c4e, 0x6b72, 0x6b9f, 0x6bba, 0x6bbb,
+	0x23a8d, 0x21d0b, 0x23afa, 0x6c4e, 0x23cbc, 0x6cbf, 0x6ccd, 0x6c67,
+	0x6d16, 0x6d3e, 0x6d77, 0x6d41, 0x6d69, 0x6d78, 0x6d85, 0x23d1e,
+	0x6d34, 0x6e2f, 0x6e6e, 0x3d33, 0x6ecb, 0x6ec7, 0x23ed1, 0x6df9,
+	0x6f6e, 0x23f5e, 0x23f8e, 0x6fc6, 0x7039, 0x701e, 0x701b, 0x3d96,
+	0x704a, 0x707d, 0x7077, 0x70ad, 0x20525, 0x7145, 0x24263, 0x719c,
+	0x243ab, 0x7228, 0x7235, 0x7250, 0x24608, 0x7280, 0x7295, 0x24735,
+	0x24814, 0x737a, 0x738b, 0x3eac, 0x73a5, 0x3eb8, 0x3eb8, 0x7447,
+	0x745c, 0x7471, 0x7485, 0x74ca, 0x3f1b, 0x7524, 0x24c36, 0x753e,
+	0x24c92, 0x7570, 0x2219f, 0x7610, 0x24fa1, 0x24fb8, 0x25044, 0x3ffc,
+	0x4008, 0x76f4, 0x250f3, 0x250f2, 0x25119, 0x25133, 0x771e, 0x771f,
+	0x771f, 0x774a, 0x4039, 0x778b, 0x4046, 0x4096, 0x2541d, 0x784e,
+	0x788c, 0x78cc, 0x40e3, 0x25626, 0x7956, 0x2569a, 0x256c5, 0x798f,
+	0x79eb, 0x412f, 0x7a40, 0x7a4a, 0x7a4f, 0x2597c, 0x25aa7, 0x25aa7,
+	0x7aee, 0x4202, 0x25bab, 0x7bc6, 0x7bc9, 0x4227, 0x25c80, 0x7cd2,
+	0x42a0, 0x7ce8, 0x7ce3, 0x7d00, 0x25f86, 0x7d63, 0x4301, 0x7dc7,
+	0x7e02, 0x7e45, 0x4334, 0x26228, 0x26247, 0x4359, 0x262d9, 0x7f7a,
+	0x2633e, 0x7f95, 0x7ffa, 0x8005, 0x264da, 0x26523, 0x8060, 0x265a8,
+	0x8070, 0x2335f, 0x43d5, 0x80b2, 0x8103, 0x440b, 0x813e, 0x5ab5,
+	0x267a7, 0x267b5, 0x23393, 0x2339c, 0x8201, 0x8204, 0x8f9e, 0x446b,
+	0x8291, 0x828b, 0x829d, 0x52b3, 0x82b1, 0x82b3, 0x82bd, 0x82e6,
+	0x26b3c, 0x82e5, 0x831d, 0x8363, 0x83ad, 0x8323, 0x83bd, 0x83e7,
+	0x8457, 0x8353, 0x83ca, 0x83cc, 0x83dc, 0x26c36, 0x26d6b, 0x26cd5,
+	0x452b, 0x84f1, 0x84f3, 0x8516, 0x273ca, 0x8564, 0x26f2c, 0x455d,
+	0x4561, 0x26fb1, 0x270d2, 0x456b, 0x8650, 0x865c, 0x8667, 0x8669,
+	0x86a9, 0x8688, 0x870e, 0x86e2, 0x8779, 0x8728, 0x876b, 0x8786,
+	0x45d7, 0x87e1, 0x8801, 0x45f9, 0x8860, 0x8863, 0x27667, 0x88d7,
+	0x88de, 0x4635, 0x88fa, 0x34bb, 0x278ae, 0x27966, 0x46be, 0x46c7,
+	0x8aa0, 0x8aed, 0x8b8a, 0x8c55, 0x27ca8, 0x8cab, 0x8cc1, 0x8d1b,
+	0x8d77, 0x27f2f, 0x20804, 0x8dcb, 0x8dbc, 0x8df0, 0x208de, 0x8ed4,
+	0x8f38, 0x285d2, 0x285ed, 0x9094, 0x90f1, 0x9111, 0x2872e, 0x911b,
+	0x9238, 0x92d7, 0x92d8, 0x927c, 0x93f9, 0x9415, 0x28bfa, 0x958b,
+	0x4995, 0x95b7, 0x28d77, 0x49e6, 0x96c3, 0x5db2, 0x9723, 0x29145,
+	0x2921a, 0x4a6e, 0x4a76, 0x97e0, 0x2940a, 0x4ab2, 0x29496, 0x980b,
+	0x980b, 0x9829, 0x295b6, 0x98e2, 0x4b33, 0x9929, 0x99a7, 0x99c2,
+	0x99fe, 0x4bce, 0x29b30, 0x9b12, 0x9c40, 0x9cfd, 0x4cce, 0x4ced,
+	0x9d67, 0x2a0ce, 0x4cf8, 0x2a105, 0x2a20e, 0x2a291, 0x9ebb, 0x4d56,
+	0x9ef9, 0x9efe, 0x9f05, 0x9f0f, 0x9f16, 0x9f3b, 0x2a600, };
+
+gli_decomp_block_t unigen_decomp_block_0x0[256] = {
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 0 },
+	{ 2, 2 },
+	{ 2, 4 },
+	{ 2, 6 },
+	{ 2, 8 },
+	{ 2, 10 },
+	{ 0, 0 },
+	{ 2, 12 },
+	{ 2, 14 },
+	{ 2, 16 },
+	{ 2, 18 },
+	{ 2, 20 },
+	{ 2, 22 },
+	{ 2, 24 },
+	{ 2, 26 },
+	{ 2, 28 },
+	{ 0, 0 },
+	{ 2, 30 },
+	{ 2, 32 },
+	{ 2, 34 },
+	{ 2, 36 },
+	{ 2, 38 },
+	{ 2, 40 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 42 },
+	{ 2, 44 },
+	{ 2, 46 },
+	{ 2, 48 },
+	{ 2, 50 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 52 },
+	{ 2, 54 },
+	{ 2, 56 },
+	{ 2, 58 },
+	{ 2, 60 },
+	{ 2, 62 },
+	{ 0, 0 },
+	{ 2, 64 },
+	{ 2, 66 },
+	{ 2, 68 },
+	{ 2, 70 },
+	{ 2, 72 },
+	{ 2, 74 },
+	{ 2, 76 },
+	{ 2, 78 },
+	{ 2, 80 },
+	{ 0, 0 },
+	{ 2, 82 },
+	{ 2, 84 },
+	{ 2, 86 },
+	{ 2, 88 },
+	{ 2, 90 },
+	{ 2, 92 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 94 },
+	{ 2, 96 },
+	{ 2, 98 },
+	{ 2, 100 },
+	{ 2, 102 },
+	{ 0, 0 },
+	{ 2, 104 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x1[256] = {
+	{ 2, 106 },
+	{ 2, 108 },
+	{ 2, 110 },
+	{ 2, 112 },
+	{ 2, 114 },
+	{ 2, 116 },
+	{ 2, 118 },
+	{ 2, 120 },
+	{ 2, 122 },
+	{ 2, 124 },
+	{ 2, 126 },
+	{ 2, 128 },
+	{ 2, 130 },
+	{ 2, 132 },
+	{ 2, 134 },
+	{ 2, 136 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 138 },
+	{ 2, 140 },
+	{ 2, 142 },
+	{ 2, 144 },
+	{ 2, 146 },
+	{ 2, 148 },
+	{ 2, 150 },
+	{ 2, 152 },
+	{ 2, 154 },
+	{ 2, 156 },
+	{ 2, 158 },
+	{ 2, 160 },
+	{ 2, 162 },
+	{ 2, 164 },
+	{ 2, 166 },
+	{ 2, 168 },
+	{ 2, 170 },
+	{ 2, 172 },
+	{ 2, 174 },
+	{ 2, 176 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 178 },
+	{ 2, 180 },
+	{ 2, 182 },
+	{ 2, 184 },
+	{ 2, 186 },
+	{ 2, 188 },
+	{ 2, 190 },
+	{ 2, 192 },
+	{ 2, 194 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 196 },
+	{ 2, 198 },
+	{ 2, 200 },
+	{ 2, 202 },
+	{ 0, 0 },
+	{ 2, 204 },
+	{ 2, 206 },
+	{ 2, 208 },
+	{ 2, 210 },
+	{ 2, 212 },
+	{ 2, 214 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 216 },
+	{ 2, 218 },
+	{ 2, 220 },
+	{ 2, 222 },
+	{ 2, 224 },
+	{ 2, 226 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 228 },
+	{ 2, 230 },
+	{ 2, 232 },
+	{ 2, 234 },
+	{ 2, 236 },
+	{ 2, 238 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 240 },
+	{ 2, 242 },
+	{ 2, 244 },
+	{ 2, 246 },
+	{ 2, 248 },
+	{ 2, 250 },
+	{ 2, 252 },
+	{ 2, 254 },
+	{ 2, 256 },
+	{ 2, 258 },
+	{ 2, 260 },
+	{ 2, 262 },
+	{ 2, 264 },
+	{ 2, 266 },
+	{ 2, 268 },
+	{ 2, 270 },
+	{ 2, 272 },
+	{ 2, 274 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 276 },
+	{ 2, 278 },
+	{ 2, 280 },
+	{ 2, 282 },
+	{ 2, 284 },
+	{ 2, 286 },
+	{ 2, 288 },
+	{ 2, 290 },
+	{ 2, 292 },
+	{ 2, 294 },
+	{ 2, 296 },
+	{ 2, 298 },
+	{ 2, 300 },
+	{ 2, 302 },
+	{ 2, 304 },
+	{ 2, 306 },
+	{ 2, 308 },
+	{ 2, 310 },
+	{ 2, 312 },
+	{ 2, 314 },
+	{ 2, 316 },
+	{ 2, 318 },
+	{ 2, 320 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 322 },
+	{ 2, 324 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 326 },
+	{ 2, 328 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 330 },
+	{ 2, 332 },
+	{ 2, 334 },
+	{ 2, 336 },
+	{ 2, 338 },
+	{ 2, 340 },
+	{ 2, 342 },
+	{ 2, 344 },
+	{ 3, 346 },
+	{ 3, 349 },
+	{ 3, 352 },
+	{ 3, 355 },
+	{ 3, 358 },
+	{ 3, 361 },
+	{ 3, 364 },
+	{ 3, 367 },
+	{ 0, 0 },
+	{ 3, 370 },
+	{ 3, 373 },
+	{ 3, 376 },
+	{ 3, 379 },
+	{ 2, 382 },
+	{ 2, 384 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 386 },
+	{ 2, 388 },
+	{ 2, 390 },
+	{ 2, 392 },
+	{ 2, 394 },
+	{ 2, 396 },
+	{ 3, 398 },
+	{ 3, 401 },
+	{ 2, 404 },
+	{ 2, 406 },
+	{ 2, 408 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 410 },
+	{ 2, 412 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 414 },
+	{ 2, 416 },
+	{ 3, 418 },
+	{ 3, 421 },
+	{ 2, 424 },
+	{ 2, 426 },
+	{ 2, 428 },
+	{ 2, 430 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x2[256] = {
+	{ 2, 432 },
+	{ 2, 434 },
+	{ 2, 436 },
+	{ 2, 438 },
+	{ 2, 440 },
+	{ 2, 442 },
+	{ 2, 444 },
+	{ 2, 446 },
+	{ 2, 448 },
+	{ 2, 450 },
+	{ 2, 452 },
+	{ 2, 454 },
+	{ 2, 456 },
+	{ 2, 458 },
+	{ 2, 460 },
+	{ 2, 462 },
+	{ 2, 464 },
+	{ 2, 466 },
+	{ 2, 468 },
+	{ 2, 470 },
+	{ 2, 472 },
+	{ 2, 474 },
+	{ 2, 476 },
+	{ 2, 478 },
+	{ 2, 480 },
+	{ 2, 482 },
+	{ 2, 484 },
+	{ 2, 486 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 488 },
+	{ 2, 490 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 492 },
+	{ 2, 494 },
+	{ 2, 496 },
+	{ 2, 498 },
+	{ 3, 500 },
+	{ 3, 503 },
+	{ 3, 506 },
+	{ 3, 509 },
+	{ 2, 512 },
+	{ 2, 514 },
+	{ 3, 516 },
+	{ 3, 519 },
+	{ 2, 522 },
+	{ 2, 524 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x3[256] = {
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 1, 526 },
+	{ 1, 527 },
+	{ 0, 0 },
+	{ 1, 528 },
+	{ 2, 529 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 1, 531 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 1, 532 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 533 },
+	{ 2, 535 },
+	{ 1, 537 },
+	{ 2, 538 },
+	{ 2, 540 },
+	{ 2, 542 },
+	{ 0, 0 },
+	{ 2, 544 },
+	{ 0, 0 },
+	{ 2, 546 },
+	{ 2, 548 },
+	{ 3, 550 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 553 },
+	{ 2, 555 },
+	{ 2, 557 },
+	{ 2, 559 },
+	{ 2, 561 },
+	{ 2, 563 },
+	{ 3, 565 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 568 },
+	{ 2, 570 },
+	{ 2, 572 },
+	{ 2, 574 },
+	{ 2, 576 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 578 },
+	{ 2, 580 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x4[256] = {
+	{ 2, 582 },
+	{ 2, 584 },
+	{ 0, 0 },
+	{ 2, 586 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 588 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 590 },
+	{ 2, 592 },
+	{ 2, 594 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 596 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 598 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 600 },
+	{ 2, 602 },
+	{ 0, 0 },
+	{ 2, 604 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 606 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 608 },
+	{ 2, 610 },
+	{ 2, 612 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 614 },
+	{ 2, 616 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 618 },
+	{ 2, 620 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 622 },
+	{ 2, 624 },
+	{ 2, 626 },
+	{ 2, 628 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 630 },
+	{ 2, 632 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 634 },
+	{ 2, 636 },
+	{ 2, 638 },
+	{ 2, 640 },
+	{ 2, 642 },
+	{ 2, 644 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 646 },
+	{ 2, 648 },
+	{ 2, 650 },
+	{ 2, 652 },
+	{ 2, 654 },
+	{ 2, 656 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 658 },
+	{ 2, 660 },
+	{ 2, 662 },
+	{ 2, 664 },
+	{ 2, 666 },
+	{ 2, 668 },
+	{ 2, 670 },
+	{ 2, 672 },
+	{ 2, 674 },
+	{ 2, 676 },
+	{ 2, 678 },
+	{ 2, 680 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 682 },
+	{ 2, 684 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x1e[256] = {
+	{ 2, 828 },
+	{ 2, 830 },
+	{ 2, 832 },
+	{ 2, 834 },
+	{ 2, 836 },
+	{ 2, 838 },
+	{ 2, 840 },
+	{ 2, 842 },
+	{ 3, 844 },
+	{ 3, 847 },
+	{ 2, 850 },
+	{ 2, 852 },
+	{ 2, 854 },
+	{ 2, 856 },
+	{ 2, 858 },
+	{ 2, 860 },
+	{ 2, 862 },
+	{ 2, 864 },
+	{ 2, 866 },
+	{ 2, 868 },
+	{ 3, 870 },
+	{ 3, 873 },
+	{ 3, 876 },
+	{ 3, 879 },
+	{ 2, 882 },
+	{ 2, 884 },
+	{ 2, 886 },
+	{ 2, 888 },
+	{ 3, 890 },
+	{ 3, 893 },
+	{ 2, 896 },
+	{ 2, 898 },
+	{ 2, 900 },
+	{ 2, 902 },
+	{ 2, 904 },
+	{ 2, 906 },
+	{ 2, 908 },
+	{ 2, 910 },
+	{ 2, 912 },
+	{ 2, 914 },
+	{ 2, 916 },
+	{ 2, 918 },
+	{ 2, 920 },
+	{ 2, 922 },
+	{ 2, 924 },
+	{ 2, 926 },
+	{ 3, 928 },
+	{ 3, 931 },
+	{ 2, 934 },
+	{ 2, 936 },
+	{ 2, 938 },
+	{ 2, 940 },
+	{ 2, 942 },
+	{ 2, 944 },
+	{ 2, 946 },
+	{ 2, 948 },
+	{ 3, 950 },
+	{ 3, 953 },
+	{ 2, 956 },
+	{ 2, 958 },
+	{ 2, 960 },
+	{ 2, 962 },
+	{ 2, 964 },
+	{ 2, 966 },
+	{ 2, 968 },
+	{ 2, 970 },
+	{ 2, 972 },
+	{ 2, 974 },
+	{ 2, 976 },
+	{ 2, 978 },
+	{ 2, 980 },
+	{ 2, 982 },
+	{ 2, 984 },
+	{ 2, 986 },
+	{ 2, 988 },
+	{ 2, 990 },
+	{ 3, 992 },
+	{ 3, 995 },
+	{ 3, 998 },
+	{ 3, 1001 },
+	{ 3, 1004 },
+	{ 3, 1007 },
+	{ 3, 1010 },
+	{ 3, 1013 },
+	{ 2, 1016 },
+	{ 2, 1018 },
+	{ 2, 1020 },
+	{ 2, 1022 },
+	{ 2, 1024 },
+	{ 2, 1026 },
+	{ 2, 1028 },
+	{ 2, 1030 },
+	{ 3, 1032 },
+	{ 3, 1035 },
+	{ 2, 1038 },
+	{ 2, 1040 },
+	{ 2, 1042 },
+	{ 2, 1044 },
+	{ 2, 1046 },
+	{ 2, 1048 },
+	{ 3, 1050 },
+	{ 3, 1053 },
+	{ 3, 1056 },
+	{ 3, 1059 },
+	{ 3, 1062 },
+	{ 3, 1065 },
+	{ 2, 1068 },
+	{ 2, 1070 },
+	{ 2, 1072 },
+	{ 2, 1074 },
+	{ 2, 1076 },
+	{ 2, 1078 },
+	{ 2, 1080 },
+	{ 2, 1082 },
+	{ 2, 1084 },
+	{ 2, 1086 },
+	{ 2, 1088 },
+	{ 2, 1090 },
+	{ 2, 1092 },
+	{ 2, 1094 },
+	{ 3, 1096 },
+	{ 3, 1099 },
+	{ 3, 1102 },
+	{ 3, 1105 },
+	{ 2, 1108 },
+	{ 2, 1110 },
+	{ 2, 1112 },
+	{ 2, 1114 },
+	{ 2, 1116 },
+	{ 2, 1118 },
+	{ 2, 1120 },
+	{ 2, 1122 },
+	{ 2, 1124 },
+	{ 2, 1126 },
+	{ 2, 1128 },
+	{ 2, 1130 },
+	{ 2, 1132 },
+	{ 2, 1134 },
+	{ 2, 1136 },
+	{ 2, 1138 },
+	{ 2, 1140 },
+	{ 2, 1142 },
+	{ 2, 1144 },
+	{ 2, 1146 },
+	{ 2, 1148 },
+	{ 2, 1150 },
+	{ 2, 1152 },
+	{ 2, 1154 },
+	{ 2, 1156 },
+	{ 2, 1158 },
+	{ 2, 1160 },
+	{ 2, 1162 },
+	{ 2, 1164 },
+	{ 2, 1166 },
+	{ 0, 0 },
+	{ 2, 1168 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 1170 },
+	{ 2, 1172 },
+	{ 2, 1174 },
+	{ 2, 1176 },
+	{ 3, 1178 },
+	{ 3, 1181 },
+	{ 3, 1184 },
+	{ 3, 1187 },
+	{ 3, 1190 },
+	{ 3, 1193 },
+	{ 3, 1196 },
+	{ 3, 1199 },
+	{ 3, 1202 },
+	{ 3, 1205 },
+	{ 3, 1208 },
+	{ 3, 1211 },
+	{ 3, 1214 },
+	{ 3, 1217 },
+	{ 3, 1220 },
+	{ 3, 1223 },
+	{ 3, 1226 },
+	{ 3, 1229 },
+	{ 3, 1232 },
+	{ 3, 1235 },
+	{ 2, 1238 },
+	{ 2, 1240 },
+	{ 2, 1242 },
+	{ 2, 1244 },
+	{ 2, 1246 },
+	{ 2, 1248 },
+	{ 3, 1250 },
+	{ 3, 1253 },
+	{ 3, 1256 },
+	{ 3, 1259 },
+	{ 3, 1262 },
+	{ 3, 1265 },
+	{ 3, 1268 },
+	{ 3, 1271 },
+	{ 3, 1274 },
+	{ 3, 1277 },
+	{ 2, 1280 },
+	{ 2, 1282 },
+	{ 2, 1284 },
+	{ 2, 1286 },
+	{ 2, 1288 },
+	{ 2, 1290 },
+	{ 2, 1292 },
+	{ 2, 1294 },
+	{ 3, 1296 },
+	{ 3, 1299 },
+	{ 3, 1302 },
+	{ 3, 1305 },
+	{ 3, 1308 },
+	{ 3, 1311 },
+	{ 3, 1314 },
+	{ 3, 1317 },
+	{ 3, 1320 },
+	{ 3, 1323 },
+	{ 3, 1326 },
+	{ 3, 1329 },
+	{ 3, 1332 },
+	{ 3, 1335 },
+	{ 3, 1338 },
+	{ 3, 1341 },
+	{ 3, 1344 },
+	{ 3, 1347 },
+	{ 3, 1350 },
+	{ 3, 1353 },
+	{ 2, 1356 },
+	{ 2, 1358 },
+	{ 2, 1360 },
+	{ 2, 1362 },
+	{ 3, 1364 },
+	{ 3, 1367 },
+	{ 3, 1370 },
+	{ 3, 1373 },
+	{ 3, 1376 },
+	{ 3, 1379 },
+	{ 3, 1382 },
+	{ 3, 1385 },
+	{ 3, 1388 },
+	{ 3, 1391 },
+	{ 2, 1394 },
+	{ 2, 1396 },
+	{ 2, 1398 },
+	{ 2, 1400 },
+	{ 2, 1402 },
+	{ 2, 1404 },
+	{ 2, 1406 },
+	{ 2, 1408 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x1f[256] = {
+	{ 2, 1410 },
+	{ 2, 1412 },
+	{ 3, 1414 },
+	{ 3, 1417 },
+	{ 3, 1420 },
+	{ 3, 1423 },
+	{ 3, 1426 },
+	{ 3, 1429 },
+	{ 2, 1432 },
+	{ 2, 1434 },
+	{ 3, 1436 },
+	{ 3, 1439 },
+	{ 3, 1442 },
+	{ 3, 1445 },
+	{ 3, 1448 },
+	{ 3, 1451 },
+	{ 2, 1454 },
+	{ 2, 1456 },
+	{ 3, 1458 },
+	{ 3, 1461 },
+	{ 3, 1464 },
+	{ 3, 1467 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 1470 },
+	{ 2, 1472 },
+	{ 3, 1474 },
+	{ 3, 1477 },
+	{ 3, 1480 },
+	{ 3, 1483 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 1486 },
+	{ 2, 1488 },
+	{ 3, 1490 },
+	{ 3, 1493 },
+	{ 3, 1496 },
+	{ 3, 1499 },
+	{ 3, 1502 },
+	{ 3, 1505 },
+	{ 2, 1508 },
+	{ 2, 1510 },
+	{ 3, 1512 },
+	{ 3, 1515 },
+	{ 3, 1518 },
+	{ 3, 1521 },
+	{ 3, 1524 },
+	{ 3, 1527 },
+	{ 2, 1530 },
+	{ 2, 1532 },
+	{ 3, 1534 },
+	{ 3, 1537 },
+	{ 3, 1540 },
+	{ 3, 1543 },
+	{ 3, 1546 },
+	{ 3, 1549 },
+	{ 2, 1552 },
+	{ 2, 1554 },
+	{ 3, 1556 },
+	{ 3, 1559 },
+	{ 3, 1562 },
+	{ 3, 1565 },
+	{ 3, 1568 },
+	{ 3, 1571 },
+	{ 2, 1574 },
+	{ 2, 1576 },
+	{ 3, 1578 },
+	{ 3, 1581 },
+	{ 3, 1584 },
+	{ 3, 1587 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 1590 },
+	{ 2, 1592 },
+	{ 3, 1594 },
+	{ 3, 1597 },
+	{ 3, 1600 },
+	{ 3, 1603 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 1606 },
+	{ 2, 1608 },
+	{ 3, 1610 },
+	{ 3, 1613 },
+	{ 3, 1616 },
+	{ 3, 1619 },
+	{ 3, 1622 },
+	{ 3, 1625 },
+	{ 0, 0 },
+	{ 2, 1628 },
+	{ 0, 0 },
+	{ 3, 1630 },
+	{ 0, 0 },
+	{ 3, 1633 },
+	{ 0, 0 },
+	{ 3, 1636 },
+	{ 2, 1639 },
+	{ 2, 1641 },
+	{ 3, 1643 },
+	{ 3, 1646 },
+	{ 3, 1649 },
+	{ 3, 1652 },
+	{ 3, 1655 },
+	{ 3, 1658 },
+	{ 2, 1661 },
+	{ 2, 1663 },
+	{ 3, 1665 },
+	{ 3, 1668 },
+	{ 3, 1671 },
+	{ 3, 1674 },
+	{ 3, 1677 },
+	{ 3, 1680 },
+	{ 2, 1683 },
+	{ 2, 1685 },
+	{ 2, 1687 },
+	{ 2, 1689 },
+	{ 2, 1691 },
+	{ 2, 1693 },
+	{ 2, 1695 },
+	{ 2, 1697 },
+	{ 2, 1699 },
+	{ 2, 1701 },
+	{ 2, 1703 },
+	{ 2, 1705 },
+	{ 2, 1707 },
+	{ 2, 1709 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 3, 1711 },
+	{ 3, 1714 },
+	{ 4, 1717 },
+	{ 4, 1721 },
+	{ 4, 1725 },
+	{ 4, 1729 },
+	{ 4, 1733 },
+	{ 4, 1737 },
+	{ 3, 1741 },
+	{ 3, 1744 },
+	{ 4, 1747 },
+	{ 4, 1751 },
+	{ 4, 1755 },
+	{ 4, 1759 },
+	{ 4, 1763 },
+	{ 4, 1767 },
+	{ 3, 1771 },
+	{ 3, 1774 },
+	{ 4, 1777 },
+	{ 4, 1781 },
+	{ 4, 1785 },
+	{ 4, 1789 },
+	{ 4, 1793 },
+	{ 4, 1797 },
+	{ 3, 1801 },
+	{ 3, 1804 },
+	{ 4, 1807 },
+	{ 4, 1811 },
+	{ 4, 1815 },
+	{ 4, 1819 },
+	{ 4, 1823 },
+	{ 4, 1827 },
+	{ 3, 1831 },
+	{ 3, 1834 },
+	{ 4, 1837 },
+	{ 4, 1841 },
+	{ 4, 1845 },
+	{ 4, 1849 },
+	{ 4, 1853 },
+	{ 4, 1857 },
+	{ 3, 1861 },
+	{ 3, 1864 },
+	{ 4, 1867 },
+	{ 4, 1871 },
+	{ 4, 1875 },
+	{ 4, 1879 },
+	{ 4, 1883 },
+	{ 4, 1887 },
+	{ 2, 1891 },
+	{ 2, 1893 },
+	{ 3, 1895 },
+	{ 2, 1898 },
+	{ 3, 1900 },
+	{ 0, 0 },
+	{ 2, 1903 },
+	{ 3, 1905 },
+	{ 2, 1908 },
+	{ 2, 1910 },
+	{ 2, 1912 },
+	{ 2, 1914 },
+	{ 2, 1916 },
+	{ 0, 0 },
+	{ 1, 1918 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 1919 },
+	{ 3, 1921 },
+	{ 2, 1924 },
+	{ 3, 1926 },
+	{ 0, 0 },
+	{ 2, 1929 },
+	{ 3, 1931 },
+	{ 2, 1934 },
+	{ 2, 1936 },
+	{ 2, 1938 },
+	{ 2, 1940 },
+	{ 2, 1942 },
+	{ 2, 1944 },
+	{ 2, 1946 },
+	{ 2, 1948 },
+	{ 2, 1950 },
+	{ 2, 1952 },
+	{ 3, 1954 },
+	{ 3, 1957 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 1960 },
+	{ 3, 1962 },
+	{ 2, 1965 },
+	{ 2, 1967 },
+	{ 2, 1969 },
+	{ 2, 1971 },
+	{ 0, 0 },
+	{ 2, 1973 },
+	{ 2, 1975 },
+	{ 2, 1977 },
+	{ 2, 1979 },
+	{ 2, 1981 },
+	{ 3, 1983 },
+	{ 3, 1986 },
+	{ 2, 1989 },
+	{ 2, 1991 },
+	{ 2, 1993 },
+	{ 3, 1995 },
+	{ 2, 1998 },
+	{ 2, 2000 },
+	{ 2, 2002 },
+	{ 2, 2004 },
+	{ 2, 2006 },
+	{ 2, 2008 },
+	{ 2, 2010 },
+	{ 1, 2012 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 3, 2013 },
+	{ 2, 2016 },
+	{ 3, 2018 },
+	{ 0, 0 },
+	{ 2, 2021 },
+	{ 3, 2023 },
+	{ 2, 2026 },
+	{ 2, 2028 },
+	{ 2, 2030 },
+	{ 2, 2032 },
+	{ 2, 2034 },
+	{ 1, 2036 },
+	{ 0, 0 },
+	{ 0, 0 },
+};
+
+gli_decomp_block_t unigen_decomp_block_0x22[256] = {
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 2055 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 2057 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 2059 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 2, 2061 },
+	{ 0, 0 },
+	{ 2, 2063 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },
+	{ 0, 0 },


Commit: 743660448317ac0a0e806f27945fe75e358e2b3d
    https://github.com/scummvm/scummvm/commit/743660448317ac0a0e806f27945fe75e358e2b3d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added time and date glk functions

Changed paths:
  A engines/gargoyle/time.cpp
  A engines/gargoyle/time.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/module.mk
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index c385482..f1b24cb 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -857,11 +857,12 @@ void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
 }
 
 void Glk::glk_set_hyperlink(glui32 linkval) {
-	// TODO
+	_streams->getCurrent()->setHyperlink(linkval);
 }
 
 void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
-	// TODO
+	if (str)
+		str->setHyperlink(linkval);
 }
 
 void Glk::glk_request_hyperlink_event(winid_t win) {
@@ -873,50 +874,64 @@ void Glk::glk_request_hyperlink_event(winid_t win) {
 }
 
 void Glk::glk_cancel_hyperlink_event(winid_t win) {
-	// TODO
+	if (win) {
+		win->cancelHyperlinkEvent();
+	} else {
+		warning("cancel_hyperlink_event: invalid ref");
+	}
 }
 
 void Glk::glk_current_time(glktimeval_t *time) {
-	// TODO
+	TimeAndDate td;
+	*time = td;
 }
 
 glsi32 Glk::glk_current_simple_time(glui32 factor) {
-	// TODO
-	return 0;
+	assert(factor);
+	TimeAndDate td;
+
+	return td / factor;
 }
 
-void Glk::glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date) {
-	// TODO
+void Glk::glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date) {
+	// TODO: timezones aren't currently supported
+	*date = TimeAndDate(*time);
 }
 
-void Glk::glk_time_to_date_local(glktimeval_t *time, glkdate_t *date) {
-	// TODO
+void Glk::glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date) {
+	*date = TimeAndDate(*time);
 }
 
 void Glk::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
-	// TODO
+	TimeSeconds secs = (int64)time * factor;
+	*date = TimeAndDate(secs);
 }
 
 void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
-	// TODO
+	TimeSeconds secs = (int64)time * factor;
+	*date = TimeAndDate(secs);
 }
 
-void Glk::glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time) {
-	// TODO
+void Glk::glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time) {
+	// TODO: timezones aren't currently supported
+	*time = TimeAndDate(*date);
 }
 
-void Glk::glk_date_to_time_local(glkdate_t *date, glktimeval_t *time) {
-	// TODO
+void Glk::glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time) {
+	*time = TimeAndDate(*date);
 }
 
-glsi32 Glk::glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor) {
-	// TODO
-	return 0;
+glsi32 Glk::glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor) {
+	// TODO: timezones aren't currently supported
+	assert(factor);
+	TimeSeconds ts = TimeAndDate(*date);
+	return ts / factor;
 }
 
-glsi32 Glk::glk_date_to_simple_time_local(glkdate_t *date, glui32 factor) {
-	// TODO
-	return 0;
+glsi32 Glk::glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor) {
+	assert(factor);
+	TimeSeconds ts = TimeAndDate(*date);
+	return ts / factor;
 }
 
 /* XXX non-official Glk functions that may or may not exist */
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 34531c0..cf06474 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -25,11 +25,11 @@
 
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/glk_types.h"
+#include "gargoyle/time.h"
 #include "gargoyle/windows.h"
 
 namespace Gargoyle {
 
-
 /**
  * Implements the GLK interface
  */
@@ -257,16 +257,14 @@ public:
 
 	void glk_current_time(glktimeval_t *time);
 	glsi32 glk_current_simple_time(glui32 factor);
-	void glk_time_to_date_utc(glktimeval_t *time, glkdate_t *date);
-	void glk_time_to_date_local(glktimeval_t *time, glkdate_t *date);
-	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor,
-		glkdate_t *date);
-	void glk_simple_time_to_date_local(glsi32 time, glui32 factor,
-		glkdate_t *date);
-	void glk_date_to_time_utc(glkdate_t *date, glktimeval_t *time);
-	void glk_date_to_time_local(glkdate_t *date, glktimeval_t *time);
-	glsi32 glk_date_to_simple_time_utc(glkdate_t *date, glui32 factor);
-	glsi32 glk_date_to_simple_time_local(glkdate_t *date, glui32 factor);
+	void glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date);
+	void glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date);
+	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date);
+	void glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date);
+	void glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time);
+	void glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time);
+	glsi32 glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor);
+	glsi32 glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor);
 
 #endif /* GLK_MODULE_DATETIME */
 
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index a521548..274443a 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -215,34 +215,11 @@ enum ImageAlign {
 
 #endif /* GLK_MODULE_IMAGE */
 
-#ifdef GLK_MODULE_DATETIME
-
-struct glktimeval_struct {
-	glsi32 high_sec;
-	glui32 low_sec;
-	glsi32 microsec;
-};
-typedef glktimeval_struct glktimeval_t;
-
-struct glkdate_struct {
-	glsi32 year;     ///< full (four-digit) year */
-	glsi32 month;    ///< 1-12, 1 is January
-	glsi32 day;      ///< 1-31
-	glsi32 weekday;  ///< 0-6, 0 is Sunday
-	glsi32 hour;     ///< 0-23
-	glsi32 minute;   ///< 0-59
-	glsi32 second;   ///< 0-59, maybe 60 during a leap second
-	glsi32 microsec; ///< 0-999999
-};
-typedef glkdate_struct glkdate_t;
-
 union gidispatch_rock_t {
 	glui32 num;
 	void *ptr;
 };
 
-#endif /* GLK_MODULE_DATETIME */
-
 } // End of namespace Gargoyle
 
 #endif
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index d3cf7a1..b8ef788 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -10,6 +10,7 @@ MODULE_OBJS := \
 	picture.o \
 	screen.o \
 	streams.o \
+	time.o \
 	unicode.o \
 	unicode_gen.o \
 	windows.o \
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 1942ddf..3ebb98b 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -156,6 +156,11 @@ void WindowStream::setStyle(glui32 val) {
 		_window->_echoStream->setStyle(val);
 }
 
+void WindowStream::setHyperlink(glui32 linkVal) {
+	if (_writable)
+		_window->_attr.hyper = linkVal;
+}
+
 /*--------------------------------------------------------------------------*/
 
 MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 77ae8cb..de8b704 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -153,6 +153,10 @@ public:
 	 */
 	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) { return 0; }
 
+	/**
+	 * Set a hyperlink
+	 */
+	virtual void setHyperlink(glui32 linkVal) {}
 };
 typedef Stream *strid_t;
 
@@ -195,6 +199,11 @@ public:
 	virtual void putBufferUni(const uint32 *buf, size_t len) override;
 
 	virtual void setStyle(glui32 val) override;
+
+	/**
+	 * Set a hyperlink
+	 */
+	virtual void setHyperlink(glui32 linkVal) override;
 };
 
 /**
diff --git a/engines/gargoyle/time.cpp b/engines/gargoyle/time.cpp
new file mode 100644
index 0000000..9a99d3b
--- /dev/null
+++ b/engines/gargoyle/time.cpp
@@ -0,0 +1,118 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY{} without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program{} if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/time.h"
+#include "common/system.h"
+
+namespace Gargoyle {
+
+TimeAndDate::TimeAndDate() {
+	::TimeDate t;
+	g_system->getTimeAndDate(t);
+
+	year = t.tm_year;
+	month = t.tm_mon;
+	day = t.tm_mday;
+	weekday = t.tm_wday;
+	hour = t.tm_hour;
+	minute = t.tm_min;
+	second = t.tm_sec;
+	microsec = 0;
+}
+
+TimeAndDate::TimeAndDate(const TimeSeconds &ts) {
+	setTime(ts);
+}
+
+TimeAndDate::TimeAndDate(const Timestamp &t) {
+	setTime(((int64)t.high_sec << 32) | t.low_sec);
+}
+
+TimeAndDate::operator TimeSeconds() const {
+	return getTime();
+}
+
+TimeAndDate::operator Timestamp() const {
+	TimeSeconds secs = getTime();
+	Timestamp ts;
+	ts.high_sec = secs >> 32;
+	ts.low_sec = secs & 0xffffffff;
+	ts.microsec = 0;
+
+	return ts;
+}
+
+void TimeAndDate::setTime(const TimeSeconds &ts) {
+	TimeSeconds total = ts;
+	int daysInYear, secsInYear;
+
+	// Figure out the year
+	this->year = 1969;
+	do {
+		++this->year;
+		daysInYear = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 365 : 365;
+		secsInYear = daysInYear * 24 * 60 * 60;
+	} while (total >= daysInYear);
+
+	// Figure out month and day
+	int dayInYear = total / (24 * 60 * 60);
+	total %= 24 * 60 * 60;
+
+	int MONTH_DAYS[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+	this->month = 1;
+	while (dayInYear >= MONTH_DAYS[this->month - 1]) {
+		dayInYear -= MONTH_DAYS[this->month - 1];
+		this->month++;
+	}
+
+	this->day = dayInYear + 1;
+
+	// set the time within the day
+	this->hour = total / (60 * 60);
+	total %= (60 * 60);
+	this->minute = total / 60;
+	this->second = total % 60;
+	this->microsec = 0;
+}
+
+TimeSeconds TimeAndDate::getTime() const {
+	uint32 days = day - 1;
+	for (int i = 1970; i < year; ++i)
+		if ((i % 4 == 0 && i % 100 != 0) || (i % 400 == 0))
+			days += 366;
+		else
+			days += 365;
+
+	int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+	for (int i = 1; i < month; ++i) {
+		days += mdays[i - 1];
+		if (i == 2)
+			if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
+				days += 1;
+	}
+
+	int64 totalHours = days * 24 + hour;
+	int64 totalMinutes = totalHours * 60 + minute;
+	return totalMinutes * 60 + second;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/time.h b/engines/gargoyle/time.h
new file mode 100644
index 0000000..3e658d2
--- /dev/null
+++ b/engines/gargoyle/time.h
@@ -0,0 +1,93 @@
+/* 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 GARGOYLE_TIME_H
+#define GARGOYLE_TIME_H
+
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+typedef int64 TimeSeconds;
+
+struct Timestamp {
+	glsi32 high_sec;
+	glui32 low_sec;
+	glsi32 microsec;
+};
+typedef Timestamp glktimeval_t;
+
+struct TimeAndDate {
+	glsi32 year;     ///< full (four-digit) year
+	glsi32 month;    ///< 1-12, 1 is January
+	glsi32 day;      ///< 1-31
+	glsi32 weekday;  ///< 0-6, 0 is Sunday
+	glsi32 hour;     ///< 0-23
+	glsi32 minute;   ///< 0-59
+	glsi32 second;   ///< 0-59, maybe 60 during a leap second
+	glsi32 microsec; ///< 0-999999
+private:
+	/**
+	 * Get the number of seconds since the start of 1970
+	 */
+	int64 getSecondsSince1970() const;
+
+	/**
+	 * Convert a time in seconds to the structure
+	 */
+	void setTime(const TimeSeconds &ts);
+
+	/**
+	 * Get time in seconds from the structure
+	 */
+	TimeSeconds getTime() const;
+public:
+	/**
+	 * Constructor
+	 */
+	TimeAndDate();
+
+	/**
+	 * Constructor
+	 */
+	TimeAndDate(const TimeSeconds &ts);
+
+	/**
+	 * Constructor
+	 */
+	TimeAndDate(const Timestamp &t);
+
+	/**
+	 * Convert to seconds
+	 */
+	operator TimeSeconds() const;
+
+	/**
+	 * Convert to time stamp
+	 */
+	operator Timestamp() const;
+};
+typedef TimeAndDate glkdate_t;
+
+} // End of namespace Gargoyle
+
+#endif


Commit: 76bf2726f804a059d48ead24ecca6e1654fb1fb7
    https://github.com/scummvm/scummvm/commit/76bf2726f804a059d48ead24ecca6e1654fb1fb7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added input window focus, and cleanup of event method stubs

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 3373628..a4dadcb 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -24,11 +24,7 @@
 
 namespace Gargoyle {
 
-void Events::pollEvents() {
-	// TODO
-}
-
-void Events::clearEvent(Event *ev) {
+void Events::getEvent(event_t *event, bool polled) {
 	// TODO
 }
 
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index f49d9ff..6140dc4 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -123,12 +123,13 @@ public:
 	Events() : _forceClick(false) {}
 
 	/**
-	 * Checks for new events
-	 */
-	void pollEvents();
-
-	void clearEvent(Event *ev);
+	  * Get any pending event
+	  */
+	void getEvent(event_t *event, bool polled);
 
+	/**
+	 * Store an event for retrieval
+	 */
 	void eventStore(EvType type, Window *win, uint32 val1 = 0, uint32 val2 = 0);
 };
 
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index f1b24cb..736ed3e 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -502,20 +502,20 @@ glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
 
 void Glk::glk_select(event_t *event) {
 	if (!_gliFirstEvent) {
-		gliInputGuessFocus();
+		_windows->inputGuessFocus();
 		_gliFirstEvent = true;
 	}
 	
-	gliSelect(event, false);
+	_events->getEvent(event, false);
 }
 
 void Glk::glk_select_poll(event_t *event) {
 	if (!_gliFirstEvent) {
-		gliInputGuessFocus();
+		_windows->inputGuessFocus();
 		_gliFirstEvent = true;
 	}
 
-	gliSelect(event, true);
+	_events->getEvent(event, true);
 }
 
 void Glk::glk_request_timer_events(glui32 millisecs) {
@@ -881,6 +881,8 @@ void Glk::glk_cancel_hyperlink_event(winid_t win) {
 	}
 }
 
+/*--------------------------------------------------------------------------*/
+
 void Glk::glk_current_time(glktimeval_t *time) {
 	TimeAndDate td;
 	*time = td;
@@ -934,6 +936,8 @@ glsi32 Glk::glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor)
 	return ts / factor;
 }
 
+/*--------------------------------------------------------------------------*/
+
 /* XXX non-official Glk functions that may or may not exist */
 
 char *garglk_fileref_get_name(frefid_t fref) {
@@ -987,15 +991,4 @@ void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
 	// TODO
 }
 
-/*--------------------------------------------------------------------------*/
-
-void Glk::gliInputGuessFocus() {
-	// TODO
-}
-
-void Glk::gliSelect(event_t *event, bool polled) {
-	// TODO
-	event->type = evtype_Quit;
-}
-
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index cf06474..6bfffd6 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -38,13 +38,6 @@ private:
 	bool _gliFirstEvent;
 	unsigned char _charTolowerTable[256];
 	unsigned char _charToupperTable[256];
-private:
-	/**
-	 * Pick first window which might want input. This is called after every keystroke.
-	 */
-	void gliInputGuessFocus();
-
-	void gliSelect(event_t *event, bool polled);
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 2cfaebd..26244e3 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -716,7 +716,7 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 	if (!ev)
 		ev = &dummyEv;
 
-	g_vm->_events->clearEvent(ev);
+	ev->clear();
 
 	if (!_lineRequest && !_lineRequestUni)
 		return;
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 01c3193..4786d02 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -329,7 +329,7 @@ void TextGridWindow::cancelLineEvent(Event *ev) {
 	if (!ev)
 		ev = &dummyEv;
 
-	g_vm->_events->clearEvent(ev);
+	ev->clear();
 
 	if (!_lineRequest && !_lineRequestUni)
 		return;
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 2f2008c..19f61ba 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -276,6 +276,24 @@ void Windows::rearrange() {
 	}
 }
 
+void Windows::inputGuessFocus() {
+	Window *altWin = _focusWin;
+
+	do {
+		if (altWin
+			&& (altWin->_lineRequest || altWin->_charRequest ||
+				altWin->_lineRequestUni || altWin->_charRequestUni))
+			break;
+		altWin = iterateTreeOrder(altWin);
+	} while (altWin != _focusWin);
+
+	if (_focusWin != altWin) {
+		_focusWin = altWin;
+		_forceRedraw = true;
+		redraw();
+	}
+}
+
 void Windows::selectionChanged() {
 	_claimSelect = false;
 	_forceRedraw = true;
@@ -440,7 +458,7 @@ void Window::cancelLineEvent(Event *ev) {
 	if (!ev)
 		ev = &dummyEv;
 
-	g_vm->_events->clearEvent(ev);
+	ev->clear();
 }
 
 void Window::moveCursor(const Common::Point &newPos) {
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 2c03c15..b0ee311 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -149,6 +149,11 @@ public:
 	 */
 	void setFocus(Window *win) { _focusWin = win; }
 
+	/**
+	 * Pick first window which might want input. This is called after every keystroke.
+	 */
+	void inputGuessFocus();
+
 	void selectionChanged();
 
 	void clearClaimSelect() { _claimSelect = false; }


Commit: 7bbedcd099dfe552547b97482c1d974318d3ce16
    https://github.com/scummvm/scummvm/commit/7bbedcd099dfe552547b97482c1d974318d3ce16
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Beginnings of event handling

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index a4dadcb..6fcf634 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -28,8 +28,36 @@ void Events::getEvent(event_t *event, bool polled) {
 	// TODO
 }
 
-void Events::eventStore(EvType type, Window *win, uint32 val1, uint32 val2) {
-	// TODO
+void Events::store(EvType type, Window *win, uint32 val1, uint32 val2) {
+	Event ev(type, win, val1, val2);
+
+	switch (type) {
+	case evtype_Arrange:
+	case evtype_Redraw:
+	case evtype_SoundNotify:
+	case evtype_Timer:
+		_eventsPolled.push(ev);
+		break;
+
+	default:
+		_eventsLogged.push(ev);
+		break;
+	}
+}
+
+void Events::dispatchEvent(Event &ev, bool polled) {
+	Event dispatch;
+
+	if (!polled) {
+		dispatch = _eventsLogged.retrieve();
+		if (dispatch)
+			dispatch = _eventsPolled.retrieve();
+	} else {
+		dispatch = _eventsPolled.retrieve();
+	}
+
+	if (dispatch)
+		ev = dispatch;
 }
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 6140dc4..54a2774 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -100,7 +100,17 @@ struct Event {
 	/**
 	 * Constructor
 	 */
-	Event() : type(evtype_None), window(nullptr), val1(0), val2(0) {}
+	Event() { clear(); }
+
+	/**
+	 * Constructor
+	 */
+	Event(EvType evType, Window *evWindow, uint32 evVal1, uint32 evVal2) {
+		type = evType;
+		window = evWindow;
+		val1 = evVal1;
+		val2 = evVal2;
+	}
 
 	/**
 	 * Clear
@@ -110,10 +120,30 @@ struct Event {
 		window = nullptr;
 		val1 = val2 = 0;
 	}
+
+	/**
+	 * Boolean cast to allow checking whether event is filled out
+	 */
+	operator bool() const { return type != evtype_None; }
 };
 typedef Event event_t;
 
+class EventQueue : public Common::Queue<Event> {
+public:
+	/**
+	 * Retrieve a pending event, if any
+	 */
+	Event retrieve() {
+		return empty() ? Event() : pop();
+	}
+};
+
 class Events {
+private:
+	EventQueue _eventsPolled;
+	EventQueue _eventsLogged;
+private:
+	void dispatchEvent(Event &ev, bool polled);
 public:
 	bool _forceClick;
 public:
@@ -130,7 +160,7 @@ public:
 	/**
 	 * Store an event for retrieval
 	 */
-	void eventStore(EvType type, Window *win, uint32 val1 = 0, uint32 val2 = 0);
+	void store(EvType type, Window *win, uint32 val1 = 0, uint32 val2 = 0);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 26244e3..fa8a7cc 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -574,7 +574,7 @@ void TextBufferWindow::click(const Common::Point &newPos) {
 	if (_hyperRequest) {
 		glui32 linkval = g_vm->_windowMask->getHyperlink(newPos);
 		if (linkval) {
-			g_vm->_events->eventStore(evtype_Hyperlink, this, linkval, 0);
+			g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
 			_hyperRequest = false;
 			if (g_conf->_safeClicks)
 				g_vm->_events->_forceClick = 1;
@@ -1246,7 +1246,7 @@ void TextBufferWindow::acceptReadChar(glui32 arg) {
 
 	_charRequest = false;
 	_charRequestUni = false;
-	g_vm->_events->eventStore(evtype_CharInput, this, key, 0);
+	g_vm->_events->store(evtype_CharInput, this, key, 0);
 }
 
 void TextBufferWindow::acceptReadLine(glui32 arg) {
@@ -1479,11 +1479,11 @@ void TextBufferWindow::acceptLine(glui32 keycode) {
 		glui32 val2 = keycode;
 		if (val2 == keycode_Return)
 			val2 = 0;
-		g_vm->_events->eventStore(evtype_LineInput, this, len, val2);
+		g_vm->_events->store(evtype_LineInput, this, len, val2);
 		free(_lineTerminators);
 		_lineTerminators = nullptr;
 	} else {
-		g_vm->_events->eventStore(evtype_LineInput, this, len, 0);
+		g_vm->_events->store(evtype_LineInput, this, len, 0);
 	}
 
 	_lineRequest = false;
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 4786d02..e2ee06e 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -198,7 +198,7 @@ void TextGridWindow::click(const Common::Point &newPos) {
 		_windows->setFocus(this);
 
 	if (_mouseRequest) {
-		g_vm->_events->eventStore(evtype_MouseInput, this, x / g_conf->_cellW, y / g_conf->_leading);
+		g_vm->_events->store(evtype_MouseInput, this, x / g_conf->_cellW, y / g_conf->_leading);
 		_mouseRequest = false;
 		if (g_conf->_safeClicks)
 			g_vm->_events->_forceClick = true;
@@ -208,7 +208,7 @@ void TextGridWindow::click(const Common::Point &newPos) {
 		glui32 linkval = g_vm->_windowMask->getHyperlink(newPos);
 		if (linkval)
 		{
-			g_vm->_events->eventStore(evtype_Hyperlink, this, linkval, 0);
+			g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
 			_hyperRequest = false;
 			if (g_conf->_safeClicks)
 				g_vm->_events->_forceClick = true;
@@ -405,7 +405,7 @@ void TextGridWindow::acceptReadChar(glui32 arg) {
 
 	_charRequest = false;
 	_charRequestUni = false;
-	g_vm->_events->eventStore(evtype_CharInput, this, key, 0);
+	g_vm->_events->store(evtype_CharInput, this, key, 0);
 }
 
 void TextGridWindow::acceptLine(glui32 keycode) {
@@ -444,11 +444,11 @@ void TextGridWindow::acceptLine(glui32 keycode) {
 		glui32 val2 = keycode;
 		if (val2 == keycode_Return)
 			val2 = 0;
-		g_vm->_events->eventStore(evtype_LineInput, this, _inLen, val2);
+		g_vm->_events->store(evtype_LineInput, this, _inLen, val2);
 		free(_lineTerminators);
 		_lineTerminators = NULL;
 	} else {
-		g_vm->_events->eventStore(evtype_LineInput, this, _inLen, 0);
+		g_vm->_events->store(evtype_LineInput, this, _inLen, 0);
 	}
 	_lineRequest = false;
 	_lineRequestUni = false;


Commit: 9eb4debd158f0348c9a13f548c788d9f9d721f70
    https://github.com/scummvm/scummvm/commit/9eb4debd158f0348c9a13f548c788d9f9d721f70
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Logic for keypress event handling

Changed paths:
  A engines/gargoyle/clipboard.cpp
  A engines/gargoyle/clipboard.h
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/module.mk
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/clipboard.cpp b/engines/gargoyle/clipboard.cpp
new file mode 100644
index 0000000..ef11d55
--- /dev/null
+++ b/engines/gargoyle/clipboard.cpp
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 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 "gargoyle/clipboard.h"
+
+namespace Gargoyle {
+
+void Clipboard::store(const uint32 *text, size_t len) {
+	// TODO
+}
+
+void Clipboard::send() {
+	// TODO
+}
+
+void Clipboard::receive() {
+	// TODO
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/clipboard.h b/engines/gargoyle/clipboard.h
new file mode 100644
index 0000000..ccd8d79
--- /dev/null
+++ b/engines/gargoyle/clipboard.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_CLIPBOARD_H
+#define GARGOYLE_CLIPBOARD_H
+
+#include "common/array.h"
+
+namespace Gargoyle {
+
+class Clipboard {
+private:
+	Common::Array<uint32> _text;
+public:
+	void store(const uint32 *text, size_t len);
+
+	void send();
+
+	void receive();
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 6fcf634..00adce8 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -21,11 +21,35 @@
  */
 
 #include "gargoyle/events.h"
+#include "gargoyle/clipboard.h"
+#include "gargoyle/gargoyle.h"
+#include "gargoyle/windows.h"
 
 namespace Gargoyle {
 
 void Events::getEvent(event_t *event, bool polled) {
-	// TODO
+	_currentEvent  = event;
+	event->clear();
+
+	Common::Event ev;
+	dispatchEvent(*_currentEvent, polled);
+
+	if (!polled) {
+		while (!g_vm->shouldQuit() && _currentEvent->type == evtype_None && !_timeouts) {
+			pollEvents();
+			g_system->delayMillis(10);
+
+			dispatchEvent(*_currentEvent, polled);
+		}
+	}
+
+	if (_currentEvent->type == evtype_None && _timeouts) {
+		store(evtype_Timer, NULL, 0, 0);
+		dispatchEvent(*_currentEvent, polled);
+		_timeouts = false;
+	}
+
+	_currentEvent = nullptr;
 }
 
 void Events::store(EvType type, Window *win, uint32 val1, uint32 val2) {
@@ -60,4 +84,76 @@ void Events::dispatchEvent(Event &ev, bool polled) {
 		ev = dispatch;
 }
 
+void Events::pollEvents() {
+	Common::Event event;
+
+	do {
+		g_system->getEventManager()->pollEvent(event);
+
+		switch (event.type) {
+		case Common::EVENT_KEYDOWN:
+			handleKeyDown(event.kbd);
+			return;
+		default:
+			break;
+		}
+	} while (event.type == Common::EVENT_MOUSEMOVE);
+}
+
+void Events::handleKeyDown(const Common::KeyState &ks) {
+	Clipboard &clipboard = *g_vm->_clipboard;
+	Windows &windows = *g_vm->_windows;
+
+	if (ks.flags & Common::KBD_CTRL) {
+		if (ks.keycode == Common::KEYCODE_a)
+			windows.inputHandleKey(keycode_Home);
+		else if (ks.keycode == Common::KEYCODE_c)
+			clipboard.send();
+		else if (ks.keycode == Common::KEYCODE_e)
+			windows.inputHandleKey(keycode_End);
+		else if (ks.keycode == Common::KEYCODE_u)
+			windows.inputHandleKey(keycode_Escape);
+		else if (ks.keycode == Common::KEYCODE_v)
+			clipboard.receive();
+		else if (ks.keycode == Common::KEYCODE_x)
+			clipboard.send();
+		else if (ks.keycode == Common::KEYCODE_LEFT || ks.keycode == Common::KEYCODE_KP4)
+			windows.inputHandleKey(keycode_SkipWordLeft);
+		else if (ks.keycode == Common::KEYCODE_RIGHT || ks.keycode == Common::KEYCODE_KP6)
+			windows.inputHandleKey(keycode_SkipWordRight);
+
+		return;
+	}
+
+	if (ks.flags & Common::KBD_ALT)
+		return;
+
+	if (ks.keycode == Common::KEYCODE_RETURN) windows.inputHandleKey(keycode_Return);
+	else if (ks.keycode == Common::KEYCODE_BACKSPACE) windows.inputHandleKey(keycode_Delete);
+	else if (ks.keycode == Common::KEYCODE_DELETE) windows.inputHandleKey(keycode_Erase);
+	else if (ks.keycode == Common::KEYCODE_TAB) windows.inputHandleKey(keycode_Tab);
+	else if (ks.keycode == Common::KEYCODE_UP) windows.inputHandleKey(keycode_PageUp);
+	else if (ks.keycode == Common::KEYCODE_PAGEDOWN) windows.inputHandleKey(keycode_PageDown);
+	else if (ks.keycode == Common::KEYCODE_HOME) windows.inputHandleKey(keycode_Home);
+	else if (ks.keycode == Common::KEYCODE_END) windows.inputHandleKey(keycode_End);
+	else if (ks.keycode == Common::KEYCODE_LEFT) windows.inputHandleKey(keycode_Left);
+	else if (ks.keycode == Common::KEYCODE_RIGHT) windows.inputHandleKey(keycode_Right);
+	else if (ks.keycode == Common::KEYCODE_UP) windows.inputHandleKey(keycode_Up);
+	else if (ks.keycode == Common::KEYCODE_DOWN) windows.inputHandleKey(keycode_Down);
+	else if (ks.keycode == Common::KEYCODE_ESCAPE) windows.inputHandleKey(keycode_Escape);
+	else if (ks.keycode == Common::KEYCODE_F1) windows.inputHandleKey(keycode_Func1);
+	else if (ks.keycode == Common::KEYCODE_F2) windows.inputHandleKey(keycode_Func2);
+	else if (ks.keycode == Common::KEYCODE_F3) windows.inputHandleKey(keycode_Func3);
+	else if (ks.keycode == Common::KEYCODE_F4) windows.inputHandleKey(keycode_Func4);
+	else if (ks.keycode == Common::KEYCODE_F5) windows.inputHandleKey(keycode_Func5);
+	else if (ks.keycode == Common::KEYCODE_F6) windows.inputHandleKey(keycode_Func6);
+	else if (ks.keycode == Common::KEYCODE_F7) windows.inputHandleKey(keycode_Func7);
+	else if (ks.keycode == Common::KEYCODE_F8) windows.inputHandleKey(keycode_Func8);
+	else if (ks.keycode == Common::KEYCODE_F9) windows.inputHandleKey(keycode_Func9);
+	else if (ks.keycode == Common::KEYCODE_F10) windows.inputHandleKey(keycode_Func10);
+	else if (ks.keycode == Common::KEYCODE_F11) windows.inputHandleKey(keycode_Func11);
+	else if (ks.keycode == Common::KEYCODE_F12) windows.inputHandleKey(keycode_Func12);
+	else windows.inputHandleKey(ks.ascii);
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 54a2774..cd0a3a6 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -138,19 +138,37 @@ public:
 	}
 };
 
+/**
+ * Events manager
+ */
 class Events {
 private:
-	EventQueue _eventsPolled;
-	EventQueue _eventsLogged;
+	EventQueue _eventsPolled;		///< User generated events
+	EventQueue _eventsLogged;		///< Custom events generated by game code
+	Event *_currentEvent;			///< Event pointer passed during event retrieval
+	bool _timeouts;					///< Timer timeouts flag
 private:
+	/**
+	 * Dispatches an event
+	 */
 	void dispatchEvent(Event &ev, bool polled);
+
+	/**
+	 * Poll for user events
+	 */
+	void pollEvents();
+
+	/**
+	 * Handle a key down event
+	 */
+	void handleKeyDown(const Common::KeyState &ks);
 public:
 	bool _forceClick;
 public:
 	/**
 	 * Constructor
 	 */
-	Events() : _forceClick(false) {}
+	Events() : _forceClick(false), _currentEvent(nullptr), _timeouts(false) {}
 
 	/**
 	  * Get any pending event
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 1a8cc61..1f74c31 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -29,6 +29,7 @@
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/clipboard.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/events.h"
 #include "gargoyle/picture.h"
@@ -42,14 +43,15 @@ namespace Gargoyle {
 GargoyleEngine *g_vm;
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _conf(nullptr),
-		_events(nullptr), _picList(nullptr), _screen(nullptr), _windows(nullptr),
-		_windowMask(nullptr), _copySelect(false),
+		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _clipboard(nullptr),
+		_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr), _windows(nullptr),
+		_windowMask(nullptr), _copySelect(false), _terminated(false),
 		gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
 	g_vm = this;
 }
 
 GargoyleEngine::~GargoyleEngine() {
+	delete _clipboard;
 	delete _conf;
 	delete _events;
 	delete _picList;
@@ -68,6 +70,7 @@ void GargoyleEngine::initialize() {
 
 	initGraphics(640, 480, false);
 	_screen = new Screen();
+	_clipboard = new Clipboard();
 	_conf = new Conf();
 	_events = new Events();
 	_picList = new PicList();
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index c34538b..9677d67 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -33,6 +33,7 @@
 
 namespace Gargoyle {
 
+class Clipboard;
 class Conf;
 class Events;
 class PicList;
@@ -93,6 +94,7 @@ protected:
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
 public:
+	Clipboard *_clipboard;
 	Conf *_conf;
 	Events *_events;
 	PicList *_picList;
@@ -101,6 +103,7 @@ public:
 	Windows *_windows;
 	WindowMask *_windowMask;
 	bool _copySelect;
+	bool _terminated;
 	void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock);
 	gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, const char *typecode);
 	void (*gli_unregister_arr)(void *array, glui32 len, const char *typecode, gidispatch_rock_t objrock);
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index b8ef788..a45608f 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -1,6 +1,7 @@
 MODULE := engines/gargoyle
 
 MODULE_OBJS := \
+	clipboard.o \
 	conf.o \
 	detection.o \
 	events.o \
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index fa8a7cc..c322127 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "gargoyle/window_text_buffer.h"
+#include "gargoyle/clipboard.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/screen.h"
@@ -1155,7 +1156,7 @@ void TextBufferWindow::redraw() {
     if (selbuf && _copyPos) {
         Windows::_claimSelect = true;
 
-		copyTextToClipboard(_copyBuf, _copyPos);
+		g_vm->_clipboard->store(_copyBuf, _copyPos);
         for (i = 0; i < _copyPos; i++)
             _copyBuf[i] = 0;
         _copyPos = 0;
@@ -1623,10 +1624,6 @@ int TextBufferWindow::calcWidth(glui32 *chars, Attributes *attrs, int startchar,
 	return w;
 }
 
-void TextBufferWindow::copyTextToClipboard(const glui32 *text, size_t len) {
-	// TODO
-}
-
 void TextBufferWindow::getSize(glui32 *width, glui32 *height) const {
 	if (width)
 		*width = (_bbox.width() - g_conf->_tMarginX * 2) / g_conf->_cellW;
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index be9ffcb..61b30e2 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -84,11 +84,6 @@ private:
 	void scrollOneLine(bool forced);
 	void scrollResize();
 	int calcWidth(glui32 *chars, Attributes *attrs, int startchar, int numchars, int spw);
-
-	/**
-	 * Copy the passed text to the clipboard
-	 */
-	void copyTextToClipboard(const glui32 *text, size_t len);
 public:
 	int _width, _height;
 	int _spaced;
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 19f61ba..06b83e1 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -294,6 +294,96 @@ void Windows::inputGuessFocus() {
 	}
 }
 
+void Windows::inputMoreFocus() {
+	Window *altWin = _focusWin;
+
+	do {
+		if (altWin && altWin->_moreRequest)
+			break;
+		altWin = iterateTreeOrder(altWin);
+	} while (altWin != _focusWin);
+
+	_focusWin = altWin;
+}
+
+void Windows::inputNextFocus() {
+	Window *altWin = _focusWin;
+
+	do
+	{
+		altWin = iterateTreeOrder(altWin);
+		if (altWin
+			&& (altWin->_lineRequest || altWin->_charRequest ||
+				altWin->_lineRequestUni || altWin->_charRequestUni))
+			break;
+	} while (altWin != _focusWin);
+
+	if (_focusWin  != altWin) {
+		_focusWin = altWin;
+		_forceRedraw = true;
+		redraw();
+	}
+}
+
+void Windows::inputScrollFocus() {
+	Window *altWin = _focusWin;
+
+	do {
+		if (altWin && altWin->_scrollRequest)
+			break;
+		altWin = iterateTreeOrder(altWin);
+	} while (altWin != _focusWin);
+
+	_focusWin = altWin;
+}
+
+void Windows::inputHandleKey(glui32 key) {
+	if (_moreFocus) {
+		inputMoreFocus();
+	} else {
+		switch (key) {
+		case keycode_Tab:
+			inputNextFocus();
+			return;
+		case keycode_PageUp:
+		case keycode_PageDown:
+		case keycode_MouseWheelUp:
+		case keycode_MouseWheelDown:
+			inputScrollFocus();
+			break;
+		default:
+			inputGuessFocus();
+			break;
+		}
+	}
+
+	Window *win = _focusWin;
+	if (!win)
+		return;
+
+	bool deferExit = false;
+
+	TextGridWindow *gridWindow = dynamic_cast<TextGridWindow *>(win);
+	TextBufferWindow *bufWindow = dynamic_cast<TextBufferWindow *>(win);
+
+	if (gridWindow) {
+		if (gridWindow->_charRequest || gridWindow->_charRequestUni)
+			gridWindow->acceptReadChar(key);
+		else if (gridWindow->_lineRequest || gridWindow->_lineRequestUni)
+			gridWindow->acceptReadLine(key);
+	} else if (bufWindow) {
+		if (bufWindow->_charRequest || bufWindow->_charRequestUni)
+			bufWindow->acceptReadChar(key);
+		else if (bufWindow->_lineRequest || bufWindow->_lineRequestUni)
+			bufWindow->acceptReadLine(key);
+		else if (bufWindow->_moreRequest || bufWindow->_scrollRequest)
+			deferExit = bufWindow->acceptScroll(key);
+	}
+
+	if (!deferExit && g_vm->_terminated)
+		g_vm->quitGame();
+}
+
 void Windows::selectionChanged() {
 	_claimSelect = false;
 	_forceRedraw = true;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index b0ee311..0840822 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -101,6 +101,23 @@ private:
 	void refocus(Window *win);
 
 	Window *iterateTreeOrder(Window *win);
+
+
+	/**
+	 * Pick first window which has a more request
+	 */
+	void inputMoreFocus();
+
+	/**
+	 *
+	 */
+	void inputNextFocus();
+
+	/**
+	 * Pick first window which might want scrolling.
+	 * This is called after pressing page keys.
+	 */
+	void inputScrollFocus();
 public:
 	static bool _overrideReverse;
 	static bool _overrideFgSet;
@@ -154,6 +171,11 @@ public:
 	 */
 	void inputGuessFocus();
 
+	/**
+	 * Handle input keypress
+	 */
+	void inputHandleKey(glui32 key);
+
 	void selectionChanged();
 
 	void clearClaimSelect() { _claimSelect = false; }


Commit: efacc17bd1bea3f0ca316c57f90a5ca9849932b6
    https://github.com/scummvm/scummvm/commit/efacc17bd1bea3f0ca316c57f90a5ca9849932b6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Handle more user events

Changed paths:
    engines/gargoyle/clipboard.cpp
    engines/gargoyle/clipboard.h
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/clipboard.cpp b/engines/gargoyle/clipboard.cpp
index ef11d55..69ef8f8 100644
--- a/engines/gargoyle/clipboard.cpp
+++ b/engines/gargoyle/clipboard.cpp
@@ -28,11 +28,11 @@ void Clipboard::store(const uint32 *text, size_t len) {
 	// TODO
 }
 
-void Clipboard::send() {
+void Clipboard::send(ClipSource source) {
 	// TODO
 }
 
-void Clipboard::receive() {
+void Clipboard::receive(ClipSource source) {
 	// TODO
 }
 
diff --git a/engines/gargoyle/clipboard.h b/engines/gargoyle/clipboard.h
index ccd8d79..23e7aaf 100644
--- a/engines/gargoyle/clipboard.h
+++ b/engines/gargoyle/clipboard.h
@@ -27,15 +27,17 @@
 
 namespace Gargoyle {
 
+enum ClipSource { PRIMARY = 0, CLIPBOARD = 1 };
+
 class Clipboard {
 private:
 	Common::Array<uint32> _text;
 public:
 	void store(const uint32 *text, size_t len);
 
-	void send();
+	void send(ClipSource source);
 
-	void receive();
+	void receive(ClipSource source);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 00adce8..b1f1823 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -94,6 +94,13 @@ void Events::pollEvents() {
 		case Common::EVENT_KEYDOWN:
 			handleKeyDown(event.kbd);
 			return;
+		case Common::EVENT_WHEELUP:
+		case Common::EVENT_WHEELDOWN:
+			handleScroll(event.type == Common::EVENT_WHEELUP);
+			return;
+		case Common::EVENT_LBUTTONDOWN:
+		case Common::EVENT_RBUTTONDOWN:
+
 		default:
 			break;
 		}
@@ -108,15 +115,15 @@ void Events::handleKeyDown(const Common::KeyState &ks) {
 		if (ks.keycode == Common::KEYCODE_a)
 			windows.inputHandleKey(keycode_Home);
 		else if (ks.keycode == Common::KEYCODE_c)
-			clipboard.send();
+			clipboard.send(CLIPBOARD);
 		else if (ks.keycode == Common::KEYCODE_e)
 			windows.inputHandleKey(keycode_End);
 		else if (ks.keycode == Common::KEYCODE_u)
 			windows.inputHandleKey(keycode_Escape);
 		else if (ks.keycode == Common::KEYCODE_v)
-			clipboard.receive();
+			clipboard.receive(CLIPBOARD);
 		else if (ks.keycode == Common::KEYCODE_x)
-			clipboard.send();
+			clipboard.send(CLIPBOARD);
 		else if (ks.keycode == Common::KEYCODE_LEFT || ks.keycode == Common::KEYCODE_KP4)
 			windows.inputHandleKey(keycode_SkipWordLeft);
 		else if (ks.keycode == Common::KEYCODE_RIGHT || ks.keycode == Common::KEYCODE_KP6)
@@ -156,4 +163,38 @@ void Events::handleKeyDown(const Common::KeyState &ks) {
 	else windows.inputHandleKey(ks.ascii);
 }
 
+void Events::handleScroll(bool wheelUp) {
+	g_vm->_windows->inputHandleKey(wheelUp ? keycode_MouseWheelUp : keycode_MouseWheelDown);
+}
+
+void Events::handleMouseMove(const Common::Point &pos) {
+	// hyperlinks and selection
+	// TODO: Properly handle commented out lines
+	if (g_vm->_copySelect) {
+		//gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_ibeam);
+		g_vm->_windowMask->moveSelection(pos);
+	} else {
+		if (g_vm->_windowMask->getHyperlink(pos)) {
+			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_hand);
+		} else {
+			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), NULL);
+		}
+	}
+}
+
+void Events::handleButtonDown(bool isLeft, const Common::Point &pos) {
+	if (isLeft)
+		g_vm->_windows->inputHandleClick(pos);
+	else
+		g_vm->_clipboard->receive(PRIMARY);
+}
+
+void Events::handleButtonUp(bool isLeft, const Common::Point &pos) {
+	if (isLeft) {
+		g_vm->_copySelect = false;
+		//gdk_window_set_cursor((GTK_WIDGET(widget)->window), NULL);
+		g_vm->_clipboard->send(PRIMARY);
+	}
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index cd0a3a6..544b524 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -162,6 +162,26 @@ private:
 	 * Handle a key down event
 	 */
 	void handleKeyDown(const Common::KeyState &ks);
+
+	/**
+	 * Handle scroll events
+	 */
+	void handleScroll(bool wheelUp);
+
+	/**
+	 * Handle mouse move events
+	 */
+	void handleMouseMove(const Common::Point &pos);
+
+	/**
+	 * Handle mouse down events
+	 */
+	void handleButtonDown(bool isLeft, const Common::Point &pos);
+
+	/**
+	 * Handle mouse up events
+	 */
+	void handleButtonUp(bool isLeft, const Common::Point &pos);
 public:
 	bool _forceClick;
 public:
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 06b83e1..0f1c9eb 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -384,6 +384,11 @@ void Windows::inputHandleKey(glui32 key) {
 		g_vm->quitGame();
 }
 
+void Windows::inputHandleClick(const Common::Point &pos) {
+	if (_rootWin)
+		_rootWin->click(pos);
+}
+
 void Windows::selectionChanged() {
 	_claimSelect = false;
 	_forceRedraw = true;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 0840822..643fecf 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -176,6 +176,11 @@ public:
 	 */
 	void inputHandleKey(glui32 key);
 
+	/**
+	 * Handle mouse clicks
+	 */
+	void inputHandleClick(const Common::Point &pos);
+
 	void selectionChanged();
 
 	void clearClaimSelect() { _claimSelect = false; }


Commit: 65fa4fa7cbfeb5eae8da6e8a4a077f3c8e1866f6
    https://github.com/scummvm/scummvm/commit/65fa4fa7cbfeb5eae8da6e8a4a077f3c8e1866f6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Beginnings of font handling

Changed paths:
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/screen.h


diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 9c84a12..9264db2 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -22,9 +22,54 @@
 
 #include "gargoyle/fonts.h"
 #include "gargoyle/glk_types.h"
+#include "gargoyle/conf.h"
 
 namespace Gargoyle {
 
+const char *gli_conf_propr = "NotoSerif-Regular";
+const char *gli_conf_propb = "NotoSerif-Bold";
+const char *gli_conf_propi = "NotoSerif-Italic";
+const char *gli_conf_propz = "NotoSerif-BoldItalic";
+
+const char *gli_conf_monor = "GoMono-Regular";
+const char *gli_conf_monob = "GoMono-Bold";
+const char *gli_conf_monoi = "GoMono-Italic";
+const char *gli_conf_monoz = "GoMono-BoldItalic";
+
+#ifdef BUNDLED_FONTS
+const char *gli_conf_monofont = "";
+const char *gli_conf_propfont = "";
+const double gli_conf_monosize = 12.5;	///< good size for GoMono
+const double gli_conf_propsize = 13.4;	///< good size for NotoSerif
+#else
+const char *gli_conf_monofont = "Liberation Mono";
+const char *gli_conf_propfont = "Linux Libertine O";
+const double gli_conf_monosize = 12.5;	///< good size for LiberationMono
+const double gli_conf_propsize = 15.5;	///< good size for Libertine
+#endif
+
+Fonts::Fonts() {
+	double monoAspect = g_conf->_monoAspect;
+	double propAspect = g_conf->_propAspect;
+	double monoSize = g_conf->_monoSize;
+	double propSize = g_conf->_propSize;
+
+	_fontTable[0] = new Font(gli_conf_monor, monoSize, monoAspect, FONTR);
+	_fontTable[1] = new Font(gli_conf_monob, monoSize, monoAspect, FONTB);
+	_fontTable[2] = new Font(gli_conf_monoi, monoSize, monoAspect, FONTI);
+	_fontTable[3] = new Font(gli_conf_monoz, monoSize, monoAspect, FONTZ);
+
+	_fontTable[4] = new Font(gli_conf_propr, propSize, propAspect, FONTR);
+	_fontTable[5] = new Font(gli_conf_propb, propSize, propAspect, FONTB);
+	_fontTable[6] = new Font(gli_conf_propi, propSize, propAspect, FONTI);
+	_fontTable[7] = new Font(gli_conf_propz, propSize, propAspect, FONTZ);
+}
+
+Fonts::~Fonts() {
+	for (int idx = 0; idx < FONTS_TOTAL; ++idx)
+		delete _fontTable[idx];
+}
+
 FACES Fonts::getId(const Common::String &name) {
 	if (name == "monor") return MONOR;
 	if (name == "monob") return MONOB;
@@ -37,4 +82,8 @@ FACES Fonts::getId(const Common::String &name) {
 	return MONOR;
 }
 
+/*--------------------------------------------------------------------------*/
+
+
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index f2f701a..c303474 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -28,16 +28,40 @@
 
 namespace Gargoyle {
 
+#define FONTS_TOTAL 8
+
 enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
 enum TYPES { MONOF, PROPF };
 enum STYLES { FONTR, FONTB, FONTI, FONTZ };
 
+class Font {
+public:
+	/**
+	 * Constructor
+	 */
+	Font(const char *name, double size, double aspect, STYLES style) {
+		// TODO
+	}
+};
+
 class Fonts {
+private:
+	Font *_fontTable[FONTS_TOTAL];
 public:
 	/**
 	 * Get the index/id of a font by name
 	 */
 	static FACES getId(const Common::String &name);
+public:
+	/**
+	 * Constructor
+	 */
+	Fonts();
+
+	/**
+	 * Destructor
+	 */
+	virtual ~Fonts();
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 1f74c31..3135fcb 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -69,9 +69,10 @@ void GargoyleEngine::initialize() {
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
 
 	initGraphics(640, 480, false);
+	_conf = new Conf();
 	_screen = new Screen();
+
 	_clipboard = new Clipboard();
-	_conf = new Conf();
 	_events = new Events();
 	_picList = new PicList();
 	_streams = new Streams();
diff --git a/engines/gargoyle/screen.h b/engines/gargoyle/screen.h
index ce9088d..b1d24fa 100644
--- a/engines/gargoyle/screen.h
+++ b/engines/gargoyle/screen.h
@@ -24,10 +24,11 @@
 #define GARGOYLE_DRAW_H
 
 #include "graphics/screen.h"
+#include "gargoyle/fonts.h"
 
 namespace Gargoyle {
 
-class Screen : public Graphics::Screen {
+class Screen : public Graphics::Screen, Fonts {
 public:
 	/**
 	 * Fills the screen with a given rgb color


Commit: e5854a6bd35fb29912bc185af32a3443598dc3cb
    https://github.com/scummvm/scummvm/commit/e5854a6bd35fb29912bc185af32a3443598dc3cb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add loading of TrueType fonts

Changed paths:
    engines/gargoyle/configure.engine
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h


diff --git a/engines/gargoyle/configure.engine b/engines/gargoyle/configure.engine
index 671f91c..3569297 100644
--- a/engines/gargoyle/configure.engine
+++ b/engines/gargoyle/configure.engine
@@ -1,3 +1,3 @@
 # This file is included from the main "configure" script
 # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine gargoyle "Interactive Fiction games" no
+add_engine gargoyle "Interactive Fiction games" no "" "" "freetype2"
diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 9264db2..41067a5 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -23,6 +23,8 @@
 #include "gargoyle/fonts.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/conf.h"
+#include "common/file.h"
+#include "graphics/fonts/ttf.h"
 
 namespace Gargoyle {
 
@@ -54,15 +56,15 @@ Fonts::Fonts() {
 	double monoSize = g_conf->_monoSize;
 	double propSize = g_conf->_propSize;
 
-	_fontTable[0] = new Font(gli_conf_monor, monoSize, monoAspect, FONTR);
-	_fontTable[1] = new Font(gli_conf_monob, monoSize, monoAspect, FONTB);
-	_fontTable[2] = new Font(gli_conf_monoi, monoSize, monoAspect, FONTI);
-	_fontTable[3] = new Font(gli_conf_monoz, monoSize, monoAspect, FONTZ);
+	_fontTable[0] = loadFont(MONOR, monoSize, monoAspect, FONTR);
+	_fontTable[1] = loadFont(MONOB, monoSize, monoAspect, FONTB);
+	_fontTable[2] = loadFont(MONOI, monoSize, monoAspect, FONTI);
+	_fontTable[3] = loadFont(MONOZ, monoSize, monoAspect, FONTZ);
 
-	_fontTable[4] = new Font(gli_conf_propr, propSize, propAspect, FONTR);
-	_fontTable[5] = new Font(gli_conf_propb, propSize, propAspect, FONTB);
-	_fontTable[6] = new Font(gli_conf_propi, propSize, propAspect, FONTI);
-	_fontTable[7] = new Font(gli_conf_propz, propSize, propAspect, FONTZ);
+	_fontTable[4] = loadFont(PROPR, propSize, propAspect, FONTR);
+	_fontTable[5] = loadFont(PROPB, propSize, propAspect, FONTB);
+	_fontTable[6] = loadFont(PROPI, propSize, propAspect, FONTI);
+	_fontTable[7] = loadFont(PROPZ, propSize, propAspect, FONTZ);
 }
 
 Fonts::~Fonts() {
@@ -82,8 +84,24 @@ FACES Fonts::getId(const Common::String &name) {
 	return MONOR;
 }
 
-/*--------------------------------------------------------------------------*/
+Graphics::Font *Fonts::loadFont(FACES face, double size, double aspect, int style) {
+	static const char *const MAP[8] = {
+		"Go-Mono-Regular",
+		"Go-Mono-Bold",
+		"Go-Mono-Italic",
+		"Go-Mono-BoldItalic",
+		"NotoSerif-Regular",
+		"NotoSerif-Bold",
+		"NotoSerif-Italic",
+		"NotoSerif-BoldItalic"
+	};
 
+	Common::File f;
+	if (!f.open(Common::String::format("%s.ttf", MAP[face])))
+		return nullptr;
+
+	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
+}
 
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index c303474..be00a8d 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -25,6 +25,7 @@
 
 #include "gargoyle/glk_types.h"
 #include "common/str.h"
+#include "graphics/font.h"
 
 namespace Gargoyle {
 
@@ -34,19 +35,19 @@ enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
 enum TYPES { MONOF, PROPF };
 enum STYLES { FONTR, FONTB, FONTI, FONTZ };
 
+/*
 class Font {
+
 public:
-	/**
-	 * Constructor
-	 */
-	Font(const char *name, double size, double aspect, STYLES style) {
-		// TODO
-	}
+	Font(const char *name, double size, double aspect, STYLES style);
 };
+*/
 
 class Fonts {
 private:
-	Font *_fontTable[FONTS_TOTAL];
+	Graphics::Font *_fontTable[FONTS_TOTAL];
+private:
+	Graphics::Font *loadFont(FACES face, double size, double aspect, int style);
 public:
 	/**
 	 * Get the index/id of a font by name


Commit: 6807e646b48a7b3850496bda890809b53a3093f5
    https://github.com/scummvm/scummvm/commit/6807e646b48a7b3850496bda890809b53a3093f5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix initialization of text buffer window lines

Changed paths:
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index c322127..818bcf0 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -45,6 +45,10 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windo
 	_type = wintype_TextBuffer;
 	Common::fill(&_history[0], &_history[HISTORYLEN], nullptr);
 
+	_lines.resize(SCROLLBACK);
+	_chars = _lines[0]._chars;
+	_attrs = _lines[0]._attrs;
+
 	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], _styles);
 }
 


Commit: b23291b9777311d73363bd97f2392402d2b2eb6c
    https://github.com/scummvm/scummvm/commit/b23291b9777311d73363bd97f2392402d2b2eb6c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Fix loading game data

Changed paths:
    engines/gargoyle/scott/scott.cpp


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 9632408..d533a7e 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -325,7 +325,7 @@ char *Scott::ReadString(Common::SeekableReadStream *f) {
 			tmp[ct++] = c;
 		else
 			tmp[ct++] = '?';
-	} while (1);
+	}
 
 	tmp[ct] = 0;
 	t = (char *)MemAlloc(ct + 1);
@@ -1263,15 +1263,11 @@ int Scott::xstrncasecmp(const char *s1, const char *s2, size_t n) {
 void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
 	va_list va;
 	va_start(va, count);
-	unsigned char c = '\0';
+	unsigned char c = f->readByte();
 
 	for (size_t idx = 0; idx < count; ++idx) {
-		if (idx > 0) {
-			while (f->pos() < f->size() && Common::isSpace(c))
-				c = f->readByte();
-		} else {
+		while (f->pos() < f->size() && Common::isSpace(c))
 			c = f->readByte();
-		}
 
 		// Get the next value
 		int *val = (int *)va_arg(va, int);


Commit: 998cf547eda31ac7b3c67c5dd0e2d1249ddf4741
    https://github.com/scummvm/scummvm/commit/998cf547eda31ac7b3c67c5dd0e2d1249ddf4741
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Exit game when ScummVM is closed

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/scott/scott.cpp


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index b1f1823..bba9df1 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -31,7 +31,6 @@ void Events::getEvent(event_t *event, bool polled) {
 	_currentEvent  = event;
 	event->clear();
 
-	Common::Event ev;
 	dispatchEvent(*_currentEvent, polled);
 
 	if (!polled) {
@@ -41,6 +40,9 @@ void Events::getEvent(event_t *event, bool polled) {
 
 			dispatchEvent(*_currentEvent, polled);
 		}
+
+		if (g_vm->shouldQuit())
+			_currentEvent->type = evtype_Quit;
 	}
 
 	if (_currentEvent->type == evtype_None && _timeouts) {
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index d533a7e..3812392 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -92,6 +92,9 @@ Distributed under the GNU software license\n\n");
 
 		if (GetInput(&vb, &no) == -1)
 			continue;
+		if (g_vm->shouldQuit())
+			return;
+
 		switch (PerformActions(vb, no)) {
 		case -1:
 			Output("I don't understand your command. ");
@@ -567,8 +570,9 @@ void Scott::LineInput(char *buf, size_t n) {
 
 	do {
 		glk_select(&ev);
-
-		if (ev.type == evtype_LineInput)
+		if (ev.type == evtype_Quit)
+			return;
+		else if (ev.type == evtype_LineInput)
 			break;
 		else if (ev.type == evtype_Arrange && split_screen)
 			Look();
@@ -654,6 +658,9 @@ int Scott::GetInput(int *vb, int *no) {
 		do {
 			Output("\nTell me what to do ? ");
 			LineInput(buf, sizeof buf);
+			if (g_vm->shouldQuit())
+				return 0;
+
 			num = sscanf(buf, "%9s %9s", verb, noun);
 		} while (num == 0 || *buf == '\n');
 		


Commit: 9d72d6007e10ebdfcede91e03385eb486f452a7c
    https://github.com/scummvm/scummvm/commit/9d72d6007e10ebdfcede91e03385eb486f452a7c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Move font method stubs from Screen to Fonts

Changed paths:
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h
    engines/gargoyle/screen.cpp
    engines/gargoyle/screen.h


diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 41067a5..41b2f65 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -50,7 +50,7 @@ const double gli_conf_monosize = 12.5;	///< good size for LiberationMono
 const double gli_conf_propsize = 15.5;	///< good size for Libertine
 #endif
 
-Fonts::Fonts() {
+Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface) {
 	double monoAspect = g_conf->_monoAspect;
 	double propAspect = g_conf->_propAspect;
 	double monoSize = g_conf->_monoSize;
@@ -103,5 +103,24 @@ Graphics::Font *Fonts::loadFont(FACES face, double size, double aspect, int styl
 	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
 }
 
+int Fonts::drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+int Fonts::drawStringUni(int x, int y, int fidx, const byte *rgb, const uint32 *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+int Fonts::stringWidth(int fidx, const char *s, int n, int spw) {
+	// TODO
+	return 0;
+}
+
+int Fonts::stringWidthUni(int fidx, const uint32 *s, int n, int spw) {
+	// TODO
+	return 0;
+}
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index be00a8d..35db815 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -35,16 +35,9 @@ enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
 enum TYPES { MONOF, PROPF };
 enum STYLES { FONTR, FONTB, FONTI, FONTZ };
 
-/*
-class Font {
-
-public:
-	Font(const char *name, double size, double aspect, STYLES style);
-};
-*/
-
 class Fonts {
 private:
+	Graphics::ManagedSurface *_surface;
 	Graphics::Font *_fontTable[FONTS_TOTAL];
 private:
 	Graphics::Font *loadFont(FACES face, double size, double aspect, int style);
@@ -57,12 +50,20 @@ public:
 	/**
 	 * Constructor
 	 */
-	Fonts();
+	Fonts(Graphics::ManagedSurface *surface);
 
 	/**
 	 * Destructor
 	 */
 	virtual ~Fonts();
+
+	int drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw);
+
+	int drawStringUni(int x, int y, int fidx, const byte *rgb, const uint32 *s, int n, int spw);
+
+	int stringWidth(int fidx, const char *s, int n, int spw);
+
+	int stringWidthUni(int fidx, const uint32 *s, int n, int spw);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/screen.cpp b/engines/gargoyle/screen.cpp
index dad3e6d..f541b76 100644
--- a/engines/gargoyle/screen.cpp
+++ b/engines/gargoyle/screen.cpp
@@ -34,26 +34,6 @@ void Screen::fillRect(uint x, uint y, uint w, uint h, const byte *rgb) {
 	Graphics::Screen::fillRect(Common::Rect(x, y, x + w, y + h), color);
 }
 
-int Screen::drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw) {
-	// TODO
-	return 0;
-}
-
-int Screen::drawStringUni(int x, int y, int fidx, const byte *rgb, const uint32 *s, int n, int spw) {
-	// TODO
-	return 0;
-}
-
-int Screen::stringWidth(int fidx, const char *s, int n, int spw) {
-	// TODO
-	return 0;
-}
-
-int Screen::stringWidthUni(int fidx, const uint32 *s, int n, int spw) {
-	// TODO
-	return 0;
-}
-
 void Screen::drawCaret(const Common::Point &pos) {
 	// TODO
 }
diff --git a/engines/gargoyle/screen.h b/engines/gargoyle/screen.h
index b1d24fa..8cdf988 100644
--- a/engines/gargoyle/screen.h
+++ b/engines/gargoyle/screen.h
@@ -28,9 +28,14 @@
 
 namespace Gargoyle {
 
-class Screen : public Graphics::Screen, Fonts {
+class Screen : public Graphics::Screen, public Fonts {
 public:
 	/**
+	 * Constructor
+	 */
+	Screen() : Graphics::Screen(), Fonts(this) {}
+
+	/**
 	 * Fills the screen with a given rgb color
 	 */
 	void fill(const byte *rgb);
@@ -40,14 +45,6 @@ public:
 	 */
 	void fillRect(uint x, uint y, uint w, uint h, const byte *rgb);
 
-	int drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw);
-
-	int drawStringUni(int x, int y, int fidx, const byte *rgb, const uint32 *s, int n, int spw);
-
-	int stringWidth(int fidx, const char *s, int n, int spw);
-
-	int stringWidthUni(int fidx, const uint32 *s, int n, int spw);
-
 	void drawCaret(const Common::Point &pos);
 };
 


Commit: 13e2838715247b6b6ca5b17d61f80fa516d23c34
    https://github.com/scummvm/scummvm/commit/13e2838715247b6b6ca5b17d61f80fa516d23c34
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Implement font drawing methods

Changed paths:
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp


diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 41b2f65..45f8671 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -103,24 +103,31 @@ Graphics::Font *Fonts::loadFont(FACES face, double size, double aspect, int styl
 	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
 }
 
-int Fonts::drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw) {
-	// TODO
-	return 0;
+int Fonts::drawString(const Common::Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
+	Graphics::Font *font = _fontTable[fontIdx];
+	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+	font->drawString(_surface, text, pos.x, pos.y, _surface->w - pos.x, color);
+	return font->getBoundingBox(text, pos.x, pos.y, _surface->w - pos.x).right;
 }
 
-int Fonts::drawStringUni(int x, int y, int fidx, const byte *rgb, const uint32 *s, int n, int spw) {
-	// TODO
-	return 0;
+int Fonts::drawStringUni(const Common::Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
+	Graphics::Font *font = _fontTable[fontIdx];
+	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+	font->drawString(_surface, text, pos.x, pos.y, _surface->w - pos.x, color);
+
+	return font->getBoundingBox(text, pos.x, pos.y, _surface->w - pos.x).right;
 }
 
-int Fonts::stringWidth(int fidx, const char *s, int n, int spw) {
-	// TODO
-	return 0;
+size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {
+	// TODO: Handle spw
+	Graphics::Font *font = _fontTable[fontIdx];
+	return font->getStringWidth(text);
 }
 
-int Fonts::stringWidthUni(int fidx, const uint32 *s, int n, int spw) {
-	// TODO
-	return 0;
+size_t Fonts::stringWidthUni(int fontIdx, const Common::U32String &text, int spw) {
+	// TODO: Handle spw
+	Graphics::Font *font = _fontTable[fontIdx];
+	return font->getStringWidth(text);
 }
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index 35db815..a486a01 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -25,6 +25,7 @@
 
 #include "gargoyle/glk_types.h"
 #include "common/str.h"
+#include "common/ustr.h"
 #include "graphics/font.h"
 
 namespace Gargoyle {
@@ -57,13 +58,13 @@ public:
 	 */
 	virtual ~Fonts();
 
-	int drawString(int x, int y, int fidx, const byte *rgb, const char *s, int n, int spw);
+	int drawString(const Common::Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
 
-	int drawStringUni(int x, int y, int fidx, const byte *rgb, const uint32 *s, int n, int spw);
+	int drawStringUni(const Common::Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
 
-	int stringWidth(int fidx, const char *s, int n, int spw);
+	size_t stringWidth(int fontIdx, const Common::String &text, int spw = -1);
 
-	int stringWidthUni(int fidx, const uint32 *s, int n, int spw);
+	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = -1);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 818bcf0..ebce9c3 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -966,7 +966,7 @@ void TextBufferWindow::redraw() {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = ln->_attrs[a].attrBg(_styles);
-                w = screen.stringWidthUni(font, ln->_chars + a, b - a, spw);
+                w = screen.stringWidthUni(font, Common::U32String(ln->_chars + a, b - a), spw);
                 screen.fillRect(x/GLI_SUBPIX, y,
                         w/GLI_SUBPIX, g_conf->_leading,
                         color);
@@ -985,7 +985,7 @@ void TextBufferWindow::redraw() {
         link = ln->_attrs[a].hyper;
         font = ln->_attrs[a].attrFont(_styles);
         color = ln->_attrs[a].attrBg(_styles);
-        w = screen.stringWidthUni(font, ln->_chars + a, b - a, spw);
+        w = screen.stringWidthUni(font, Common::U32String(ln->_chars + a, b - a), spw);
         screen.fillRect(x/GLI_SUBPIX, y, w/GLI_SUBPIX,
                 g_conf->_leading, color);
         if (link) {
@@ -1025,16 +1025,16 @@ void TextBufferWindow::redraw() {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-                x = screen.drawStringUni(x, y + g_conf->_baseLine,
-                        font, color, ln->_chars + a, b - a, spw);
+                x = screen.drawStringUni(Common::Point(x, y + g_conf->_baseLine),
+                        font, color, Common::U32String(ln->_chars + a, b - a), spw);
                 a = b;
             }
         }
         link = ln->_attrs[a].hyper;
         font = ln->_attrs[a].attrFont(_styles);
         color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-        screen.drawStringUni(x, y + g_conf->_baseLine,
-                font, color, ln->_chars + a, linelen - a, spw);
+        screen.drawStringUni(Common::Point(x, y + g_conf->_baseLine),
+                font, color, Common::U32String(ln->_chars + a, linelen - a), spw);
     }
 
     /*
@@ -1053,8 +1053,7 @@ void TextBufferWindow::redraw() {
                 x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
                 color);
 
-        w = screen.stringWidth(g_conf->_moreFont,
-                g_conf->_morePrompt.c_str(), g_conf->_morePrompt.size(), -1);
+        w = screen.stringWidth(g_conf->_moreFont, g_conf->_morePrompt);
 
         if (g_conf->_moreAlign == 1)    /* center */
             x = x0 + SLOP + (x1 - x0 - w - SLOP * 2) / 2;
@@ -1062,9 +1061,8 @@ void TextBufferWindow::redraw() {
             x = x1 - SLOP - w;
 
         color = Windows::_overrideFgSet ? g_conf->_moreColor : _fgColor;
-		screen.drawString(x, y + g_conf->_baseLine,
-                g_conf->_moreFont, color,
-                g_conf->_morePrompt.c_str(), g_conf->_morePrompt.size(), -1);
+		screen.drawString(Common::Point(x, y + g_conf->_baseLine),
+                g_conf->_moreFont, color, g_conf->_morePrompt);
         y1 = y; /* don't want pictures overdrawing "[more]" */
 
         /* try to claim the focus */
@@ -1617,13 +1615,12 @@ int TextBufferWindow::calcWidth(glui32 *chars, Attributes *attrs, int startchar,
 	for (b = startchar; b < numChars; b++) {
 		if (attrs[a] == attrs[b]) {
 			w += screen.stringWidthUni(attrs[a].attrFont(_styles),
-				chars + a, b - a, spw);
+				Common::U32String(chars + a, b - a), spw);
 			a = b;
 		}
 	}
 
-	w += screen.stringWidthUni(attrs[a].attrFont(_styles),
-		chars + a, b - a, spw);
+	w += screen.stringWidthUni(attrs[a].attrFont(_styles), Common::U32String(chars + a, b - a), spw);
 
 	return w;
 }
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index e2ee06e..7b349a5 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -607,9 +607,8 @@ void TextGridWindow::redraw() {
 					o = x;
 
 					for (k = a; k < b; k++) {
-						screen.drawStringUni(o * GLI_SUBPIX,
-							y + g_conf->_baseLine, font, fgcolor,
-							&ln->_chars[k], 1, -1);
+						screen.drawStringUni(Common::Point(o * GLI_SUBPIX, y + g_conf->_baseLine),
+							font, fgcolor, Common::U32String(&ln->_chars[k], 1), -1);
 						o += g_conf->_cellW;
 					}
 					if (link) {
@@ -631,9 +630,8 @@ void TextGridWindow::redraw() {
 
 			o = x;
 			for (k = a; k < b; k++) {
-				screen.drawStringUni(o * GLI_SUBPIX,
-					y + g_conf->_baseLine, font, fgcolor,
-					&ln->_chars[k], 1, -1);
+				screen.drawStringUni(Common::Point(o * GLI_SUBPIX, y + g_conf->_baseLine),
+					font, fgcolor, Common::U32String(&ln->_chars[k], 1));
 				o += g_conf->_cellW;
 			}
 			if (link) {


Commit: ffe0f5220bef728938847f54bdc4e489739d6ded
    https://github.com/scummvm/scummvm/commit/ffe0f5220bef728938847f54bdc4e489739d6ded
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Converted fillRect to use a Rect to specify bounds

Changed paths:
  A engines/gargoyle/utils.cpp
  A engines/gargoyle/utils.h
    engines/gargoyle/conf.cpp
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/module.mk
    engines/gargoyle/screen.cpp
    engines/gargoyle/screen.h
    engines/gargoyle/unicode.cpp
    engines/gargoyle/unicode.h
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_graphics.h
    engines/gargoyle/window_mask.cpp
    engines/gargoyle/window_mask.h
    engines/gargoyle/window_pair.cpp
    engines/gargoyle/window_pair.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index e2b8d04..a684889 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -22,7 +22,7 @@
 
 #include "gargoyle/conf.h"
 #include "gargoyle/fonts.h"
-#include "gargoyle/unicode.h"
+#include "gargoyle/utils.h"
 #include "gargoyle/windows.h"
 #include "common/config-manager.h"
 #include "common/system.h"
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index bba9df1..6836faf 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -169,7 +169,7 @@ void Events::handleScroll(bool wheelUp) {
 	g_vm->_windows->inputHandleKey(wheelUp ? keycode_MouseWheelUp : keycode_MouseWheelDown);
 }
 
-void Events::handleMouseMove(const Common::Point &pos) {
+void Events::handleMouseMove(const Point &pos) {
 	// hyperlinks and selection
 	// TODO: Properly handle commented out lines
 	if (g_vm->_copySelect) {
@@ -184,14 +184,14 @@ void Events::handleMouseMove(const Common::Point &pos) {
 	}
 }
 
-void Events::handleButtonDown(bool isLeft, const Common::Point &pos) {
+void Events::handleButtonDown(bool isLeft, const Point &pos) {
 	if (isLeft)
 		g_vm->_windows->inputHandleClick(pos);
 	else
 		g_vm->_clipboard->receive(PRIMARY);
 }
 
-void Events::handleButtonUp(bool isLeft, const Common::Point &pos) {
+void Events::handleButtonUp(bool isLeft, const Point &pos) {
 	if (isLeft) {
 		g_vm->_copySelect = false;
 		//gdk_window_set_cursor((GTK_WIDGET(widget)->window), NULL);
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 544b524..f49ef35 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -24,6 +24,7 @@
 #define GARGOYLE_EVENTS_H
 
 #include "common/events.h"
+#include "gargoyle/utils.h"
 
 namespace Gargoyle {
 
@@ -171,17 +172,17 @@ private:
 	/**
 	 * Handle mouse move events
 	 */
-	void handleMouseMove(const Common::Point &pos);
+	void handleMouseMove(const Point &pos);
 
 	/**
 	 * Handle mouse down events
 	 */
-	void handleButtonDown(bool isLeft, const Common::Point &pos);
+	void handleButtonDown(bool isLeft, const Point &pos);
 
 	/**
 	 * Handle mouse up events
 	 */
-	void handleButtonUp(bool isLeft, const Common::Point &pos);
+	void handleButtonUp(bool isLeft, const Point &pos);
 public:
 	bool _forceClick;
 public:
diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 45f8671..bcb129c 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -103,14 +103,14 @@ Graphics::Font *Fonts::loadFont(FACES face, double size, double aspect, int styl
 	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
 }
 
-int Fonts::drawString(const Common::Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
+int Fonts::drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
 	Graphics::Font *font = _fontTable[fontIdx];
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	font->drawString(_surface, text, pos.x, pos.y, _surface->w - pos.x, color);
 	return font->getBoundingBox(text, pos.x, pos.y, _surface->w - pos.x).right;
 }
 
-int Fonts::drawStringUni(const Common::Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
+int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
 	Graphics::Font *font = _fontTable[fontIdx];
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	font->drawString(_surface, text, pos.x, pos.y, _surface->w - pos.x, color);
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index a486a01..4472025 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -24,6 +24,7 @@
 #define GARGOYLE_FONTS_H
 
 #include "gargoyle/glk_types.h"
+#include "gargoyle/utils.h"
 #include "common/str.h"
 #include "common/ustr.h"
 #include "graphics/font.h"
@@ -58,9 +59,9 @@ public:
 	 */
 	virtual ~Fonts();
 
-	int drawString(const Common::Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
+	int drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
 
-	int drawStringUni(const Common::Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
+	int drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
 
 	size_t stringWidth(int fontIdx, const Common::String &text, int spw = -1);
 
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 736ed3e..abc4d2c 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -295,7 +295,7 @@ void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
 	if (!win) {
 		warning("window_move_cursor: invalid ref");
 	} else {
-		win->moveCursor(Common::Point(xpos, ypos));
+		win->moveCursor(Point(xpos, ypos));
 	}
 }
 
@@ -770,7 +770,7 @@ void Glk::glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 wid
 	if (!win) {
 		warning("window_erase_rect: invalid ref");
 	} else {
-		win->eraseRect(false, Common::Rect(left, top, left + width, top + height));
+		win->eraseRect(false, Rect(left, top, left + width, top + height));
 	}
 }
 
@@ -779,7 +779,7 @@ void Glk::glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 to
 	if (!win) {
 		warning("window_fill_rect: invalid ref");
 	} else {
-		win->eraseRect(color, Common::Rect(left, top, left + width, top + height));
+		win->eraseRect(color, Rect(left, top, left + width, top + height));
 	}
 }
 
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index a45608f..f4422b0 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -14,6 +14,7 @@ MODULE_OBJS := \
 	time.o \
 	unicode.o \
 	unicode_gen.o \
+	utils.o \
 	windows.o \
 	window_mask.o \
 	window_graphics.o \
diff --git a/engines/gargoyle/screen.cpp b/engines/gargoyle/screen.cpp
index f541b76..f500b86 100644
--- a/engines/gargoyle/screen.cpp
+++ b/engines/gargoyle/screen.cpp
@@ -29,12 +29,12 @@ void Screen::fill(const byte *rgb) {
 	clear(color);
 }
 
-void Screen::fillRect(uint x, uint y, uint w, uint h, const byte *rgb) {
+void Screen::fillRect(const Rect &box, const byte *rgb) {
 	uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
-	Graphics::Screen::fillRect(Common::Rect(x, y, x + w, y + h), color);
+	Graphics::Screen::fillRect(box, color);
 }
 
-void Screen::drawCaret(const Common::Point &pos) {
+void Screen::drawCaret(const Point &pos) {
 	// TODO
 }
 
diff --git a/engines/gargoyle/screen.h b/engines/gargoyle/screen.h
index 8cdf988..3974096 100644
--- a/engines/gargoyle/screen.h
+++ b/engines/gargoyle/screen.h
@@ -43,9 +43,9 @@ public:
 	/**
 	 * Fill a given area of the screen with an rgb color
 	 */
-	void fillRect(uint x, uint y, uint w, uint h, const byte *rgb);
+	void fillRect(const Rect &box, const byte *rgb);
 
-	void drawCaret(const Common::Point &pos);
+	void drawCaret(const Point &pos);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/unicode.cpp b/engines/gargoyle/unicode.cpp
index 601d8f0..c547d2b 100644
--- a/engines/gargoyle/unicode.cpp
+++ b/engines/gargoyle/unicode.cpp
@@ -33,22 +33,6 @@ size_t strlen_uni(const uint32 *s) {
 	return len;
 }
 
-int strToInt(const char *s) {
-	if (!*s)
-		// No string at all
-		return 0;
-	else if (toupper(s[strlen(s) - 1]) != 'H')
-		// Standard decimal string
-		return atoi(s);
-
-	// Hexadecimal string
-	uint tmp = 0;
-	int read = sscanf(s, "%xh", &tmp);
-	if (read < 1)
-		error("strToInt failed on string \"%s\"", s);
-	return (int)tmp;
-}
-
 glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCase destcase,
 		BufferChangeCond cond, int changerest) {
 	glui32 ix, jx;
diff --git a/engines/gargoyle/unicode.h b/engines/gargoyle/unicode.h
index 806a63f..2392c1a 100644
--- a/engines/gargoyle/unicode.h
+++ b/engines/gargoyle/unicode.h
@@ -46,11 +46,6 @@ size_t strlen_uni(const uint32 *s);
 extern glui32 bufferChangeCase(glui32 *buf, glui32 len,
 	glui32 numchars, BufferChangeCase destcase, BufferChangeCond cond, int changerest);
 
-/**
- * Converts a decimal or hexadecimal string into a number
- */
-int strToInt(const char *s);
-
 } // End of namespace Gargoyle
 
 #endif
diff --git a/engines/gargoyle/utils.cpp b/engines/gargoyle/utils.cpp
new file mode 100644
index 0000000..6857301
--- /dev/null
+++ b/engines/gargoyle/utils.cpp
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/utils.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+
+int strToInt(const char *s) {
+	if (!*s)
+		// No string at all
+		return 0;
+	else if (toupper(s[strlen(s) - 1]) != 'H')
+		// Standard decimal string
+		return atoi(s);
+
+	// Hexadecimal string
+	uint tmp = 0;
+	int read = sscanf(s, "%xh", &tmp);
+	if (read < 1)
+		error("strToInt failed on string \"%s\"", s);
+	return (int)tmp;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/utils.h b/engines/gargoyle/utils.h
new file mode 100644
index 0000000..b8a8e02
--- /dev/null
+++ b/engines/gargoyle/utils.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_UTILS_H
+#define GARGOYLE_UTILS_H
+
+#include "common/rect.h"
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+
+typedef Common::Point Point;
+
+struct Rect : public Common::Rect {
+public:
+	static Rect fromXYWH(int x, int y, int w, int h) {
+		return Rect(x, y, x + w, y + h);
+	}
+
+	Rect() : Common::Rect() {}
+	Rect(int16 w, int16 h) : Common::Rect(w, h) {}
+	Rect(int16 x1, int16 y1, int16 x2, int16 y2) : Common::Rect(x1, y1, x2, y2) {}
+};
+
+/**
+ * Converts a decimal or hexadecimal string into a number
+ */
+int strToInt(const char *s);
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index 0ea5876..6609f0a 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -36,7 +36,7 @@ GraphicsWindow::~GraphicsWindow() {
 	delete _surface;
 }
 
-void GraphicsWindow::rearrange(const Common::Rect &box) {
+void GraphicsWindow::rearrange(const Rect &box) {
 	int newwid, newhgt;
 	int bothwid, bothhgt;
 	int oldw, oldh;
@@ -92,7 +92,7 @@ void GraphicsWindow::redraw() {
 		_dirty = 0;
 
 		if (_surface)
-			screen.blitFrom(*_surface, Common::Point(_bbox.left, _bbox.top));
+			screen.blitFrom(*_surface, Point(_bbox.left, _bbox.top));
 	}
 }
 
@@ -120,7 +120,7 @@ glui32 GraphicsWindow::drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int s
 	return true;
 }
 
-void GraphicsWindow::eraseRect(bool whole, const Common::Rect &box) {
+void GraphicsWindow::eraseRect(bool whole, const Rect &box) {
 	int x0 = box.left, y0 = box.top, x1 = box.right, y1 = box.bottom;
 	int hx0, hx1, hy0, hy1;
 
@@ -148,11 +148,11 @@ void GraphicsWindow::eraseRect(bool whole, const Common::Rect &box) {
 	/* zero out hyperlinks for these coordinates */
 	g_vm->_windowMask->putHyperlink(0, hx0, hy0, hx1, hy1);
 
-	_surface->fillRect(Common::Rect(x0, y0, x1, y1), MKTAG(_bgnd[0], _bgnd[1], _bgnd[2], 0));
+	_surface->fillRect(Rect(x0, y0, x1, y1), MKTAG(_bgnd[0], _bgnd[1], _bgnd[2], 0));
 	touch();
 }
 
-void GraphicsWindow::fillRect(glui32 color, const Common::Rect &box) {
+void GraphicsWindow::fillRect(glui32 color, const Rect &box) {
 	unsigned char col[3];
 	int x0 = box.left, y0 = box.top, x1 = box.right, y1 = box.bottom;
 	int hx0, hx1, hy0, hy1;
@@ -178,7 +178,7 @@ void GraphicsWindow::fillRect(glui32 color, const Common::Rect &box) {
 	/* zero out hyperlinks for these coordinates */
 	g_vm->_windowMask->putHyperlink(0, hx0, hy0, hx1, hy1);
 
-	_surface->fillRect(Common::Rect(x0, y0, x1, y1), MKTAG(col[0], col[1], col[2], 0));
+	_surface->fillRect(Rect(x0, y0, x1, y1), MKTAG(col[0], col[1], col[2], 0));
 	touch();
 }
 
@@ -233,7 +233,7 @@ void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int h
 	w = sx1 - sx0;
 	h = sy1 - sy0;
 
-	_surface->blitFrom(*g_vm->_screen, Common::Rect(sx0, sy0, sx0 + w, sy0 + h), Common::Point(0, 0));
+	_surface->blitFrom(*g_vm->_screen, Rect(sx0, sy0, sx0 + w, sy0 + h), Point(0, 0));
 }
 
 void GraphicsWindow::getSize(glui32 *width, glui32 *height) const {
diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h
index 8c2724e..f271444 100644
--- a/engines/gargoyle/window_graphics.h
+++ b/engines/gargoyle/window_graphics.h
@@ -58,7 +58,7 @@ public:
 	/**
 	 * Rearranges the window
 	 */
-	virtual void rearrange(const Common::Rect &box) override;
+	virtual void rearrange(const Rect &box) override;
 
 	/**
 	 * Get window split size within parent pair window
@@ -86,9 +86,9 @@ public:
 	 */
 	virtual void redraw() override;
 
-	virtual void eraseRect(bool whole, const Common::Rect &box) override;
+	virtual void eraseRect(bool whole, const Rect &box) override;
 
-	virtual void fillRect(glui32 color, const Common::Rect &box) override;
+	virtual void fillRect(glui32 color, const Rect &box) override;
 
 	virtual void getSize(glui32 *width, glui32 *height) const override;
 
diff --git a/engines/gargoyle/window_mask.cpp b/engines/gargoyle/window_mask.cpp
index fe1d463..b3b629b 100644
--- a/engines/gargoyle/window_mask.cpp
+++ b/engines/gargoyle/window_mask.cpp
@@ -94,7 +94,7 @@ void WindowMask::putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1
 	}
 }
 
-glui32 WindowMask::getHyperlink(const Common::Point &pos) {
+glui32 WindowMask::getHyperlink(const Point &pos) {
 	if (!_hor || !_ver) {
 		warning("getHyperlink: struct not initialized");
 		return 0;
@@ -110,7 +110,7 @@ glui32 WindowMask::getHyperlink(const Common::Point &pos) {
 	return _links[pos.x][pos.y];
 }
 
-void WindowMask::startSelection(const Common::Point &pos) {
+void WindowMask::startSelection(const Point &pos) {
 	int tx, ty;
 
 	if (!_hor || !_ver) {
@@ -129,7 +129,7 @@ void WindowMask::startSelection(const Common::Point &pos) {
 	g_vm->_windows->selectionChanged();
 }
 
-void WindowMask::moveSelection(const Common::Point &pos) {
+void WindowMask::moveSelection(const Point &pos) {
 	int tx, ty;
 
 	if (ABS(pos.x - _lastX) < 5 && abs(pos.y - _lastY) < 5)
diff --git a/engines/gargoyle/window_mask.h b/engines/gargoyle/window_mask.h
index 9dc5826..e9030a2 100644
--- a/engines/gargoyle/window_mask.h
+++ b/engines/gargoyle/window_mask.h
@@ -25,6 +25,7 @@
 
 #include "common/rect.h"
 #include "gargoyle/glk_types.h"
+#include "gargoyle/utils.h"
 
 namespace Gargoyle {
 
@@ -34,7 +35,7 @@ class WindowMask {
 public:
 	size_t _hor, _ver;
 	glui32 **_links;
-	Common::Rect _select;
+	Rect _select;
 
 	static int _lastX, _lastY;
 public:
@@ -50,11 +51,11 @@ public:
 
 	void putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1);
 
-	glui32 getHyperlink(const Common::Point &pos);
+	glui32 getHyperlink(const Point &pos);
 
-	void startSelection(const Common::Point &pos);
+	void startSelection(const Point &pos);
 
-	void moveSelection(const Common::Point &pos);
+	void moveSelection(const Point &pos);
 
 	void clearSelection();
 
diff --git a/engines/gargoyle/window_pair.cpp b/engines/gargoyle/window_pair.cpp
index b8d593e..3f0f294 100644
--- a/engines/gargoyle/window_pair.cpp
+++ b/engines/gargoyle/window_pair.cpp
@@ -36,8 +36,8 @@ PairWindow::PairWindow(Windows *windows, glui32 method, Window *key, glui32 size
 	_type = wintype_Pair;
 }
 
-void PairWindow::rearrange(const Common::Rect &box) {
-	Common::Rect box1, box2;
+void PairWindow::rearrange(const Rect &box) {
+	Rect box1, box2;
 	int min, diff, split, splitwid, max;
 	Window *ch1, *ch2;
 
diff --git a/engines/gargoyle/window_pair.h b/engines/gargoyle/window_pair.h
index 8e80f79..2037b3d 100644
--- a/engines/gargoyle/window_pair.h
+++ b/engines/gargoyle/window_pair.h
@@ -51,7 +51,7 @@ public:
 	/**
 	 * Rearranges the window
 	 */
-	virtual void rearrange(const Common::Rect &box) override;
+	virtual void rearrange(const Rect &box) override;
 
 	/**
 	 * Redraw the window
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index ebce9c3..3946771 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -70,7 +70,7 @@ TextBufferWindow::~TextBufferWindow() {
 	}
 }
 
-void TextBufferWindow::rearrange(const Common::Rect &box) {
+void TextBufferWindow::rearrange(const Rect &box) {
 	Window::rearrange(box);
 	int newwid, newhgt;
 	int rnd;
@@ -371,7 +371,7 @@ void TextBufferWindow::touch(int line) {
 	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
 	_lines[line]._dirty = 1;
 	g_vm->_windowMask->clearSelection();
-	_windows->repaint(Common::Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
+	_windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
 }
 
 glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
@@ -567,7 +567,7 @@ void TextBufferWindow::clear() {
 		touch(i);
 }
 
-void TextBufferWindow::click(const Common::Point &newPos) {
+void TextBufferWindow::click(const Point &newPos) {
 	int gh = false;
 	int gs = false;
 
@@ -843,7 +843,7 @@ void TextBufferWindow::redraw() {
 
         /* repaint previously selected lines if needed */
         if (ln->_repaint && !Windows::_forceRedraw)
-            _windows->redrawRect(Common::Rect(x0 / GLI_SUBPIX, y,
+            _windows->redrawRect(Rect(x0 / GLI_SUBPIX, y,
 				x1/GLI_SUBPIX, y + g_conf->_leading));
 
         /* keep selected line dirty and flag for repaint */
@@ -954,8 +954,7 @@ void TextBufferWindow::redraw() {
          * fill in background colors
          */
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        screen.fillRect(x0/GLI_SUBPIX, y,
-                (x1-x0) / GLI_SUBPIX, g_conf->_leading,
+        screen.fillRect(Rect::fromXYWH(x0 / GLI_SUBPIX, y, (x1-x0) / GLI_SUBPIX, g_conf->_leading),
                 color);
 
         x = x0 + SLOP + ln->_lm;
@@ -967,13 +966,11 @@ void TextBufferWindow::redraw() {
                 font = ln->_attrs[a].attrFont(_styles);
                 color = ln->_attrs[a].attrBg(_styles);
                 w = screen.stringWidthUni(font, Common::U32String(ln->_chars + a, b - a), spw);
-                screen.fillRect(x/GLI_SUBPIX, y,
-                        w/GLI_SUBPIX, g_conf->_leading,
+                screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, w / GLI_SUBPIX, g_conf->_leading),
                         color);
                 if (link) {
-                    screen.fillRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
-                            w/GLI_SUBPIX + 1, g_conf->_linkStyle,
-                            g_conf->_linkColor);
+                    screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+						w / GLI_SUBPIX + 1, g_conf->_linkStyle), g_conf->_linkColor);
                     g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
                             x/GLI_SUBPIX + w/GLI_SUBPIX,
                             y + g_conf->_leading);
@@ -986,22 +983,18 @@ void TextBufferWindow::redraw() {
         font = ln->_attrs[a].attrFont(_styles);
         color = ln->_attrs[a].attrBg(_styles);
         w = screen.stringWidthUni(font, Common::U32String(ln->_chars + a, b - a), spw);
-        screen.fillRect(x/GLI_SUBPIX, y, w/GLI_SUBPIX,
-                g_conf->_leading, color);
+        screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, w / GLI_SUBPIX, g_conf->_leading), color);
         if (link) {
-            screen.fillRect(x/GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
-                    w/GLI_SUBPIX + 1, g_conf->_linkStyle,
-                    g_conf->_linkColor);
-            g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
-                    x/GLI_SUBPIX + w/GLI_SUBPIX,
+            screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
+                    w/GLI_SUBPIX + 1, g_conf->_linkStyle), g_conf->_linkColor);
+            g_vm->_windowMask->putHyperlink(link, x / GLI_SUBPIX, y,
+                    x / GLI_SUBPIX + w / GLI_SUBPIX,
                     y + g_conf->_leading);
         }
         x += w;
 
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        screen.fillRect(x/GLI_SUBPIX, y,
-                x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
-                color);
+        screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading), color);
 
         /*
          * draw caret
@@ -1010,7 +1003,7 @@ void TextBufferWindow::redraw() {
         if (_windows->getFocusWindow() == this && i == 0 && (_lineRequest || _lineRequestUni)) {
             w = calcWidth(_chars, _attrs, 0, _inCurs, spw);
             if (w < pw - g_conf->_caretShape * 2 * GLI_SUBPIX)
-                screen.drawCaret(Common::Point(x0 + SLOP + ln->_lm + w, y + g_conf->_baseLine));
+                screen.drawCaret(Point(x0 + SLOP + ln->_lm + w, y + g_conf->_baseLine));
         }
 
         /*
@@ -1025,7 +1018,7 @@ void TextBufferWindow::redraw() {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-                x = screen.drawStringUni(Common::Point(x, y + g_conf->_baseLine),
+                x = screen.drawStringUni(Point(x, y + g_conf->_baseLine),
                         font, color, Common::U32String(ln->_chars + a, b - a), spw);
                 a = b;
             }
@@ -1033,7 +1026,7 @@ void TextBufferWindow::redraw() {
         link = ln->_attrs[a].hyper;
         font = ln->_attrs[a].attrFont(_styles);
         color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-        screen.drawStringUni(Common::Point(x, y + g_conf->_baseLine),
+        screen.drawStringUni(Point(x, y + g_conf->_baseLine),
                 font, color, Common::U32String(ln->_chars + a, linelen - a), spw);
     }
 
@@ -1049,9 +1042,7 @@ void TextBufferWindow::redraw() {
                 x1/GLI_SUBPIX, y + g_conf->_leading);
 
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
-        screen.fillRect(x/GLI_SUBPIX, y,
-                x1/GLI_SUBPIX - x/GLI_SUBPIX, g_conf->_leading,
-                color);
+        screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX, y, x1 / GLI_SUBPIX - x / GLI_SUBPIX, g_conf->_leading), color);
 
         w = screen.stringWidth(g_conf->_moreFont, g_conf->_morePrompt);
 
@@ -1061,7 +1052,7 @@ void TextBufferWindow::redraw() {
             x = x1 - SLOP - w;
 
         color = Windows::_overrideFgSet ? g_conf->_moreColor : _fgColor;
-		screen.drawString(Common::Point(x, y + g_conf->_baseLine),
+		screen.drawString(Point(x, y + g_conf->_baseLine),
                 g_conf->_moreFont, color, g_conf->_morePrompt);
         y1 = y; /* don't want pictures overdrawing "[more]" */
 
@@ -1141,16 +1132,14 @@ void TextBufferWindow::redraw() {
             t0 = t1 = y0;
         }
 
-        screen.fillRect(x0+1, y0, x1-x0-2, y1-y0, g_conf->_scrollBg);
-        screen.fillRect(x0+1, t0, x1-x0-2, t1-t0, g_conf->_scrollFg);
+        screen.fillRect(Rect::fromXYWH(x0 + 1, y0, x1-x0 - 2, y1 - y0), g_conf->_scrollBg);
+        screen.fillRect(Rect::fromXYWH(x0 + 1, t0, x1-x0 - 2, t1 - t0), g_conf->_scrollFg);
 
         for (i = 0; i < g_conf->_scrollWidth / 2 + 1; i++) {
-            screen.fillRect(x0+g_conf->_scrollWidth/2-i,
-                    y0 - g_conf->_scrollWidth/2 + i,
-                    i*2, 1, g_conf->_scrollFg);
-            screen.fillRect(x0+g_conf->_scrollWidth/2-i,
-                    y1 + g_conf->_scrollWidth/2 - i,
-                    i*2, 1, g_conf->_scrollFg);
+            screen.fillRect(Rect::fromXYWH(x0 + g_conf->_scrollWidth / 2 - i,
+				y0 - g_conf->_scrollWidth/2 + i, i * 2, 1), g_conf->_scrollFg);
+            screen.fillRect(Rect::fromXYWH(x0 + g_conf->_scrollWidth / 2 - i,
+                    y1 + g_conf->_scrollWidth / 2 - i, i * 2, 1), g_conf->_scrollFg);
         }
     }
 
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index 61b30e2..ab7890a 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -147,7 +147,7 @@ public:
 	/**
 	 * Rearranges the window
 	 */
-	virtual void rearrange(const Common::Rect &box) override;
+	virtual void rearrange(const Rect &box) override;
 
 	/**
 	 * Get window split size within parent pair window
@@ -172,7 +172,7 @@ public:
 	/**
 	 * Click the window
 	 */
-	virtual void click(const Common::Point &newPos) override;
+	virtual void click(const Point &newPos) override;
 
 	/**
 	 * Prepare for inputing a line
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 7b349a5..b1effc4 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -52,7 +52,7 @@ TextGridWindow::~TextGridWindow() {
 	delete[] _lineTerminators;
 }
 
-void TextGridWindow::rearrange(const Common::Rect &box) {
+void TextGridWindow::rearrange(const Rect &box) {
 	Window::rearrange(box);
 	int newwid, newhgt;
 
@@ -76,7 +76,7 @@ void TextGridWindow::rearrange(const Common::Rect &box) {
 void TextGridWindow::touch(int line) {
 	int y = _bbox.top + line * g_conf->_leading;
 	_lines[line].dirty = true;
-	_windows->repaint(Common::Rect(_bbox.left, y, _bbox.right, y + g_conf->_leading));
+	_windows->repaint(Rect(_bbox.left, y, _bbox.right, y + g_conf->_leading));
 }
 
 glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
@@ -161,7 +161,7 @@ bool TextGridWindow::unputCharUni(uint32 ch) {
 	}
 }
 
-void TextGridWindow::moveCursor(const Common::Point &pos) {
+void TextGridWindow::moveCursor(const Point &pos) {
 	// If the values are negative, they're really huge positive numbers --
 	// remember that they were cast from glui32. So set them huge and
 	// let canonicalization take its course.
@@ -189,7 +189,7 @@ void TextGridWindow::clear() {
 	_curY = 0;
 }
 
-void TextGridWindow::click(const Common::Point &newPos) {
+void TextGridWindow::click(const Point &newPos) {
 	int x = newPos.x - _bbox.left;
 	int y = newPos.y - _bbox.top;
 
@@ -603,17 +603,17 @@ void TextGridWindow::redraw() {
 					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
 					bgcolor = ln->_attrs[a].attrBg(styles);
 					w = (b - a) * g_conf->_cellW;
-					screen.fillRect(x, y, w, g_conf->_leading, bgcolor);
+					screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
 					o = x;
 
 					for (k = a; k < b; k++) {
-						screen.drawStringUni(Common::Point(o * GLI_SUBPIX, y + g_conf->_baseLine),
+						screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine),
 							font, fgcolor, Common::U32String(&ln->_chars[k], 1), -1);
 						o += g_conf->_cellW;
 					}
 					if (link) {
-						screen.fillRect(x, y + g_conf->_baseLine + 1, w,
-							g_conf->_linkStyle, g_conf->_linkColor);
+						screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w,
+							g_conf->_linkStyle), g_conf->_linkColor);
 						g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 					}
 					x += w;
@@ -626,17 +626,17 @@ void TextGridWindow::redraw() {
 			bgcolor = ln->_attrs[a].attrBg(styles);
 			w = (b - a) * g_conf->_cellW;
 			w += _bbox.right - (x + w);
-			screen.fillRect(x, y, w, g_conf->_leading, bgcolor);
+			screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
 
 			o = x;
 			for (k = a; k < b; k++) {
-				screen.drawStringUni(Common::Point(o * GLI_SUBPIX, y + g_conf->_baseLine),
+				screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine),
 					font, fgcolor, Common::U32String(&ln->_chars[k], 1));
 				o += g_conf->_cellW;
 			}
 			if (link) {
-				screen.fillRect(x, y + g_conf->_baseLine + 1, w,
-					g_conf->_linkStyle, g_conf->_linkColor);
+				screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w, g_conf->_linkStyle),
+					g_conf->_linkColor);
 				g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 			}
 		}
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 9ff588b..1fba707 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -90,7 +90,7 @@ public:
 	/**
 	 * Rearranges the window
 	 */
-	virtual void rearrange(const Common::Rect &box) override;
+	virtual void rearrange(const Rect &box) override;
 
 	/**
 	 * Get window split size within parent pair window
@@ -110,7 +110,7 @@ public:
 	/**
 	 * Move the cursor
 	 */
-	virtual void moveCursor(const Common::Point &newPos) override;
+	virtual void moveCursor(const Point &newPos) override;
 
 	/**
 	 * Clear the window
@@ -120,7 +120,7 @@ public:
 	/**
 	 * Click the window
 	 */
-	virtual void click(const Common::Point &newPos) override;
+	virtual void click(const Point &newPos) override;
 
 	/**
 	 * Cancel a hyperlink event
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 0f1c9eb..22c2701 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -249,7 +249,7 @@ PairWindow *Windows::newPairWindow(glui32 method, Window *key, glui32 size) {
 
 void Windows::rearrange() {
 	if (_rootWin) {
-		Common::Rect box;
+		Rect box;
 
 		if (g_conf->_lockCols) {
 			int desired_width = g_conf->_wMarginSaveX * 2 + g_conf->_cellW * g_conf->_cols;
@@ -384,7 +384,7 @@ void Windows::inputHandleKey(glui32 key) {
 		g_vm->quitGame();
 }
 
-void Windows::inputHandleClick(const Common::Point &pos) {
+void Windows::inputHandleClick(const Point &pos) {
 	if (_rootWin)
 		_rootWin->click(pos);
 }
@@ -399,7 +399,7 @@ void Windows::redraw() {
 	_claimSelect = false;
 
 	if (_forceRedraw) {
-		repaint(Common::Rect(0, 0, g_conf->_imageW, g_conf->_imageH));
+		repaint(Rect(0, 0, g_conf->_imageW, g_conf->_imageH));
 		g_vm->_screen->fill(g_conf->_windowColor);
 	}
 
@@ -412,12 +412,12 @@ void Windows::redraw() {
 	_forceRedraw = 0;
 }
 
-void Windows::redrawRect(const Common::Rect &r) {
+void Windows::redrawRect(const Rect &r) {
 	_drawSelect = true;
 	repaint(r);
 }
 
-void Windows::repaint(const Common::Rect &box) {
+void Windows::repaint(const Rect &box) {
 	// No implementation
 }
 
@@ -556,7 +556,7 @@ void Window::cancelLineEvent(Event *ev) {
 	ev->clear();
 }
 
-void Window::moveCursor(const Common::Point &newPos) {
+void Window::moveCursor(const Point &newPos) {
 	warning("moveCursor: not a TextGrid window");
 }
 
@@ -572,7 +572,7 @@ void Window::redraw() {
 	if (Windows::_forceRedraw) {
 		unsigned char *color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
 		int y0 = _yAdj ? _bbox.top - _yAdj : _bbox.top;
-		g_vm->_screen->fillRect(_bbox.left, y0, _bbox.width(), _bbox.bottom - y0, color);
+		g_vm->_screen->fillRect(Rect(_bbox.left, y0, _bbox.right, _bbox.bottom), color);
 	}
 }
 
@@ -604,11 +604,11 @@ void Window::flowBreak() {
 	warning("flowBreak: not a text buffer window");
 }
 
-void Window::eraseRect(bool whole, const Common::Rect &box) {
+void Window::eraseRect(bool whole, const Rect &box) {
 	warning("eraseRect: not a graphics window");
 }
 
-void Window::fillRect(glui32 color, const Common::Rect &box) {
+void Window::fillRect(glui32 color, const Rect &box) {
 	warning("fillRect: not a graphics window");
 }
 
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 643fecf..231ed67 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -179,7 +179,7 @@ public:
 	/**
 	 * Handle mouse clicks
 	 */
-	void inputHandleClick(const Common::Point &pos);
+	void inputHandleClick(const Point &pos);
 
 	void selectionChanged();
 
@@ -192,12 +192,12 @@ public:
 
 	void redraw();
 
-	void redrawRect(const Common::Rect &r);
+	void redrawRect(const Rect &r);
 
 	/**
 	 * Repaint an area of the windows
 	 */
-	void repaint(const Common::Rect &box);
+	void repaint(const Rect &box);
 
 	/**
 	 * Get an iterator that will move over the tree
@@ -282,7 +282,7 @@ public:
 
 	Window *_parent;       ///< pair window which contains this one
 	Window *_next, *_prev; ///< in the big linked list of windows
-	Common::Rect _bbox;
+	Rect _bbox;
 	int _yAdj;
 
 	Stream *_stream;       ///< the window stream.
@@ -328,7 +328,7 @@ public:
 	/**
 	 * Rearranges the window
 	 */
-	virtual void rearrange(const Common::Rect &box) { _bbox = box; }
+	virtual void rearrange(const Rect &box) { _bbox = box; }
 
 	/**
 	 * Get window split size within parent pair window
@@ -348,7 +348,7 @@ public:
 	/**
 	 * Move the cursor
 	 */
-	virtual void moveCursor(const Common::Point &newPos);
+	virtual void moveCursor(const Point &newPos);
 
 	/**
 	 * Clear the window
@@ -358,7 +358,7 @@ public:
 	/**
 	 * Click the window
 	 */
-	virtual void click(const Common::Point &newPos) {}
+	virtual void click(const Point &newPos) {}
 
 	/**
 	 * Prepare for inputing a line
@@ -423,9 +423,9 @@ public:
 
 	virtual void flowBreak();
 
-	virtual void eraseRect(bool whole, const Common::Rect &box);
+	virtual void eraseRect(bool whole, const Rect &box);
 
-	virtual void fillRect(glui32 color, const Common::Rect &box);
+	virtual void fillRect(glui32 color, const Rect &box);
 
 	virtual void setBackgroundColor(glui32 color);
 };


Commit: a9f5b3351e1daedbd2fc301254048060bc464b58
    https://github.com/scummvm/scummvm/commit/a9f5b3351e1daedbd2fc301254048060bc464b58
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix fg/bg window color initialization

Changed paths:
    engines/gargoyle/windows.cpp


diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 22c2701..bf126eb 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -495,8 +495,8 @@ Window::Window(Windows *windows, glui32 rock) : _windows(windows), _rock(rock),
 	_attr.bgcolor = 0;
 	_attr.hyper = 0;
 
-	Common::fill(&_bgColor[0], &_bgColor[3], 3);
-	Common::fill(&_fgColor[0], &_fgColor[3], 3);
+	Common::copy(&g_conf->_windowColor[0], &g_conf->_windowColor[3], &_bgColor[0]);
+	Common::copy(&g_conf->_moreColor[0], &g_conf->_moreColor[3], _fgColor);
 	_dispRock.num = 0;
 
 	Streams &streams = *g_vm->_streams;


Commit: 4fcffce50568bb3c202720bbb90bd92cfefbc83f
    https://github.com/scummvm/scummvm/commit/4fcffce50568bb3c202720bbb90bd92cfefbc83f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Changes of NULL to nullptr

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/events.cpp
    engines/gargoyle/glk.cpp
    engines/gargoyle/unicode.cpp
    engines/gargoyle/unicode_gen.cpp
    engines/gargoyle/unicode_gen.h
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_pair.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/windows.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index a684889..3902db3 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -237,9 +237,9 @@ void Conf::parseColor(const Common::String &str, byte *color) {
 		g[0] = str[2]; g[1] = str[3]; g[2] = 0;
 		b[0] = str[4]; b[1] = str[5]; b[2] = 0;
 
-		color[0] = strtol(r, NULL, 16);
-		color[1] = strtol(g, NULL, 16);
-		color[2] = strtol(b, NULL, 16);
+		color[0] = strtol(r, nullptr, 16);
+		color[1] = strtol(g, nullptr, 16);
+		color[2] = strtol(b, nullptr, 16);
 	}
 }
 
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 6836faf..23a8baf 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -46,7 +46,7 @@ void Events::getEvent(event_t *event, bool polled) {
 	}
 
 	if (_currentEvent->type == evtype_None && _timeouts) {
-		store(evtype_Timer, NULL, 0, 0);
+		store(evtype_Timer, nullptr, 0, 0);
 		dispatchEvent(*_currentEvent, polled);
 		_timeouts = false;
 	}
@@ -179,7 +179,7 @@ void Events::handleMouseMove(const Point &pos) {
 		if (g_vm->_windowMask->getHyperlink(pos)) {
 			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_hand);
 		} else {
-			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), NULL);
+			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), nullptr);
 		}
 	}
 }
@@ -194,7 +194,7 @@ void Events::handleButtonDown(bool isLeft, const Point &pos) {
 void Events::handleButtonUp(bool isLeft, const Point &pos) {
 	if (isLeft) {
 		g_vm->_copySelect = false;
-		//gdk_window_set_cursor((GTK_WIDGET(widget)->window), NULL);
+		//gdk_window_set_cursor((GTK_WIDGET(widget)->window), nullptr);
 		g_vm->_clipboard->send(PRIMARY);
 	}
 }
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index abc4d2c..c9823a9 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -181,8 +181,6 @@ winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size, glui32 w
 }
 
 void Glk::glk_window_close(winid_t win, stream_result_t *result) {
-	_windows->_forceRedraw = true;
-
 	if (!win) {
 		warning("glk_window_close: invalid ref");
 	} else {
@@ -280,7 +278,7 @@ void Glk::glk_window_clear(winid_t win) {
 		warning("window_clear: invalid ref");
 	} else if (win->_lineRequest || win->_lineRequestUni) {
 		if (g_conf->_safeClicks && _events->_forceClick) {
-			glk_cancel_line_event(win, NULL);
+			glk_cancel_line_event(win, nullptr);
 			_events->_forceClick = false;
 
 			win->clear();
diff --git a/engines/gargoyle/unicode.cpp b/engines/gargoyle/unicode.cpp
index c547d2b..c783915 100644
--- a/engines/gargoyle/unicode.cpp
+++ b/engines/gargoyle/unicode.cpp
@@ -63,7 +63,7 @@ glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCa
 	if (dest_block_first == CASE_TITLE)
 		dest_block_first = CASE_UPPER;
 
-	newoutbuf = NULL;
+	newoutbuf = nullptr;
 	outcount = 0;
 	outbuf = buf;
 
diff --git a/engines/gargoyle/unicode_gen.cpp b/engines/gargoyle/unicode_gen.cpp
index a6b0bc5..c9ad570 100644
--- a/engines/gargoyle/unicode_gen.cpp
+++ b/engines/gargoyle/unicode_gen.cpp
@@ -11522,7 +11522,7 @@ switch ((glui32)(ch) >> 8) {  \
         *blockptr = unigen_decomp_block_0x2fa;  \
         break;  \
     default:  \
-        *blockptr = NULL;  \
+        *blockptr = nullptr;  \
 }
 
 #define GET_DECOMP_SPECIAL(ch, countptr, posptr)  \
diff --git a/engines/gargoyle/unicode_gen.h b/engines/gargoyle/unicode_gen.h
index f621a93..1539ec4 100644
--- a/engines/gargoyle/unicode_gen.h
+++ b/engines/gargoyle/unicode_gen.h
@@ -69,7 +69,7 @@ case 0x104:  \
     *blockptr = unigen_case_block_0x104;  \
     break;  \
 default:  \
-    *blockptr = NULL;  \
+    *blockptr = nullptr;  \
 }
 
 #define GET_CASE_SPECIAL(ch, specptr)  \
@@ -420,7 +420,7 @@ case 0xfb17:  \
     *specptr = unigen_special_0xfb17;  \
     break;  \
 default:  \
-    *specptr = NULL;  \
+    *specptr = nullptr;  \
 }
 
 typedef glui32 gli_case_block_t[2];   ///< upper, lower
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index 6609f0a..b31d620 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -53,7 +53,7 @@ void GraphicsWindow::rearrange(const Rect &box) {
 		_w = 0;
 		_h = 0;
 		delete _surface;
-		_surface = NULL;
+		_surface = nullptr;
 		return;
 	}
 
diff --git a/engines/gargoyle/window_pair.h b/engines/gargoyle/window_pair.h
index 2037b3d..7e6178e 100644
--- a/engines/gargoyle/window_pair.h
+++ b/engines/gargoyle/window_pair.h
@@ -38,7 +38,7 @@ public:
 	glui32 _dir;               ///< winmethod_Left, Right, Above, or Below
 	bool _vertical, _backward; ///< flags
 	glui32 _division;          ///< winmethod_Fixed or winmethod_Proportional
-	Window *_key;              ///< NULL or a leaf-descendant (not a Pair)
+	Window *_key;              ///< nullptr or a leaf-descendant (not a Pair)
 	int _keyDamage;            ///< used as scratch space in window closing
 	glui32 _size;              ///< size value
 	glui32 _wBorder;           ///< winMethod_Border, NoBorder
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index b1effc4..55ccdcc 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -446,13 +446,13 @@ void TextGridWindow::acceptLine(glui32 keycode) {
 			val2 = 0;
 		g_vm->_events->store(evtype_LineInput, this, _inLen, val2);
 		free(_lineTerminators);
-		_lineTerminators = NULL;
+		_lineTerminators = nullptr;
 	} else {
 		g_vm->_events->store(evtype_LineInput, this, _inLen, 0);
 	}
 	_lineRequest = false;
 	_lineRequestUni = false;
-	_inBuf = NULL;
+	_inBuf = nullptr;
 	_inMax = 0;
 	_inOrgX = 0;
 	_inOrgY = 0;
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index bf126eb..6d09d82 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -80,15 +80,15 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 
 	if (!_rootWin) {
 		if (splitwin) {
-			warning("window_open: ref must be NULL");
+			warning("window_open: ref must be nullptr");
 			return nullptr;
 		}
 
 		/* ignore method and size now */
-		oldparent = NULL;
+		oldparent = nullptr;
 	} else {
 		if (!splitwin) {
-			warning("window_open: ref must not be NULL");
+			warning("window_open: ref must not be nullptr");
 			return nullptr;
 		}
 
@@ -152,6 +152,8 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 }
 
 void Windows::windowClose(Window *win, StreamResult *result) {
+	_forceRedraw = true;
+
 	if (win == _rootWin || win->_parent == nullptr) {
 		// Close the root window, which means all windows.
 		_rootWin = nullptr;


Commit: 48a00e7c0019ea2cc0681342baa828d9cfb6c00d
    https://github.com/scummvm/scummvm/commit/48a00e7c0019ea2cc0681342baa828d9cfb6c00d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Set Windows fields from int to bool

Changed paths:
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 231ed67..93d7ac2 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -288,15 +288,15 @@ public:
 	Stream *_stream;       ///< the window stream.
 	Stream *_echoStream;   ///< the window's echo stream, if any.
 
-	int _lineRequest;
-	int _lineRequestUni;
-	int _charRequest;
-	int _charRequestUni;
-	int _mouseRequest;
-	int _hyperRequest;
-	int _moreRequest;
-	int _scrollRequest;
-	int _imageLoaded;
+	bool _lineRequest;
+	bool _lineRequestUni;
+	bool _charRequest;
+	bool _charRequestUni;
+	bool _mouseRequest;
+	bool _hyperRequest;
+	bool _moreRequest;
+	bool _scrollRequest;
+	bool _imageLoaded;
 
 	glui32 _echoLineInputBase;
 	glui32 *_lineTerminatorsBase;


Commit: 1a889689f863b58cf3afdf09e4cb7d98f2ef1477
    https://github.com/scummvm/scummvm/commit/1a889689f863b58cf3afdf09e4cb7d98f2ef1477
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added pair window drawing

Changed paths:
    engines/gargoyle/window_pair.cpp
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp


diff --git a/engines/gargoyle/window_pair.cpp b/engines/gargoyle/window_pair.cpp
index 3f0f294..26903cd 100644
--- a/engines/gargoyle/window_pair.cpp
+++ b/engines/gargoyle/window_pair.cpp
@@ -22,6 +22,8 @@
 
 #include "gargoyle/window_pair.h"
 #include "gargoyle/conf.h"
+#include "gargoyle/gargoyle.h"
+#include "gargoyle/screen.h"
 
 namespace Gargoyle {
 
@@ -121,7 +123,25 @@ void PairWindow::rearrange(const Rect &box) {
 void PairWindow::redraw() {
 	Window::redraw();
 
-	// TODO
+	_child1->redraw();
+	_child2->redraw();
+
+	Window *child = !_backward ? _child1 : _child2;
+	Rect box(child->_bbox.left, child->_yAdj ? child->_bbox.top - child->_yAdj : child->_bbox.top,
+		child->_bbox.right, child->_bbox.bottom);
+
+	if (_vertical) {
+		int xBord = _wBorder ? g_conf->_wBorderX : 0;
+		int xPad = (g_conf->_wPaddingX - xBord) / 2;
+		
+		g_vm->_screen->fillRect(Rect(box.right + xPad, box.top, box.right + xPad + xBord, box.bottom),
+			g_conf->_borderColor);
+	} else {
+		int yBord = _wBorder ? g_conf->_wBorderY : 0;
+		int yPad = (g_conf->_wPaddingY - yBord) / 2;
+		g_vm->_screen->fillRect(Rect(box.left, box.bottom + yPad, box.right, box.bottom + yPad + yBord),
+			g_conf->_borderColor);
+	}
 }
 
 void PairWindow::getArrangement(glui32 *method, glui32 *size, Window **keyWin) {
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 3946771..d40dcf1 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -611,6 +611,7 @@ void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen
 		return;
 	}
 
+	_lineRequest = true;
 	int pw;
 
 	gli_tts_flush();
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 55ccdcc..cad18af 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -223,6 +223,8 @@ void TextGridWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen)
 		return;
 	}
 
+	_lineRequest = true;
+
 	if ((int)maxlen > (_width - _curX))
 		maxlen = (_width - _curX);
 


Commit: 0a21220707b879b5fcfe0cbed1774e808ddac68f
    https://github.com/scummvm/scummvm/commit/0a21220707b879b5fcfe0cbed1774e808ddac68f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add game frames to actually render the screen

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/window_mask.cpp


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 23a8baf..ef4c59d 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -23,10 +23,28 @@
 #include "gargoyle/events.h"
 #include "gargoyle/clipboard.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/screen.h"
 #include "gargoyle/windows.h"
 
 namespace Gargoyle {
 
+Events::Events() : _forceClick(false), _currentEvent(nullptr), _timeouts(false),
+	_priorFrameTime(0), _frameCounter(0) {
+}
+
+bool Events::checkForNextFrameCounter() {
+	// Check for next game frame
+	uint32 milli = g_system->getMillis();
+	if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) {
+		++_frameCounter;
+		_priorFrameTime = milli;
+
+		return true;
+	}
+
+	return false;
+}
+
 void Events::getEvent(event_t *event, bool polled) {
 	_currentEvent  = event;
 	event->clear();
@@ -89,6 +107,11 @@ void Events::dispatchEvent(Event &ev, bool polled) {
 void Events::pollEvents() {
 	Common::Event event;
 
+	if (checkForNextFrameCounter()) {
+		// Update the screen
+		g_vm->_screen->update();
+	}
+
 	do {
 		g_system->getEventManager()->pollEvent(event);
 
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index f49ef35..4d9e49b 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -28,6 +28,9 @@
 
 namespace Gargoyle {
 
+#define GAME_FRAME_RATE 100
+#define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE)
+
 class Window;
 
 /**
@@ -148,8 +151,15 @@ private:
 	EventQueue _eventsLogged;		///< Custom events generated by game code
 	Event *_currentEvent;			///< Event pointer passed during event retrieval
 	bool _timeouts;					///< Timer timeouts flag
+	uint32 _priorFrameTime;			///< Time of prior game frame
+	uint32 _frameCounter;			///< Frame counter
 private:
 	/**
+	 * Checks for whether it's time for the next game frame
+	 */
+	bool checkForNextFrameCounter();
+
+	/**
 	 * Dispatches an event
 	 */
 	void dispatchEvent(Event &ev, bool polled);
@@ -189,7 +199,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Events() : _forceClick(false), _currentEvent(nullptr), _timeouts(false) {}
+	Events();
 
 	/**
 	  * Get any pending event
diff --git a/engines/gargoyle/window_mask.cpp b/engines/gargoyle/window_mask.cpp
index b3b629b..6c85789 100644
--- a/engines/gargoyle/window_mask.cpp
+++ b/engines/gargoyle/window_mask.cpp
@@ -24,6 +24,7 @@
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/windows.h"
+#include "common/system.h"
 
 namespace Gargoyle {
 
@@ -32,6 +33,7 @@ int WindowMask::_lastY;
 
 WindowMask::WindowMask() : _hor(0), _ver(0), _links(nullptr) {
 	_lastX = _lastY = 0;
+	resize(g_system->getWidth(), g_system->getHeight());
 }
 
 void WindowMask::resize(size_t x, size_t y) {


Commit: 4fc1c30423aa8e161a3ea2edd1f45c4ad681a92a
    https://github.com/scummvm/scummvm/commit/4fc1c30423aa8e161a3ea2edd1f45c4ad681a92a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix Attributes equality checks

Changed paths:
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index d40dcf1..4db0bf9 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -960,9 +960,8 @@ void TextBufferWindow::redraw() {
 
         x = x0 + SLOP + ln->_lm;
         a = 0;
-        for (b = 0; b < linelen; b++)
-        {
-            if (ln->_attrs[a] == ln->_attrs[b]) {
+        for (b = 0; b < linelen; b++) {
+            if (ln->_attrs[a] != ln->_attrs[b]) {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = ln->_attrs[a].attrBg(_styles);
@@ -1015,7 +1014,7 @@ void TextBufferWindow::redraw() {
         a = 0;
         for (b = 0; b < linelen; b++)
         {
-            if (ln->_attrs[a] == ln->_attrs[b]) {
+            if (ln->_attrs[a] != ln->_attrs[b]) {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
@@ -1603,7 +1602,7 @@ int TextBufferWindow::calcWidth(glui32 *chars, Attributes *attrs, int startchar,
 
 	a = startchar;
 	for (b = startchar; b < numChars; b++) {
-		if (attrs[a] == attrs[b]) {
+		if (attrs[a] != attrs[b]) {
 			w += screen.stringWidthUni(attrs[a].attrFont(_styles),
 				Common::U32String(chars + a, b - a), spw);
 			a = b;
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index cad18af..6d1aff4 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -599,7 +599,7 @@ void TextGridWindow::redraw() {
 
 			a = 0;
 			for (b = 0; b < _width; b++) {
-				if (ln->_attrs[a] == ln->_attrs[b]) {
+				if (ln->_attrs[a] != ln->_attrs[b]) {
 					link = ln->_attrs[a].hyper;
 					font = ln->_attrs[a].attrFont(styles);
 					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 93d7ac2..d753d53 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -261,12 +261,27 @@ struct Attributes {
 			&& style == src.style && fgcolor == src.fgcolor && bgcolor == src.bgcolor
 			&& hyper == src.hyper;
 	}
+	/**
+	 * Inequality comparison
+	 */
+	bool operator!=(const Attributes &src) {
+		return fgset != src.fgset || bgset != src.bgset || reverse != src.reverse
+			|| style != src.style || fgcolor != src.fgcolor || bgcolor != src.bgcolor
+			|| hyper != src.hyper;
+	}
 
+	/**
+	 * Return the background color for the current font style
+	 */
 	byte *attrBg(WindowStyle *styles);
+
+	/**
+	 * Return the foreground color for the current font style
+	 */
 	byte *attrFg(WindowStyle *styles);
 
 	/**
-	 * Get the font from the attribute's style
+	 * Get the font for the current font style
 	 */
 	FACES attrFont(WindowStyle *styles) const { return styles[style].font; }
 };


Commit: e004784d23bf14c8176a1a394c32fbe01c7c488f
    https://github.com/scummvm/scummvm/commit/e004784d23bf14c8176a1a394c32fbe01c7c488f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix calculation of text grid height

Changed paths:
    engines/gargoyle/window_text_grid.cpp


diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 6d1aff4..6945aca 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -80,8 +80,7 @@ void TextGridWindow::touch(int line) {
 }
 
 glui32 TextGridWindow::getSplit(glui32 size, bool vertical) const {
-	return vertical ? size * g_conf->_cellW + g_conf->_tMarginX * 2 :
-		size * g_conf->_cellH + g_conf->_tMarginY * 2;
+	return vertical ? size * g_conf->_cellW : size * g_conf->_cellH;
 }
 
 void TextGridWindow::putCharUni(uint32 ch) {


Commit: 0d3495bb3b9bf172452e7bdc9ebbc9b23fe6b947
    https://github.com/scummvm/scummvm/commit/0d3495bb3b9bf172452e7bdc9ebbc9b23fe6b947
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add missing configuration defaults

Changed paths:
    engines/gargoyle/conf.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 3902db3..553f02c 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -89,8 +89,8 @@ Conf::Conf() {
 	get("propi", _propI);
 	get("propz", _propZ);
 	get("propfont", _propFont, "Linux Libertine O");
-	get("leading", _leading);
-	get("baseline", _baseLine);
+	get("leading", _leading, 20);
+	get("baseline", _baseLine, 15);
 	get("rows", _rows, 25);
 	get("cols", _cols, 60);
 


Commit: c5668d9300a78fb7d459b53fb2f9d03a418fd63b
    https://github.com/scummvm/scummvm/commit/c5668d9300a78fb7d459b53fb2f9d03a418fd63b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Further configuration fixes

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/fonts.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 553f02c..7e11a2f 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -89,7 +89,7 @@ Conf::Conf() {
 	get("propi", _propI);
 	get("propz", _propZ);
 	get("propfont", _propFont, "Linux Libertine O");
-	get("leading", _leading, 20);
+	get("leading", _leading, 8);
 	get("baseline", _baseLine, 15);
 	get("rows", _rows, 25);
 	get("cols", _cols, 60);
diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index bcb129c..307144b 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -65,6 +65,9 @@ Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface) {
 	_fontTable[5] = loadFont(PROPB, propSize, propAspect, FONTB);
 	_fontTable[6] = loadFont(PROPI, propSize, propAspect, FONTI);
 	_fontTable[7] = loadFont(PROPZ, propSize, propAspect, FONTZ);
+
+	g_conf->_cellW = _fontTable[0]->getStringWidth("0");
+	g_conf->_cellH = _fontTable[0]->getFontHeight();
 }
 
 Fonts::~Fonts() {


Commit: 7ebbcc87dc5f53a66092b34975f16e4c0629590b
    https://github.com/scummvm/scummvm/commit/7ebbcc87dc5f53a66092b34975f16e4c0629590b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix rendering text in text grid windows

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/fonts.cpp
    engines/gargoyle/window_text_grid.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 7e11a2f..31e2c5f 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -69,6 +69,8 @@ Conf::Conf() {
 	_imageW = g_system->getWidth();
 	_imageH = g_system->getHeight();
 	_cellW = _cellH = 8;
+	_leading = 8;
+	_baseLine = 0;
 
 	get("moreprompt", _morePrompt, "\207 more \207");
 	get("morecolor", _moreColor);
@@ -89,11 +91,18 @@ Conf::Conf() {
 	get("propi", _propI);
 	get("propz", _propZ);
 	get("propfont", _propFont, "Linux Libertine O");
-	get("leading", _leading, 8);
-	get("baseline", _baseLine, 15);
 	get("rows", _rows, 25);
 	get("cols", _cols, 60);
 
+	/* Disabled for now, since Fonts constructor resets them
+	if (ConfMan.hasKey("leading"))
+		_leading = atof(ConfMan.get("leading").c_str()) + 0.5;
+	if (ConfMan.hasKey("baseline"))
+		_baseLine = atof(ConfMan.get("baseline").c_str()) + 0.5;
+	if (!_baseLine)
+		_baseLine = _propSize + 0.5;
+	*/
+
 	if (ConfMan.hasKey("minrows"))
 		_rows = MAX(_rows, strToInt(ConfMan.get("minrows").c_str()));
 	if (ConfMan.hasKey("maxrows"))
diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 307144b..7aaf27b 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -38,18 +38,6 @@ const char *gli_conf_monob = "GoMono-Bold";
 const char *gli_conf_monoi = "GoMono-Italic";
 const char *gli_conf_monoz = "GoMono-BoldItalic";
 
-#ifdef BUNDLED_FONTS
-const char *gli_conf_monofont = "";
-const char *gli_conf_propfont = "";
-const double gli_conf_monosize = 12.5;	///< good size for GoMono
-const double gli_conf_propsize = 13.4;	///< good size for NotoSerif
-#else
-const char *gli_conf_monofont = "Liberation Mono";
-const char *gli_conf_propfont = "Linux Libertine O";
-const double gli_conf_monosize = 12.5;	///< good size for LiberationMono
-const double gli_conf_propsize = 15.5;	///< good size for Libertine
-#endif
-
 Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface) {
 	double monoAspect = g_conf->_monoAspect;
 	double propAspect = g_conf->_propAspect;
@@ -68,6 +56,7 @@ Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface) {
 
 	g_conf->_cellW = _fontTable[0]->getStringWidth("0");
 	g_conf->_cellH = _fontTable[0]->getFontHeight();
+	g_conf->_leading = g_conf->_baseLine = g_conf->_cellH;
 }
 
 Fonts::~Fonts() {
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 6945aca..455655f 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -588,7 +588,7 @@ void TextGridWindow::redraw() {
 	for (i = 0; i < _height; i++) {
 		ln = &_lines[i];
 		if (ln->dirty || Windows::_forceRedraw) {
-			ln->dirty = 0;
+			ln->dirty = false;
 
 			x = x0;
 			y = y0 + i * g_conf->_leading;
@@ -607,16 +607,15 @@ void TextGridWindow::redraw() {
 					screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
 					o = x;
 
-					for (k = a; k < b; k++) {
-						screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine),
-							font, fgcolor, Common::U32String(&ln->_chars[k], 1), -1);
-						o += g_conf->_cellW;
+					for (k = a, o = x; k < b; k++, o += g_conf->_cellW) {
+						screen.drawStringUni(Point(o, y), font, fgcolor, Common::U32String(&ln->_chars[k], 1), -1);
 					}
 					if (link) {
 						screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w,
 							g_conf->_linkStyle), g_conf->_linkColor);
 						g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 					}
+
 					x += w;
 					a = b;
 				}
@@ -629,11 +628,8 @@ void TextGridWindow::redraw() {
 			w += _bbox.right - (x + w);
 			screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
 
-			o = x;
-			for (k = a; k < b; k++) {
-				screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine),
-					font, fgcolor, Common::U32String(&ln->_chars[k], 1));
-				o += g_conf->_cellW;
+			for (k = a, o = x; k < b; k++, o += g_conf->_cellW) {
+				screen.drawStringUni(Point(o, y), font, fgcolor, Common::U32String(&ln->_chars[k], 1));
 			}
 			if (link) {
 				screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w, g_conf->_linkStyle),


Commit: 7c7178c4191b734805cca49e375beafdc8fc4315
    https://github.com/scummvm/scummvm/commit/7c7178c4191b734805cca49e375beafdc8fc4315
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Text is partially displaying in the buffer window

Changed paths:
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 4db0bf9..ff60345 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -1012,13 +1012,12 @@ void TextBufferWindow::redraw() {
 
         x = x0 + SLOP + ln->_lm;
         a = 0;
-        for (b = 0; b < linelen; b++)
-        {
+        for (b = 0; b < linelen; b++) {
             if (ln->_attrs[a] != ln->_attrs[b]) {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-                x = screen.drawStringUni(Point(x, y + g_conf->_baseLine),
+                x = screen.drawStringUni(Point(x / GLI_SUBPIX, y),
                         font, color, Common::U32String(ln->_chars + a, b - a), spw);
                 a = b;
             }
@@ -1026,8 +1025,7 @@ void TextBufferWindow::redraw() {
         link = ln->_attrs[a].hyper;
         font = ln->_attrs[a].attrFont(_styles);
         color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-        screen.drawStringUni(Point(x, y + g_conf->_baseLine),
-                font, color, Common::U32String(ln->_chars + a, linelen - a), spw);
+        screen.drawStringUni(Point(x / GLI_SUBPIX, y), font, color, Common::U32String(ln->_chars + a, linelen - a), spw);
     }
 
     /*


Commit: 666edf52a5965d12a1ef4218a62c6ec5cf5fec46
    https://github.com/scummvm/scummvm/commit/666edf52a5965d12a1ef4218a62c6ec5cf5fec46
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Don't have borders between windows by default

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/window_pair.h


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 31e2c5f..383bddb 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -121,8 +121,8 @@ Conf::Conf() {
 
 	get("wpaddingx", _wPaddingX);
 	get("wpaddingy", _wPaddingY);
-	get("wborderx", _wBorderX, 1);
-	get("wbordery", _wBorderY, 1);
+	get("wborderx", _wBorderX);
+	get("wbordery", _wBorderY);
 	get("tmarginx", _tMarginX, 7);
 	get("tmarginy", _tMarginY, 7);
 	get("gamma", _gamma, 1.0);
diff --git a/engines/gargoyle/window_pair.h b/engines/gargoyle/window_pair.h
index 7e6178e..a28f7fd0 100644
--- a/engines/gargoyle/window_pair.h
+++ b/engines/gargoyle/window_pair.h
@@ -41,7 +41,7 @@ public:
 	Window *_key;              ///< nullptr or a leaf-descendant (not a Pair)
 	int _keyDamage;            ///< used as scratch space in window closing
 	glui32 _size;              ///< size value
-	glui32 _wBorder;           ///< winMethod_Border, NoBorder
+	bool _wBorder;             ///< If windows are separated by border
 public:
 	/**
 	 * Constructor


Commit: f17998762d632fd79075bc2164c21272304ba232
    https://github.com/scummvm/scummvm/commit/f17998762d632fd79075bc2164c21272304ba232
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add drawCaret method

Changed paths:
    engines/gargoyle/screen.cpp
    engines/gargoyle/screen.h
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/screen.cpp b/engines/gargoyle/screen.cpp
index f500b86..ab49ac9 100644
--- a/engines/gargoyle/screen.cpp
+++ b/engines/gargoyle/screen.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "gargoyle/screen.h"
+#include "gargoyle/conf.h"
 
 namespace Gargoyle {
 
@@ -35,7 +36,36 @@ void Screen::fillRect(const Rect &box, const byte *rgb) {
 }
 
 void Screen::drawCaret(const Point &pos) {
-	// TODO
+	const byte *rgb = g_conf->_caretColor;
+	uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+
+	switch (g_conf->_caretShape) {
+	case SMALL_DOT:
+		hLine(pos.x + 0, pos.y + 1, pos.x + 0, color);
+		hLine(pos.x - 1, pos.y + 2, pos.x + 1, color);
+		hLine(pos.x - 2, pos.y + 3, pos.x + 2, color);
+		break;
+
+	case FAT_DOT:
+		hLine(pos.x + 0, pos.y + 1, pos.x + 0, color);
+		hLine(pos.x - 1, pos.y + 2, pos.x + 1, color);
+		hLine(pos.x - 2, pos.y + 3, pos.x + 2, color);
+		hLine(pos.x - 3, pos.y + 4, pos.x + 3, color);
+		break;
+
+	case THIN_LINE:
+		vLine(pos.x, pos.y - g_conf->_baseLine + 1, pos.y - 1, color);
+		break;
+
+	case FAT_LINE:
+		Graphics::Screen::fillRect(Rect(pos.x, pos.y - g_conf->_baseLine + 1, pos.x + 1,  pos.y - 1), color);
+		break;
+
+	default:
+		// BLOCK
+		Graphics::Screen::fillRect(Rect(pos.x, pos.y - g_conf->_baseLine + 1, pos.x + g_conf->_cellW, pos.y - 1), color);
+		break;
+	}
 }
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/screen.h b/engines/gargoyle/screen.h
index 3974096..66553c3 100644
--- a/engines/gargoyle/screen.h
+++ b/engines/gargoyle/screen.h
@@ -28,6 +28,13 @@
 
 namespace Gargoyle {
 
+enum CaretShape {
+	SMALL_DOT = 0, FAT_DOT = 1, THIN_LINE = 2, FAT_LINE = 3, BLOCK = 4
+};
+
+/**
+ * Screen surface class
+ */
 class Screen : public Graphics::Screen, public Fonts {
 public:
 	/**
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index ff60345..b22246d 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -1003,7 +1003,7 @@ void TextBufferWindow::redraw() {
         if (_windows->getFocusWindow() == this && i == 0 && (_lineRequest || _lineRequestUni)) {
             w = calcWidth(_chars, _attrs, 0, _inCurs, spw);
             if (w < pw - g_conf->_caretShape * 2 * GLI_SUBPIX)
-                screen.drawCaret(Point(x0 + SLOP + ln->_lm + w, y + g_conf->_baseLine));
+                screen.drawCaret(Point((x0 + SLOP + ln->_lm + w) / GLI_SUBPIX, y + g_conf->_baseLine));
         }
 
         /*


Commit: e9c7b302461f73bdb387aebdc56add59e4c887e3
    https://github.com/scummvm/scummvm/commit/e9c7b302461f73bdb387aebdc56add59e4c887e3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Proper handling of font baseline and leading

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h
    engines/gargoyle/screen.cpp
    engines/gargoyle/screen.h
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 383bddb..ae67a8a 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -69,7 +69,7 @@ Conf::Conf() {
 	_imageW = g_system->getWidth();
 	_imageH = g_system->getHeight();
 	_cellW = _cellH = 8;
-	_leading = 8;
+	_leading = 0;
 	_baseLine = 0;
 
 	get("moreprompt", _morePrompt, "\207 more \207");
@@ -79,13 +79,13 @@ Conf::Conf() {
 	get("morealign", _moreAlign);
 	get("monoaspect", _monoAspect, 1.0);
 	get("propaspect", _propAspect, 1.0);
-	get("monosize", _monoSize, 12.5);
+	get("monosize", _monoSize, 8);
 	get("monor", _monoR);
 	get("monob", _monoR);
 	get("monoi", _monoI);
 	get("monoz", _monoZ);
 	get("monofont", _monoFont, "Liberation Mono");
-	get("propsize", _propSize, 15.5);
+	get("propsize", _propSize, 12);
 	get("propr", _propR);
 	get("propb", _propR);
 	get("propi", _propI);
@@ -94,14 +94,10 @@ Conf::Conf() {
 	get("rows", _rows, 25);
 	get("cols", _cols, 60);
 
-	/* Disabled for now, since Fonts constructor resets them
 	if (ConfMan.hasKey("leading"))
 		_leading = atof(ConfMan.get("leading").c_str()) + 0.5;
 	if (ConfMan.hasKey("baseline"))
 		_baseLine = atof(ConfMan.get("baseline").c_str()) + 0.5;
-	if (!_baseLine)
-		_baseLine = _propSize + 0.5;
-	*/
 
 	if (ConfMan.hasKey("minrows"))
 		_rows = MAX(_rows, strToInt(ConfMan.get("minrows").c_str()));
diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 7aaf27b..7cf4e1d 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -54,9 +54,13 @@ Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface) {
 	_fontTable[6] = loadFont(PROPI, propSize, propAspect, FONTI);
 	_fontTable[7] = loadFont(PROPZ, propSize, propAspect, FONTZ);
 
+	if (!g_conf->_leading)
+		g_conf->_leading = g_conf->_propSize + 2;
+	if (!g_conf->_baseLine)
+		g_conf->_baseLine = g_conf->_propSize + 0.5;
+
 	g_conf->_cellW = _fontTable[0]->getStringWidth("0");
-	g_conf->_cellH = _fontTable[0]->getFontHeight();
-	g_conf->_leading = g_conf->_baseLine = g_conf->_cellH;
+	g_conf->_cellH = g_conf->_leading;
 }
 
 Fonts::~Fonts() {
@@ -96,18 +100,20 @@ Graphics::Font *Fonts::loadFont(FACES face, double size, double aspect, int styl
 }
 
 int Fonts::drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
+	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
 	Graphics::Font *font = _fontTable[fontIdx];
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
-	font->drawString(_surface, text, pos.x, pos.y, _surface->w - pos.x, color);
-	return font->getBoundingBox(text, pos.x, pos.y, _surface->w - pos.x).right;
+	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
+	return font->getBoundingBox(text, pt.x, pt.y, _surface->w - pt.x).right;
 }
 
 int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
+	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
 	Graphics::Font *font = _fontTable[fontIdx];
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
-	font->drawString(_surface, text, pos.x, pos.y, _surface->w - pos.x, color);
+	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
 
-	return font->getBoundingBox(text, pos.x, pos.y, _surface->w - pos.x).right;
+	return font->getBoundingBox(text, pt.x, pt.y, _surface->w - pt.x).right;
 }
 
 size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index 4472025..f2a6eb8 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -59,12 +59,40 @@ public:
 	 */
 	virtual ~Fonts();
 
+	/**
+	 * Draws a string using the specified font at the given co-ordinates
+	 * @param pos		Position for the bottom-left corner the text will be drawn with
+	 * @param fontIdx	Which font to use
+	 * @param rgb		RGB tuplet specifying the text color
+	 * @param text		The text to draw
+	 * @param spw		??
+	 */
 	int drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
 
+	/**
+	 * Draws a unicode string using the specified font at the given co-ordinates
+	 * @param pos		Position for the bottom-left corner the text will be drawn with
+	 * @param fontIdx	Which font to use
+	 * @param rgb		RGB tuplet specifying the text color
+	 * @param text		The text to draw
+	 * @param spw		??
+	 */
 	int drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
 
+	/**
+	 * Get the width in pixels of a string
+	 * @param fontIdx   Which font to use
+	 * @param text		Text to get the width of
+	 * @param spw		???
+	 */
 	size_t stringWidth(int fontIdx, const Common::String &text, int spw = -1);
 
+	/**
+	 * Get the width in pixels of a unicode string
+	 * @param fontIdx   Which font to use
+	 * @param text		Text to get the width of
+	 * @param spw		???
+	 */
 	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = -1);
 };
 
diff --git a/engines/gargoyle/screen.cpp b/engines/gargoyle/screen.cpp
index ab49ac9..6aed8f9 100644
--- a/engines/gargoyle/screen.cpp
+++ b/engines/gargoyle/screen.cpp
@@ -38,32 +38,33 @@ void Screen::fillRect(const Rect &box, const byte *rgb) {
 void Screen::drawCaret(const Point &pos) {
 	const byte *rgb = g_conf->_caretColor;
 	uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+	int x = pos.x / GLI_SUBPIX, y = pos.y;
 
 	switch (g_conf->_caretShape) {
 	case SMALL_DOT:
-		hLine(pos.x + 0, pos.y + 1, pos.x + 0, color);
-		hLine(pos.x - 1, pos.y + 2, pos.x + 1, color);
-		hLine(pos.x - 2, pos.y + 3, pos.x + 2, color);
+		hLine(x + 0, y + 1, x + 0, color);
+		hLine(x - 1, y + 2, x + 1, color);
+		hLine(x - 2, y + 3, x + 2, color);
 		break;
 
 	case FAT_DOT:
-		hLine(pos.x + 0, pos.y + 1, pos.x + 0, color);
-		hLine(pos.x - 1, pos.y + 2, pos.x + 1, color);
-		hLine(pos.x - 2, pos.y + 3, pos.x + 2, color);
-		hLine(pos.x - 3, pos.y + 4, pos.x + 3, color);
+		hLine(x + 0, y + 1, x + 0, color);
+		hLine(x - 1, y + 2, x + 1, color);
+		hLine(x - 2, y + 3, x + 2, color);
+		hLine(x - 3, y + 4, x + 3, color);
 		break;
 
 	case THIN_LINE:
-		vLine(pos.x, pos.y - g_conf->_baseLine + 1, pos.y - 1, color);
+		vLine(x, y - g_conf->_baseLine + 1, y - 1, color);
 		break;
 
 	case FAT_LINE:
-		Graphics::Screen::fillRect(Rect(pos.x, pos.y - g_conf->_baseLine + 1, pos.x + 1,  pos.y - 1), color);
+		Graphics::Screen::fillRect(Rect(x, y - g_conf->_baseLine + 1, x + 1,  y - 1), color);
 		break;
 
 	default:
 		// BLOCK
-		Graphics::Screen::fillRect(Rect(pos.x, pos.y - g_conf->_baseLine + 1, pos.x + g_conf->_cellW, pos.y - 1), color);
+		Graphics::Screen::fillRect(Rect(x, y - g_conf->_baseLine + 1, x + g_conf->_cellW, y - 1), color);
 		break;
 	}
 }
diff --git a/engines/gargoyle/screen.h b/engines/gargoyle/screen.h
index 66553c3..d7631ef 100644
--- a/engines/gargoyle/screen.h
+++ b/engines/gargoyle/screen.h
@@ -52,6 +52,11 @@ public:
 	 */
 	void fillRect(const Rect &box, const byte *rgb);
 
+	/**
+	 * Draws the text input caret at the given position
+	 * @remarks		The position specifies the caret's bottom-left corner,
+	 *		and the X position is in multiples of GLI_SUBPIX
+	 */
 	void drawCaret(const Point &pos);
 };
 
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index b22246d..34addcc 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -1003,7 +1003,7 @@ void TextBufferWindow::redraw() {
         if (_windows->getFocusWindow() == this && i == 0 && (_lineRequest || _lineRequestUni)) {
             w = calcWidth(_chars, _attrs, 0, _inCurs, spw);
             if (w < pw - g_conf->_caretShape * 2 * GLI_SUBPIX)
-                screen.drawCaret(Point((x0 + SLOP + ln->_lm + w) / GLI_SUBPIX, y + g_conf->_baseLine));
+                screen.drawCaret(Point(x0 + SLOP + ln->_lm + w, y + g_conf->_baseLine));
         }
 
         /*
@@ -1017,7 +1017,7 @@ void TextBufferWindow::redraw() {
                 link = ln->_attrs[a].hyper;
                 font = ln->_attrs[a].attrFont(_styles);
                 color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-                x = screen.drawStringUni(Point(x / GLI_SUBPIX, y),
+                x = screen.drawStringUni(Point(x, y + g_conf->_baseLine),
                         font, color, Common::U32String(ln->_chars + a, b - a), spw);
                 a = b;
             }
@@ -1025,14 +1025,13 @@ void TextBufferWindow::redraw() {
         link = ln->_attrs[a].hyper;
         font = ln->_attrs[a].attrFont(_styles);
         color = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
-        screen.drawStringUni(Point(x / GLI_SUBPIX, y), font, color, Common::U32String(ln->_chars + a, linelen - a), spw);
+        screen.drawStringUni(Point(x, y + g_conf->_baseLine), font, color, Common::U32String(ln->_chars + a, linelen - a), spw);
     }
 
     /*
      * draw more prompt
      */
-    if (_scrollPos && _height > 1)
-    {
+    if (_scrollPos && _height > 1) {
         x = x0 + SLOP;
         y = y0 + (_height - 1) * g_conf->_leading;
 
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 455655f..05c45fa 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -608,7 +608,8 @@ void TextGridWindow::redraw() {
 					o = x;
 
 					for (k = a, o = x; k < b; k++, o += g_conf->_cellW) {
-						screen.drawStringUni(Point(o, y), font, fgcolor, Common::U32String(&ln->_chars[k], 1), -1);
+						screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine), font,
+							fgcolor, Common::U32String(&ln->_chars[k], 1), -1);
 					}
 					if (link) {
 						screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w,
@@ -629,7 +630,8 @@ void TextGridWindow::redraw() {
 			screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
 
 			for (k = a, o = x; k < b; k++, o += g_conf->_cellW) {
-				screen.drawStringUni(Point(o, y), font, fgcolor, Common::U32String(&ln->_chars[k], 1));
+				screen.drawStringUni(Point(o * GLI_SUBPIX, y + g_conf->_baseLine), font,
+					fgcolor, Common::U32String(&ln->_chars[k], 1));
 			}
 			if (link) {
 				screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w, g_conf->_linkStyle),


Commit: 012d4bafbb44f3717fa4f7bfdddeb83eb6e906f2
    https://github.com/scummvm/scummvm/commit/012d4bafbb44f3717fa4f7bfdddeb83eb6e906f2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix placement of input cursor

Changed paths:
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h


diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 7cf4e1d..4f298b0 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -119,13 +119,13 @@ int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const C
 size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {
 	// TODO: Handle spw
 	Graphics::Font *font = _fontTable[fontIdx];
-	return font->getStringWidth(text);
+	return font->getStringWidth(text) * GLI_SUBPIX;
 }
 
 size_t Fonts::stringWidthUni(int fontIdx, const Common::U32String &text, int spw) {
 	// TODO: Handle spw
 	Graphics::Font *font = _fontTable[fontIdx];
-	return font->getStringWidth(text);
+	return font->getStringWidth(text) * GLI_SUBPIX;
 }
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index f2a6eb8..dc0bfd8 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -84,6 +84,7 @@ public:
 	 * @param fontIdx   Which font to use
 	 * @param text		Text to get the width of
 	 * @param spw		???
+	 * @returns			Width of string multiplied by GLI_SUBPIX
 	 */
 	size_t stringWidth(int fontIdx, const Common::String &text, int spw = -1);
 
@@ -92,6 +93,7 @@ public:
 	 * @param fontIdx   Which font to use
 	 * @param text		Text to get the width of
 	 * @param spw		???
+	 * @returns			Width of string multiplied by GLI_SUBPIX
 	 */
 	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = -1);
 };


Commit: c0fd6ca3e4d528d1a82daaf1140792853c243e45
    https://github.com/scummvm/scummvm/commit/c0fd6ca3e4d528d1a82daaf1140792853c243e45
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Cleanup of comments in text window classes

Changed paths:
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp


diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 34addcc..668fc3a 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -78,7 +78,7 @@ void TextBufferWindow::rearrange(const Rect &box) {
 	newwid = (box.width() - g_conf->_tMarginX * 2 - g_conf->_scrollWidth) / g_conf->_cellW;
 	newhgt = (box.height() - g_conf->_tMarginY * 2) / g_conf->_cellH;
 
-	/* align text with bottom */
+	// align text with bottom
 	rnd = newhgt * g_conf->_cellH + g_conf->_tMarginY * 2;
 	_yAdj = (box.height() - rnd);
 	_bbox.top += (box.height() - rnd);
@@ -89,20 +89,20 @@ void TextBufferWindow::rearrange(const Rect &box) {
 	}
 
 	if (newhgt != _height) {
-		/* scroll up if we obscure new lines */
+		// scroll up if we obscure new lines
 		if (_lastSeen >= newhgt - 1)
 			_scrollPos += (_height - newhgt);
 
 		_height = newhgt;
 
-		/* keep window within 'valid' lines */
+		// keep window within 'valid' lines
 		if (_scrollPos > _scrollMax - _height + 1)
 			_scrollPos = _scrollMax - _height + 1;
 		if (_scrollPos < 0)
 			_scrollPos = 0;
 		touchScroll();
 
-		/* allocate copy buffer */
+		// allocate copy buffer
 		if (_copyBuf)
 			delete[] _copyBuf;
 		_copyBuf = new glui32[_height * TBLINELEN];
@@ -125,7 +125,7 @@ void TextBufferWindow::reflow() {
 
 	_lines[0]._len = _numChars;
 
-	/* allocate temp buffers */
+	// allocate temp buffers
 	Attributes *attrbuf = new Attributes[SCROLLBACK * TBLINELEN];
 	glui32 *charbuf = new glui32[SCROLLBACK * TBLINELEN];
 	int *alignbuf = new int[SCROLLBACK];
@@ -143,7 +143,7 @@ void TextBufferWindow::reflow() {
 		return;
 	}
 
-	/* copy text to temp buffers */
+	// copy text to temp buffers
 
 	oldattr = _attr;
 	curattr.clear();
@@ -190,11 +190,11 @@ void TextBufferWindow::reflow() {
 
 	offsetbuf[x] = -1;
 
-	/* clear window */
+	// clear window
 
 	clear();
 
-	/* and dump text back */
+	// and dump text back
 
 	x = 0;
 	for (i = 0; i < p; i++) {
@@ -210,7 +210,7 @@ void TextBufferWindow::reflow() {
 		putCharUni(charbuf[i]);
 	}
 
-	/* terribly sorry about this... */
+	// terribly sorry about this...
 	_lastSeen = 0;
 	_scrollPos = 0;
 
@@ -369,7 +369,7 @@ void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldle
 
 void TextBufferWindow::touch(int line) {
 	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
-	_lines[line]._dirty = 1;
+	_lines[line]._dirty = true;
 	g_vm->_windowMask->clearSelection();
 	_windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
 }
@@ -447,8 +447,7 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 
 	if (g_conf->_spaces && _attr.style != style_Preformatted
 		&& _styles[_attr.style].bg == color
-		&& !_styles[_attr.style].reverse)
-	{
+		&& !_styles[_attr.style].reverse) {
 		// turn (period space space) into (period space)
 		if (g_conf->_spaces == 1) {
 			if (ch == '.')
@@ -616,13 +615,13 @@ void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen
 
 	gli_tts_flush();
 
-	/* because '>' prompt is ugly without extra space */
+	// because '>' prompt is ugly without extra space
 	if (_numChars && _chars[_numChars - 1] == '>')
 		putCharUni(' ');
 	if (_numChars && _chars[_numChars - 1] == '?')
 		putCharUni(' ');
 
-	/* make sure we have some space left for typing... */
+	// make sure we have some space left for typing...
 	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2) * GLI_SUBPIX;
 	pw = pw - 2 * SLOP - _radjw + _ladjw;
 	if (calcWidth(_chars, _attrs, 0, _numChars, -1) >= pw * 3 / 4)
@@ -667,13 +666,13 @@ void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 in
 
 	gli_tts_flush();
 
-	/* because '>' prompt is ugly without extra space */
+	// because '>' prompt is ugly without extra space
 	if (_numChars && _chars[_numChars - 1] == '>')
 		putCharUni(' ');
 	if (_numChars && _chars[_numChars - 1] == '?')
 		putCharUni(' ');
 
-	/* make sure we have some space left for typing... */
+	// make sure we have some space left for typing...
 	pw = (_bbox.right - _bbox.left - g_conf->_tMarginX * 2) * GLI_SUBPIX;
 	pw = pw - 2 * SLOP - _radjw + _ladjw;
 	if (calcWidth(_chars, _attrs, 0, _numChars, -1) >= pw * 3 / 4)
@@ -814,14 +813,14 @@ void TextBufferWindow::redraw() {
 
     pw = x1 - x0 - 2 * GLI_SUBPIX;
 
-    /* check if any part of buffer is selected */
+    // check if any part of buffer is selected
     selbuf = g_vm->_windowMask->checkSelection(x0/GLI_SUBPIX,y0,x1/GLI_SUBPIX,y1);
 
     for (i = _scrollPos + _height - 1; i >= _scrollPos; i--) {
-        /* top of line */
+        // top of line
         y = y0 + (_height - (i - _scrollPos) - 1) * g_conf->_leading;
 
-        /* check if part of line is selected */
+        // check if part of line is selected
         if (selbuf) {
             selrow = g_vm->_windowMask->getSelection(x0/GLI_SUBPIX, y,
                     x1/GLI_SUBPIX, y + g_conf->_leading,
@@ -832,22 +831,22 @@ void TextBufferWindow::redraw() {
             selrow = false;
         }
 
-        /* mark selected line dirty */
+        // mark selected line dirty
         if (selrow)
             _lines[i]._dirty = true;
 
         memcpy(ln, &_lines[i], sizeof(TextBufferRow));
 
-        /* skip if we can */
+        // skip if we can
         if (!ln->_dirty && !ln->_repaint && !Windows::_forceRedraw && _scrollPos == 0)
             continue;
 
-        /* repaint previously selected lines if needed */
+        // repaint previously selected lines if needed
         if (ln->_repaint && !Windows::_forceRedraw)
             _windows->redrawRect(Rect(x0 / GLI_SUBPIX, y,
 				x1/GLI_SUBPIX, y + g_conf->_leading));
 
-        /* keep selected line dirty and flag for repaint */
+        // keep selected line dirty and flag for repaint
         if (!selrow) {
             _lines[i]._dirty = false;
             _lines[i]._repaint = false;
@@ -855,20 +854,20 @@ void TextBufferWindow::redraw() {
             _lines[i]._repaint = true;
         }
 
-        /* leave bottom line blank for [more] prompt */
+        // leave bottom line blank for [more] prompt
         if (i == _scrollPos && i > 0)
             continue;
 
         linelen = ln->_len;
 
-        /* kill spaces at the end unless they're a different color*/
+        // kill spaces at the end unless they're a different color
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
         while (i > 0 && linelen > 1 && ln->_chars[linelen-1] == ' ' 
             && _styles[ln->_attrs[linelen-1].style].bg == color 
             && !_styles[ln->_attrs[linelen-1].style].reverse)
                 linelen --;
 
-        /* kill characters that would overwrite the scroll bar */
+        // kill characters that would overwrite the scroll bar
         while (linelen > 1 && calcWidth(ln->_chars, ln->_attrs, 0, linelen, -1) >= pw)
             linelen --;
 
@@ -888,24 +887,24 @@ void TextBufferWindow::redraw() {
             spw = -1;
         }
 
-        /* find and highlight selected characters */
+        // find and highlight selected characters
         if (selrow && !Windows::_claimSelect) {
             lsc = 0;
             rsc = 0;
             selchar = false;
-            /* optimized case for all chars selected */
+            // optimized case for all chars selected
             if (selleft && selright) {
                 rsc = linelen > 0 ? linelen - 1 : 0;
                 selchar = calcWidth(ln->_chars, ln->_attrs, lsc, rsc, spw)/GLI_SUBPIX;
             } else {
-                /* optimized case for leftmost char selected */
+                // optimized case for leftmost char selected
                 if (selleft) {
                     tsc = linelen > 0 ? linelen - 1 : 0;
                     selchar = calcWidth(ln->_chars, ln->_attrs, lsc, tsc, spw)/GLI_SUBPIX;
                 } else {
-                    /* find the substring contained by the selection */
+                    // find the substring contained by the selection
                     tx = (x0 + SLOP + ln->_lm)/GLI_SUBPIX;
-                    /* measure string widths until we find left char */
+                    // measure string widths until we find left char
                     for (tsc = 0; tsc < linelen; tsc++) {
                         tsw = calcWidth(ln->_chars, ln->_attrs, 0, tsc, spw)/GLI_SUBPIX;
                         if (tsw + tx >= sx0 ||
@@ -917,11 +916,11 @@ void TextBufferWindow::redraw() {
                     }
                 }
                 if (selchar) {
-                    /* optimized case for rightmost char selected */
+                    // optimized case for rightmost char selected
                     if (selright) {
                         rsc = linelen > 0 ? linelen - 1 : 0;
                     } else {
-                    /* measure string widths until we find right char */
+                    // measure string widths until we find right char
                         for (tsc = lsc; tsc < linelen; tsc++) {
                             tsw = calcWidth(ln->_chars, ln->_attrs, lsc, tsc, spw)/GLI_SUBPIX;
                             if (tsw + sx0 < sx1)
@@ -932,7 +931,7 @@ void TextBufferWindow::redraw() {
                     }
                 }
             }
-            /* reverse colors for selected chars */
+            // reverse colors for selected chars
             if (selchar) {
                 for (tsc = lsc; tsc <= rsc; tsc++) {
                     ln->_attrs[tsc].reverse = !ln->_attrs[tsc].reverse;
@@ -940,14 +939,14 @@ void TextBufferWindow::redraw() {
                     _copyPos++;
                 }
             }
-            /* add newline if we reach the end of the line */
+            // add newline if we reach the end of the line
             if (ln->_len == 0 || ln->_len == (rsc+1)) {
                 _copyBuf[_copyPos] = '\n';
                 _copyPos++;
             }
         }
 
-        /* clear any stored hyperlink coordinates */
+        // clear any stored hyperlink coordinates
         g_vm->_windowMask->putHyperlink(0, x0/GLI_SUBPIX, y,
                 x1/GLI_SUBPIX, y + g_conf->_leading);
 
@@ -1043,17 +1042,19 @@ void TextBufferWindow::redraw() {
 
         w = screen.stringWidth(g_conf->_moreFont, g_conf->_morePrompt);
 
-        if (g_conf->_moreAlign == 1)    /* center */
+        if (g_conf->_moreAlign == 1)
+			// center
             x = x0 + SLOP + (x1 - x0 - w - SLOP * 2) / 2;
-        if (g_conf->_moreAlign == 2)    /* right */
+        if (g_conf->_moreAlign == 2)
+			// right
             x = x1 - SLOP - w;
 
         color = Windows::_overrideFgSet ? g_conf->_moreColor : _fgColor;
 		screen.drawString(Point(x, y + g_conf->_baseLine),
                 g_conf->_moreFont, color, g_conf->_morePrompt);
-        y1 = y; /* don't want pictures overdrawing "[more]" */
+        y1 = y; // don't want pictures overdrawing "[more]"
 
-        /* try to claim the focus */
+        // try to claim the focus
         _moreRequest = true;
         Windows::_moreFocus = true;
     } else {
@@ -1103,7 +1104,7 @@ void TextBufferWindow::redraw() {
      * Draw the scrollbar
      */
 
-    /* try to claim scroll keys */
+    // try to claim scroll keys
     _scrollRequest = _scrollMax > _height;
 
     if (_scrollRequest && g_conf->_scrollWidth)
@@ -1140,7 +1141,7 @@ void TextBufferWindow::redraw() {
         }
     }
 
-    /* send selected text to clipboard */
+    // send selected text to clipboard
     if (selbuf && _copyPos) {
         Windows::_claimSelect = true;
 
@@ -1150,7 +1151,7 @@ void TextBufferWindow::redraw() {
         _copyPos = 0;
     }
 
-    /* no more prompt means all text has been seen */
+    // no more prompt means all text has been seen
     if (!_moreRequest)
         _lastSeen = 0;
 
@@ -1158,7 +1159,7 @@ void TextBufferWindow::redraw() {
 }
 
 int TextBufferWindow::acceptScroll(glui32 arg) {
-	int pageht = _height - 2;        /* 1 for prompt, 1 for overlap */
+	int pageht = _height - 2;        // 1 for prompt, 1 for overlap
 	int startpos = _scrollPos;
 
 	switch (arg) {
@@ -1266,9 +1267,7 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 	}
 
 	switch (arg) {
-
-		/* History keys (up and down) */
-
+	// History keys (up and down)
 	case keycode_Up:
 		if (_historyPos == _historyFirst)
 			return;
@@ -1304,8 +1303,7 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 			_numChars - _inFence);
 		break;
 
-		/* Cursor movement keys, during line input. */
-
+	// Cursor movement keys, during line input.
 	case keycode_Left:
 		if (_inCurs <= _inFence)
 			return;
@@ -1344,8 +1342,7 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 			_inCurs++;
 		break;
 
-		/* Delete keys, during line input. */
-
+	// Delete keys, during line input.
 	case keycode_Delete:
 		if (_inCurs <= _inFence)
 			return;
@@ -1364,8 +1361,7 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 		putTextUni(nullptr, 0, _inFence, _numChars - _inFence);
 		break;
 
-		/* Regular keys */
-
+	// Regular keys
 	case keycode_Return:
 		acceptLine(arg);
 		break;
@@ -1383,7 +1379,6 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 	touch(0);
 }
 
-/* Return or enter, during line input. Ends line input. */
 void TextBufferWindow::acceptLine(glui32 keycode) {
 	int ix;
 	int len, olen;
@@ -1445,8 +1440,7 @@ void TextBufferWindow::acceptLine(glui32 keycode) {
 		}
 	}
 
-	/* Store in event buffer. */
-
+	// Store in event buffer.
 	if (len > inmax)
 		len = inmax;
 
@@ -1495,7 +1489,7 @@ bool TextBufferWindow::leftquote(glui32 c) {
 	switch (c) {
 	case '(': case '[':
 
-		/* The following are Unicode characters in the "Separator, Space" category. */
+	// The following are Unicode characters in the "Separator, Space" category.
 	case 0x0020: case 0x00a0: case 0x1680: case 0x2000:
 	case 0x2001: case 0x2002: case 0x2003: case 0x2004:
 	case 0x2005: case 0x2006: case 0x2007: case 0x2008:
@@ -1572,10 +1566,9 @@ void TextBufferWindow::scrollResize() {
 	_chars = _lines[0]._chars;
 	_attrs = _lines[0]._attrs;
 
-	for (i = _scrollBack; i < (_scrollBack + SCROLLBACK); i++)
-	{
-		_lines[i]._dirty = 0;
-		_lines[i]._repaint = 0;
+	for (i = _scrollBack; i < (_scrollBack + SCROLLBACK); i++) {
+		_lines[i]._dirty = false;
+		_lines[i]._repaint = false;
 		_lines[i]._lm = 0;
 		_lines[i]._rm = 0;
 		_lines[i]._lPic = 0;
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index ab7890a..fdca1d8 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -66,6 +66,9 @@ private:
 	 */
 	void putTextUni(const glui32 *buf, int len, int pos, int oldlen);
 
+	/**
+	 * Return or enter, during line input. Ends line input.
+	 */
 	void acceptLine(glui32 keycode);
 
 	/**
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 05c45fa..283922c 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -97,10 +97,10 @@ void TextGridWindow::putCharUni(uint32 ch) {
 	if (_curY < 0)
 		_curY = 0;
 	else if (_curY >= _height)
-		return; /* outside the window */
+		return; // outside the window
 
 	if (ch == '\n') {
-		/* a newline just moves the cursor. */
+		// a newline just moves the cursor.
 		_curY++;
 		_curX = 0;
 		return;
@@ -121,14 +121,14 @@ bool TextGridWindow::unputCharUni(uint32 ch) {
 	TextGridRow *ln;
 	int oldx = _curX, oldy = _curY;
 
-	/* Move the cursor back. */
+	// Move the cursor back.
 	if (_curX >= _width)
 		_curX = _width - 1;
 	else
 		_curX--;
 
-	/* Canonicalize the cursor position. That is, the cursor may have been
-	left outside the window area; wrap it if necessary. */
+	// Canonicalize the cursor position. That is, the cursor may have been
+	// left outside the window area; wrap it if necessary.
 	if (_curX < 0) {
 		_curX = _width - 1;
 		_curY--;
@@ -144,7 +144,7 @@ bool TextGridWindow::unputCharUni(uint32 ch) {
 			return 1; // deleted a newline
 		_curX = oldx;
 		_curY = oldy;
-		return 0;    // it wasn't there */
+		return 0;    // it wasn't there
 	}
 
 	ln = &(_lines[_curY]);
@@ -481,8 +481,7 @@ void TextGridWindow::acceptReadLine(glui32 arg) {
 
 	switch (arg) {
 
-		/* Delete keys, during line input. */
-
+	// Delete keys, during line input.
 	case keycode_Delete:
 		if (_inLen <= 0)
 			return;
@@ -515,8 +514,7 @@ void TextGridWindow::acceptReadLine(glui32 arg) {
 		_inCurs = 0;
 		break;
 
-		/* Cursor movement keys, during line input. */
-
+	// Cursor movement keys, during line input.
 	case keycode_Left:
 		if (_inCurs <= 0)
 			return;
@@ -593,7 +591,7 @@ void TextGridWindow::redraw() {
 			x = x0;
 			y = y0 + i * g_conf->_leading;
 
-			/* clear any stored hyperlink coordinates */
+			// clear any stored hyperlink coordinates
 			g_vm->_windowMask->putHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
 
 			a = 0;


Commit: d61a7a9ee68a9c3373647fb94ad5fb5cba574f4c
    https://github.com/scummvm/scummvm/commit/d61a7a9ee68a9c3373647fb94ad5fb5cba574f4c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Change key handle if block to a switch statement

Changed paths:
    engines/gargoyle/events.cpp


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index ef4c59d..02c3d8d 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -160,32 +160,36 @@ void Events::handleKeyDown(const Common::KeyState &ks) {
 	if (ks.flags & Common::KBD_ALT)
 		return;
 
-	if (ks.keycode == Common::KEYCODE_RETURN) windows.inputHandleKey(keycode_Return);
-	else if (ks.keycode == Common::KEYCODE_BACKSPACE) windows.inputHandleKey(keycode_Delete);
-	else if (ks.keycode == Common::KEYCODE_DELETE) windows.inputHandleKey(keycode_Erase);
-	else if (ks.keycode == Common::KEYCODE_TAB) windows.inputHandleKey(keycode_Tab);
-	else if (ks.keycode == Common::KEYCODE_UP) windows.inputHandleKey(keycode_PageUp);
-	else if (ks.keycode == Common::KEYCODE_PAGEDOWN) windows.inputHandleKey(keycode_PageDown);
-	else if (ks.keycode == Common::KEYCODE_HOME) windows.inputHandleKey(keycode_Home);
-	else if (ks.keycode == Common::KEYCODE_END) windows.inputHandleKey(keycode_End);
-	else if (ks.keycode == Common::KEYCODE_LEFT) windows.inputHandleKey(keycode_Left);
-	else if (ks.keycode == Common::KEYCODE_RIGHT) windows.inputHandleKey(keycode_Right);
-	else if (ks.keycode == Common::KEYCODE_UP) windows.inputHandleKey(keycode_Up);
-	else if (ks.keycode == Common::KEYCODE_DOWN) windows.inputHandleKey(keycode_Down);
-	else if (ks.keycode == Common::KEYCODE_ESCAPE) windows.inputHandleKey(keycode_Escape);
-	else if (ks.keycode == Common::KEYCODE_F1) windows.inputHandleKey(keycode_Func1);
-	else if (ks.keycode == Common::KEYCODE_F2) windows.inputHandleKey(keycode_Func2);
-	else if (ks.keycode == Common::KEYCODE_F3) windows.inputHandleKey(keycode_Func3);
-	else if (ks.keycode == Common::KEYCODE_F4) windows.inputHandleKey(keycode_Func4);
-	else if (ks.keycode == Common::KEYCODE_F5) windows.inputHandleKey(keycode_Func5);
-	else if (ks.keycode == Common::KEYCODE_F6) windows.inputHandleKey(keycode_Func6);
-	else if (ks.keycode == Common::KEYCODE_F7) windows.inputHandleKey(keycode_Func7);
-	else if (ks.keycode == Common::KEYCODE_F8) windows.inputHandleKey(keycode_Func8);
-	else if (ks.keycode == Common::KEYCODE_F9) windows.inputHandleKey(keycode_Func9);
-	else if (ks.keycode == Common::KEYCODE_F10) windows.inputHandleKey(keycode_Func10);
-	else if (ks.keycode == Common::KEYCODE_F11) windows.inputHandleKey(keycode_Func11);
-	else if (ks.keycode == Common::KEYCODE_F12) windows.inputHandleKey(keycode_Func12);
-	else windows.inputHandleKey(ks.ascii);
+	switch (ks.keycode) {
+	case Common::KEYCODE_RETURN: windows.inputHandleKey(keycode_Return); break;
+	case Common::KEYCODE_BACKSPACE: windows.inputHandleKey(keycode_Delete); break;
+	case Common::KEYCODE_DELETE: windows.inputHandleKey(keycode_Erase); break;
+	case Common::KEYCODE_TAB: windows.inputHandleKey(keycode_Tab); break;
+	case Common::KEYCODE_PAGEUP: windows.inputHandleKey(keycode_PageUp); break;
+	case Common::KEYCODE_PAGEDOWN: windows.inputHandleKey(keycode_PageDown); break;
+	case Common::KEYCODE_HOME: windows.inputHandleKey(keycode_Home); break;
+	case Common::KEYCODE_END: windows.inputHandleKey(keycode_End); break;
+	case Common::KEYCODE_LEFT: windows.inputHandleKey(keycode_Left); break;
+	case Common::KEYCODE_RIGHT: windows.inputHandleKey(keycode_Right); break;
+	case Common::KEYCODE_UP: windows.inputHandleKey(keycode_Up); break;
+	case Common::KEYCODE_DOWN: windows.inputHandleKey(keycode_Down); break;
+	case Common::KEYCODE_ESCAPE: windows.inputHandleKey(keycode_Escape); break;
+	case Common::KEYCODE_F1: windows.inputHandleKey(keycode_Func1); break;
+	case Common::KEYCODE_F2: windows.inputHandleKey(keycode_Func2); break;
+	case Common::KEYCODE_F3: windows.inputHandleKey(keycode_Func3); break;
+	case Common::KEYCODE_F4: windows.inputHandleKey(keycode_Func4); break;
+	case Common::KEYCODE_F5: windows.inputHandleKey(keycode_Func5); break;
+	case Common::KEYCODE_F6: windows.inputHandleKey(keycode_Func6); break;
+	case Common::KEYCODE_F7: windows.inputHandleKey(keycode_Func7); break;
+	case Common::KEYCODE_F8: windows.inputHandleKey(keycode_Func8); break;
+	case Common::KEYCODE_F9: windows.inputHandleKey(keycode_Func9); break;
+	case Common::KEYCODE_F10: windows.inputHandleKey(keycode_Func10); break;
+	case Common::KEYCODE_F11: windows.inputHandleKey(keycode_Func11); break;
+	case Common::KEYCODE_F12: windows.inputHandleKey(keycode_Func12); break;
+	default:
+		windows.inputHandleKey(ks.ascii); break;
+	break;
+	}
 }
 
 void Events::handleScroll(bool wheelUp) {


Commit: 4229312b227dc62f721d7de3cdfdee8a6310f22f
    https://github.com/scummvm/scummvm/commit/4229312b227dc62f721d7de3cdfdee8a6310f22f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Cleanup of formatting in text buffer window class

Changed paths:
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 668fc3a..61cf92a 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -191,11 +191,9 @@ void TextBufferWindow::reflow() {
 	offsetbuf[x] = -1;
 
 	// clear window
-
 	clear();
 
 	// and dump text back
-
 	x = 0;
 	for (i = 0; i < p; i++) {
 		if (i == inputbyte)
@@ -242,8 +240,7 @@ void TextBufferWindow::touchScroll() {
 }
 
 bool TextBufferWindow::putPicture(Picture *pic, glui32 align, glui32 linkval) {
-	if (align == imagealign_MarginRight)
-	{
+	if (align == imagealign_MarginRight) {
 		if (_lines[0]._rPic || _numChars)
 			return false;
 
@@ -307,8 +304,7 @@ void TextBufferWindow::putText(const char *buf, int len, int pos, int oldlen) {
 	if (_numChars + diff >= TBLINELEN)
 		return;
 
-	if (diff != 0 && pos + oldlen < _numChars)
-	{
+	if (diff != 0 && pos + oldlen < _numChars) {
 		memmove(_chars + pos + len,
 			_chars + pos + oldlen,
 			(_numChars - (pos + oldlen)) * 4);
@@ -317,8 +313,7 @@ void TextBufferWindow::putText(const char *buf, int len, int pos, int oldlen) {
 			(_numChars - (pos + oldlen)) * sizeof(Attributes));
 	}
 	if (len > 0) {
-		int i;
-		for (i = 0; i < len; i++) {
+		for (int i = 0; i < len; i++) {
 			_chars[pos + i] = buf[i];
 			_attrs[pos + i].set(style_Input);
 		}
@@ -368,10 +363,11 @@ void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldle
 }
 
 void TextBufferWindow::touch(int line) {
-	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
 	_lines[line]._dirty = true;
 	g_vm->_windowMask->clearSelection();
-	_windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
+	//int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
+	//_windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
+	redraw();
 }
 
 glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {
@@ -468,8 +464,7 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 				_spaced = 1;
 			else if (ch == ' ' && _spaced == 1)
 				_spaced = 2;
-			else if (ch != ' ' && _spaced == 2)
-			{
+			else if (ch != ' ' && _spaced == 2) {
 				_spaced = 0;
 				putCharUni(' ');
 			} else {
@@ -492,11 +487,12 @@ void TextBufferWindow::putCharUni(glui32 ch) {
 	if (calcWidth(_chars, _attrs, 0, linelen, -1) >= pw) {
 		bpoint = _numChars;
 
-		for (i = _numChars - 1; i > 0; i--)
+		for (i = _numChars - 1; i > 0; i--) {
 			if (_chars[i] == ' ') {
 				bpoint = i + 1; // skip space
 				break;
 			}
+		}
 
 		saved = _numChars - bpoint;
 
@@ -747,8 +743,7 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 				ch = '?';
 			((char *)inbuf)[ix] = (char)ch;
 		}
-	}
-	else {
+	} else {
 		for (ix = 0; ix<len; ix++)
 			((glui32 *)inbuf)[ix] = _chars[_inFence + ix];
 	}
@@ -771,8 +766,7 @@ void TextBufferWindow::cancelLineEvent(Event *ev) {
 
 	if (_echoLineInput) {
 		putCharUni('\n');
-	}
-	else {
+	} else {
 		_numChars = _inFence;
 		touch(0);
 	}
@@ -1107,8 +1101,7 @@ void TextBufferWindow::redraw() {
     // try to claim scroll keys
     _scrollRequest = _scrollMax > _height;
 
-    if (_scrollRequest && g_conf->_scrollWidth)
-    {
+    if (_scrollRequest && g_conf->_scrollWidth) {
         int t0, t1;
         x0 = _bbox.right - g_conf->_scrollWidth;
         x1 = _bbox.right;
@@ -1155,7 +1148,7 @@ void TextBufferWindow::redraw() {
     if (!_moreRequest)
         _lastSeen = 0;
 
-    free(ln);
+    delete ln;
 }
 
 int TextBufferWindow::acceptScroll(glui32 arg) {
@@ -1225,6 +1218,7 @@ void TextBufferWindow::acceptReadChar(glui32 arg) {
 		return;
 	default:
 		key = arg;
+		break;
 	}
 
 	gli_tts_purge();
@@ -1246,10 +1240,7 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 	if (_height < 2)
 		_scrollPos = 0;
 
-	if (_scrollPos
-		|| arg == keycode_PageUp
-		|| arg == keycode_MouseWheelUp)
-	{
+	if (_scrollPos || arg == keycode_PageUp || arg == keycode_MouseWheelUp) {
 		acceptScroll(arg);
 		return;
 	}
@@ -1367,8 +1358,7 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 		break;
 
 	default:
-		if (arg >= 32 && arg <= 0x10FFFF)
-		{
+		if (arg >= 32 && arg <= 0x10FFFF) {
 			if (g_conf->_caps && (arg > 0x60 && arg < 0x7b))
 				arg -= 0x20;
 			putTextUni(&arg, 1, _inCurs, 0);


Commit: 4b5db1da07572eca7a69e40e32c4935921908b3c
    https://github.com/scummvm/scummvm/commit/4b5db1da07572eca7a69e40e32c4935921908b3c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix showing input text to the right of input prompt

Changed paths:
    engines/gargoyle/fonts.cpp


diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 4f298b0..e523ecb 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -113,7 +113,7 @@ int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const C
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
 
-	return font->getBoundingBox(text, pt.x, pt.y, _surface->w - pt.x).right;
+	return font->getBoundingBox(text, pt.x, pt.y, _surface->w - pt.x).right * GLI_SUBPIX;
 }
 
 size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {


Commit: d954f70d499c9cd96b2bedbfbec59bf7ed4f715a
    https://github.com/scummvm/scummvm/commit/d954f70d499c9cd96b2bedbfbec59bf7ed4f715a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix having space between prmpt and input text

Changed paths:
    engines/gargoyle/fonts.cpp


diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index e523ecb..8172c78 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -104,7 +104,9 @@ int Fonts::drawString(const Point &pos, int fontIdx, const byte *rgb, const Comm
 	Graphics::Font *font = _fontTable[fontIdx];
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
-	return font->getBoundingBox(text, pt.x, pt.y, _surface->w - pt.x).right;
+
+	pt.x += font->getStringWidth(text);
+	return MIN((int)pt.x, (int)_surface->w) * GLI_SUBPIX;
 }
 
 int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
@@ -113,7 +115,8 @@ int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const C
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
 
-	return font->getBoundingBox(text, pt.x, pt.y, _surface->w - pt.x).right * GLI_SUBPIX;
+	pt.x += font->getStringWidth(text);
+	return MIN((int)pt.x, (int)_surface->w) * GLI_SUBPIX;
 }
 
 size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {


Commit: 2551927bf68dfbd43997756821737ac41adc7dfb
    https://github.com/scummvm/scummvm/commit/2551927bf68dfbd43997756821737ac41adc7dfb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix processing input after Enter is pressed

Changed paths:
    engines/gargoyle/events.cpp


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 02c3d8d..93b9141 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -94,7 +94,7 @@ void Events::dispatchEvent(Event &ev, bool polled) {
 
 	if (!polled) {
 		dispatch = _eventsLogged.retrieve();
-		if (dispatch)
+		if (!dispatch)
 			dispatch = _eventsPolled.retrieve();
 	} else {
 		dispatch = _eventsPolled.retrieve();


Commit: e36dbfaf2e9d8d44502a7011717bcc97155212ad
    https://github.com/scummvm/scummvm/commit/e36dbfaf2e9d8d44502a7011717bcc97155212ad
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Prompt for keypress when game is exited

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/scott/scott.cpp
    engines/gargoyle/streams.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 93b9141..d5eccfe 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -226,4 +226,20 @@ void Events::handleButtonUp(bool isLeft, const Point &pos) {
 	}
 }
 
+void Events::waitForPress() {
+	Common::Event e;
+
+	do {
+		g_system->getEventManager()->pollEvent(e);
+		g_system->delayMillis(10);
+
+		if (checkForNextFrameCounter()) {
+			// Update the screen
+			g_vm->_screen->update();
+		}
+	} while (!g_vm->shouldQuit() && e.type != Common::EVENT_KEYDOWN &&
+		e.type != Common::EVENT_LBUTTONDOWN && e.type != Common::EVENT_RBUTTONDOWN &&
+		e.type != Common::EVENT_MBUTTONDOWN);
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 4d9e49b..3860dc2 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -210,6 +210,11 @@ public:
 	 * Store an event for retrieval
 	 */
 	void store(EvType type, Window *win, uint32 val1 = 0, uint32 val2 = 0);
+
+	/**
+	 * Wait for a keyboard or mouse press
+	 */
+	void waitForPress();
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index c9823a9..bda0fa7 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -59,6 +59,9 @@ Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 }
 
 void Glk::glk_exit(void) {
+	glk_put_string("[ press any key to exit ]");
+	_events->waitForPress();
+
 	quitGame();
 }
 
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 3812392..3a085ea 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -93,7 +93,7 @@ Distributed under the GNU software license\n\n");
 		if (GetInput(&vb, &no) == -1)
 			continue;
 		if (g_vm->shouldQuit())
-			return;
+			break;
 
 		switch (PerformActions(vb, no)) {
 		case -1:
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 3ebb98b..cdc2e7d 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -121,7 +121,6 @@ void WindowStream::putBuffer(const char *buf, size_t len) {
 		_window->putCharUni(*buf);
 	if (_window->_echoStream)
 		_window->_echoStream->putBuffer(buf, len);
-
 }
 
 void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index d753d53..20ce03d 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -98,11 +98,16 @@ private:
 	 */
 	PairWindow *newPairWindow(glui32 method, Window *key, glui32 size);
 
+	/**
+	 * Set the window focus
+	 */
 	void refocus(Window *win);
 
+	/**
+	 * Used to loop over windows in tree order
+	 */
 	Window *iterateTreeOrder(Window *win);
 
-
 	/**
 	 * Pick first window which has a more request
 	 */


Commit: 55c05a0d7ae6ee03f2be8ece75437bce9adb14f7
    https://github.com/scummvm/scummvm/commit/55c05a0d7ae6ee03f2be8ece75437bce9adb14f7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Camel case method names

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


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 3a085ea..ef25495 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -77,30 +77,30 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 		Top = Bottom;
 	}
 
-	Output("\
+	output("\
 Scott Free, A Scott Adams game driver in C.\n\
 Release 1.14, (c) 1993,1994,1995 Swansea University Computer Society.\n\
 Distributed under the GNU software license\n\n");
-	LoadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
+	loadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
 
 	while (!shouldQuit()) {
 		glk_tick();
 
-		PerformActions(0, 0);
+		performActions(0, 0);
 
-		Look();
+		look();
 
-		if (GetInput(&vb, &no) == -1)
+		if (getInput(&vb, &no) == -1)
 			continue;
 		if (g_vm->shouldQuit())
 			break;
 
-		switch (PerformActions(vb, no)) {
+		switch (performActions(vb, no)) {
 		case -1:
-			Output("I don't understand your command. ");
+			output("I don't understand your command. ");
 			break;
 		case -2:
-			Output("I can't do that yet. ");
+			output("I can't do that yet. ");
 			break;
 		default:
 			break;
@@ -114,9 +114,9 @@ Distributed under the GNU software license\n\n");
 				if (Items[LIGHT_SOURCE].Location == CARRIED ||
 					Items[LIGHT_SOURCE].Location == MyLoc) {
 					if (Options&SCOTTLIGHT)
-						Output("Light has run out! ");
+						output("Light has run out! ");
 					else
-						Output("Your light has run out. ");
+						output("Your light has run out. ");
 				}
 				if (Options&PREHISTORIC_LAMP)
 					Items[LIGHT_SOURCE].Location = DESTROYED;
@@ -125,12 +125,12 @@ Distributed under the GNU software license\n\n");
 					Items[LIGHT_SOURCE].Location == MyLoc) {
 
 					if (Options&SCOTTLIGHT) {
-						Output("Light runs out in ");
-						OutputNumber(GameHeader.LightTime);
-						Output(" turns. ");
+						output("Light runs out in ");
+						outputNumber(GameHeader.LightTime);
+						output(" turns. ");
 					} else {
 						if (GameHeader.LightTime % 5 == 0)
-							Output("Your light is growing dim. ");
+							output("Your light is growing dim. ");
 					}
 				}
 			}
@@ -196,7 +196,7 @@ void Scott::initialize() {
 	*/
 }
 
-void Scott::Display(winid_t w, const char *fmt, ...) {
+void Scott::display(winid_t w, const char *fmt, ...) {
 	va_list ap;
 
 	va_start(ap, fmt);
@@ -206,7 +206,7 @@ void Scott::Display(winid_t w, const char *fmt, ...) {
 	glk_put_string_stream(glk_window_get_stream(w), msg.c_str());
 }
 
-void Scott::Delay(int seconds) {
+void Scott::delay(int seconds) {
 	event_t ev;
 
 	if (!glk_gestalt(gestalt_Timer, 0))
@@ -221,26 +221,26 @@ void Scott::Delay(int seconds) {
 	glk_request_timer_events(0);
 }
 
-void Scott::Fatal(const char *x) {
+void Scott::fatal(const char *x) {
 	error(x);
 }
 
-void Scott::ClearScreen(void) {
+void Scott::clearScreen(void) {
 	glk_window_clear(Bottom);
 }
 
-void *Scott::MemAlloc(int size) {
+void *Scott::memAlloc(int size) {
 	void *t = (void *)malloc(size);
 	if (t == nullptr)
-		Fatal("Out of memory");
+		fatal("Out of memory");
 	return t;
 }
 
-bool Scott::RandomPercent(uint n) {
+bool Scott::randomPercent(uint n) {
 	return _random.getRandomNumber(99) < n;
 }
 
-int Scott::CountCarried(void) {
+int Scott::countCarried(void) {
 	int ct = 0;
 	int n = 0;
 	while (ct <= GameHeader.NumItems) {
@@ -251,7 +251,7 @@ int Scott::CountCarried(void) {
 	return n;
 }
 
-const char *Scott::MapSynonym(const char *word) {
+const char *Scott::mapSynonym(const char *word) {
 	int n = 1;
 	const char *tp;
 	static char lastword[16];	/* Last non synonym */
@@ -268,8 +268,8 @@ const char *Scott::MapSynonym(const char *word) {
 	return nullptr;
 }
 
-int Scott::MatchUpItem(const char *text, int loc) {
-	const char *word = MapSynonym(text);
+int Scott::matchUpItem(const char *text, int loc) {
+	const char *word = mapSynonym(text);
 	int ct = 0;
 
 	if (word == nullptr)
@@ -285,7 +285,7 @@ int Scott::MatchUpItem(const char *text, int loc) {
 	return -1;
 }
 
-char *Scott::ReadString(Common::SeekableReadStream *f) {
+char *Scott::readString(Common::SeekableReadStream *f) {
 	char tmp[1024];
 	char *t;
 	int c, nc;
@@ -294,12 +294,12 @@ char *Scott::ReadString(Common::SeekableReadStream *f) {
 		c = f->readByte();
 	} while (f->pos() < f->size() && Common::isSpace(c));
 	if (c != '"') {
-		Fatal("Initial quote expected");
+		fatal("Initial quote expected");
 	}
 
 	for (;;) {
 		if (f->pos() >= f->size())
-			Fatal("EOF in string");
+			fatal("EOF in string");
 
 		c = f->readByte();
 		if (c == '"') {
@@ -331,12 +331,12 @@ char *Scott::ReadString(Common::SeekableReadStream *f) {
 	}
 
 	tmp[ct] = 0;
-	t = (char *)MemAlloc(ct + 1);
+	t = (char *)memAlloc(ct + 1);
 	memcpy(t, tmp, ct + 1);
 	return t;
 }
 
-void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
+void Scott::loadDatabase(Common::SeekableReadStream *f, int loud) {
 	int unused, ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
 	int ct;
 	int lo;
@@ -348,22 +348,22 @@ void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
 	readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
 
 	GameHeader.NumItems = ni;
-	Items = (Item *)MemAlloc(sizeof(Item)*(ni + 1));
+	Items = (Item *)memAlloc(sizeof(Item)*(ni + 1));
 	GameHeader.NumActions = na;
-	Actions = (Action *)MemAlloc(sizeof(Action)*(na + 1));
+	Actions = (Action *)memAlloc(sizeof(Action)*(na + 1));
 	GameHeader.NumWords = nw;
 	GameHeader.WordLength = wl;
-	Verbs = (const char **)MemAlloc(sizeof(char *)*(nw + 1));
-	Nouns = (const char **)MemAlloc(sizeof(char *)*(nw + 1));
+	Verbs = (const char **)memAlloc(sizeof(char *)*(nw + 1));
+	Nouns = (const char **)memAlloc(sizeof(char *)*(nw + 1));
 	GameHeader.NumRooms = nr;
-	Rooms = (Room *)MemAlloc(sizeof(Room)*(nr + 1));
+	Rooms = (Room *)memAlloc(sizeof(Room)*(nr + 1));
 	GameHeader.MaxCarry = mc;
 	GameHeader.PlayerRoom = pr;
 	GameHeader.Treasures = tr;
 	GameHeader.LightTime = lt;
 	LightRefill = lt;
 	GameHeader.NumMessages = mn;
-	Messages = (const char **)MemAlloc(sizeof(char *)*(mn + 1));
+	Messages = (const char **)memAlloc(sizeof(char *)*(mn + 1));
 	GameHeader.TreasureRoom = trm;
 
 	/* Load the actions */
@@ -390,8 +390,8 @@ void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
 	if (loud)
 		debug("Reading %d word pairs.", nw);
 	while (ct<nw + 1) {
-		Verbs[ct] = ReadString(f);
-		Nouns[ct] = ReadString(f);
+		Verbs[ct] = readString(f);
+		Nouns[ct] = readString(f);
 		ct++;
 	}
 	ct = 0;
@@ -403,7 +403,7 @@ void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
 				&rp->Exits[0], &rp->Exits[1], &rp->Exits[2],
 				&rp->Exits[3], &rp->Exits[4], &rp->Exits[5]);
 	
-		rp->Text = ReadString(f);
+		rp->Text = readString(f);
 		ct++;
 		rp++;
 	}
@@ -412,7 +412,7 @@ void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
 	if (loud)
 		debug("Reading %d messages.", mn);
 	while (ct<mn + 1) {
-		Messages[ct] = ReadString(f);
+		Messages[ct] = readString(f);
 		ct++;
 	}
 
@@ -421,7 +421,7 @@ void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
 		debug("Reading %d items.", ni);
 	ip = Items;
 	while (ct < ni + 1) {
-		ip->Text = ReadString(f);
+		ip->Text = readString(f);
 		ip->AutoGet = strchr(ip->Text, '/');
 		/* Some games use // to mean no auto get/drop word! */
 		if (ip->AutoGet && strcmp(ip->AutoGet, "//") && strcmp(ip->AutoGet, "/*")) {
@@ -441,7 +441,7 @@ void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
 	ct = 0;
 	/* Discard Comment Strings */
 	while (ct<na + 1) {
-		free(ReadString(f));
+		free(readString(f));
 		ct++;
 	}
 
@@ -454,15 +454,15 @@ void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
 		debug("%d.\nLoad Complete.\n", ct);
 }
 
-void Scott::Output(const char *a) {
-	Display(Bottom, "%s", a);
+void Scott::output(const char *a) {
+	display(Bottom, "%s", a);
 }
 
-void Scott::OutputNumber(int a) {
-	Display(Bottom, "%d", a);
+void Scott::outputNumber(int a) {
+	display(Bottom, "%d", a);
 }
 
-void Scott::Look(void) {
+void Scott::look(void) {
 	static char *ExitNames[6] = { "North", "South", "East", "West", "Up", "Down" };
 	Room *r;
 	int ct, f;
@@ -474,41 +474,41 @@ void Scott::Look(void) {
 	if ((BitFlags&(1 << DARKBIT)) && Items[LIGHT_SOURCE].Location != CARRIED
 		&& Items[LIGHT_SOURCE].Location != MyLoc) {
 		if (Options&YOUARE)
-			Display(Top, "You can't see. It is too dark!\n");
+			display(Top, "You can't see. It is too dark!\n");
 		else
-			Display(Top, "I can't see. It is too dark!\n");
+			display(Top, "I can't see. It is too dark!\n");
 		if (Options & TRS80_STYLE)
-			Display(Top, TRS80_LINE);
+			display(Top, TRS80_LINE);
 		return;
 	}
 	r = &Rooms[MyLoc];
 	if (*r->Text == '*')
-		Display(Top, "%s\n", r->Text + 1);
+		display(Top, "%s\n", r->Text + 1);
 	else {
 		if (Options&YOUARE)
-			Display(Top, "You are in a %s\n", r->Text);
+			display(Top, "You are in a %s\n", r->Text);
 		else
-			Display(Top, "I'm in a %s\n", r->Text);
+			display(Top, "I'm in a %s\n", r->Text);
 	}
 
 	ct = 0;
 	f = 0;
-	Display(Top, "\nObvious exits: ");
+	display(Top, "\nObvious exits: ");
 	while (ct<6) {
 		if (r->Exits[ct] != 0)
 		{
 			if (f == 0)
 				f = 1;
 			else
-				Display(Top, ", ");
-			Display(Top, "%s", ExitNames[ct]);
+				display(Top, ", ");
+			display(Top, "%s", ExitNames[ct]);
 		}
 		ct++;
 	}
 
 	if (f == 0)
-		Display(Top, "none");
-	Display(Top, ".\n");
+		display(Top, "none");
+	display(Top, ".\n");
 	ct = 0;
 	f = 0;
 	pos = 0;
@@ -516,37 +516,37 @@ void Scott::Look(void) {
 		if (Items[ct].Location == MyLoc) {
 			if (f == 0) {
 				if (Options & YOUARE) {
-					Display(Top, "\nYou can also see: ");
+					display(Top, "\nYou can also see: ");
 					pos = 18;
 				} else {
-					Display(Top, "\nI can also see: ");
+					display(Top, "\nI can also see: ");
 					pos = 16;
 				}
 				f++;
 			} else if (!(Options & TRS80_STYLE)) {
-				Display(Top, " - ");
+				display(Top, " - ");
 				pos += 3;
 			}
 			if (pos + (int)strlen(Items[ct].Text) > (Width - 10)) {
 				pos = 0;
-				Display(Top, "\n");
+				display(Top, "\n");
 			}
-			Display(Top, "%s", Items[ct].Text);
+			display(Top, "%s", Items[ct].Text);
 			pos += strlen(Items[ct].Text);
 			if (Options & TRS80_STYLE) {
-				Display(Top, ". ");
+				display(Top, ". ");
 				pos += 2;
 			}
 		}
 		ct++;
 	}
 
-	Display(Top, "\n");
+	display(Top, "\n");
 	if (Options & TRS80_STYLE)
-		Display(Top, TRS80_LINE);
+		display(Top, TRS80_LINE);
 }
 
-int Scott::WhichWord(const char *word, const char **list) {
+int Scott::whichWord(const char *word, const char **list) {
 	int n = 1;
 	int ne = 1;
 	const char *tp;
@@ -563,7 +563,7 @@ int Scott::WhichWord(const char *word, const char **list) {
 	return -1;
 }
 
-void Scott::LineInput(char *buf, size_t n) {
+void Scott::lineInput(char *buf, size_t n) {
 	event_t ev;
 
 	glk_request_line_event(Bottom, buf, n - 1, 0);
@@ -575,13 +575,13 @@ void Scott::LineInput(char *buf, size_t n) {
 		else if (ev.type == evtype_LineInput)
 			break;
 		else if (ev.type == evtype_Arrange && split_screen)
-			Look();
+			look();
 	} while (ev.type != evtype_Quit);
 
 	buf[ev.val1] = 0;
 }
 
-void Scott::SaveGame(void) {
+void Scott::saveGame(void) {
 	strid_t file;
 	frefid_t ref;
 	int ct;
@@ -610,10 +610,10 @@ void Scott::SaveGame(void) {
 	}
 
 	glk_stream_close(file, nullptr);
-	Output("Saved.\n");
+	output("Saved.\n");
 }
 
-void Scott::LoadGame(void) {
+void Scott::loadGame(void) {
 	strid_t file;
 	frefid_t ref;
 	char buf[128];
@@ -648,7 +648,7 @@ void Scott::LoadGame(void) {
 	}
 }
 
-int Scott::GetInput(int *vb, int *no) {
+int Scott::getInput(int *vb, int *no) {
 	char buf[256];
 	char verb[10], noun[10];
 	int vc, nc;
@@ -656,8 +656,8 @@ int Scott::GetInput(int *vb, int *no) {
 
 	do {
 		do {
-			Output("\nTell me what to do ? ");
-			LineInput(buf, sizeof buf);
+			output("\nTell me what to do ? ");
+			lineInput(buf, sizeof buf);
 			if (g_vm->shouldQuit())
 				return 0;
 
@@ -665,7 +665,7 @@ int Scott::GetInput(int *vb, int *no) {
 		} while (num == 0 || *buf == '\n');
 		
 		if (xstrcasecmp(verb, "restore") == 0) {
-			LoadGame();
+			loadGame();
 			return -1;
 		}
 		if (num == 1)
@@ -682,18 +682,18 @@ int Scott::GetInput(int *vb, int *no) {
 			case 'i':strcpy(verb, "INVENTORY"); break;
 			}
 		}
-		nc = WhichWord(verb, Nouns);
+		nc = whichWord(verb, Nouns);
 		/* The Scott Adams system has a hack to avoid typing 'go' */
 		if (nc >= 1 && nc <= 6) {
 			vc = 1;
 		} else {
-			vc = WhichWord(verb, Verbs);
-			nc = WhichWord(noun, Nouns);
+			vc = whichWord(verb, Verbs);
+			nc = whichWord(noun, Nouns);
 		}
 		*vb = vc;
 		*no = nc;
 		if (vc == -1) {
-			Output("You use word(s) I don't know! ");
+			output("You use word(s) I don't know! ");
 		}
 	} while (vc == -1);
 
@@ -701,7 +701,7 @@ int Scott::GetInput(int *vb, int *no) {
 	return 0;
 }
 
-int Scott::PerformLine(int ct) {
+int Scott::performLine(int ct) {
 	int continuation = 0;
 	int param[5], pptr = 0;
 	int act[4];
@@ -754,11 +754,11 @@ int Scott::PerformLine(int ct) {
 				return 0;
 			break;
 		case 10:
-			if (CountCarried() == 0)
+			if (countCarried() == 0)
 				return 0;
 			break;
 		case 11:
-			if (CountCarried())
+			if (countCarried())
 				return 0;
 			break;
 		case 12:
@@ -808,23 +808,23 @@ int Scott::PerformLine(int ct) {
 	while (cc<4)
 	{
 		if (act[cc] >= 1 && act[cc] < 52) {
-			Output(Messages[act[cc]]);
-			Output("\n");
+			output(Messages[act[cc]]);
+			output("\n");
 		} else if (act[cc] > 101) {
-			Output(Messages[act[cc] - 50]);
-			Output("\n");
+			output(Messages[act[cc] - 50]);
+			output("\n");
 		}
 		else {
 			switch (act[cc]) {
 			case 0:/* NOP */
 				break;
 			case 52:
-				if (CountCarried() == GameHeader.MaxCarry)
+				if (countCarried() == GameHeader.MaxCarry)
 				{
 					if (Options&YOUARE)
-						Output("You are carrying too much. ");
+						output("You are carrying too much. ");
 					else
-						Output("I've too much to carry! ");
+						output("I've too much to carry! ");
 					break;
 				}
 				Items[param[pptr++]].Location = CARRIED;
@@ -855,9 +855,9 @@ int Scott::PerformLine(int ct) {
 				break;
 			case 61:
 				if (Options&YOUARE)
-					Output("You are dead.\n");
+					output("You are dead.\n");
 				else
-					Output("I am dead.\n");
+					output("I am dead.\n");
 				BitFlags &= ~(1 << DARKBIT);
 				MyLoc = GameHeader.NumRooms;/* It seems to be what the code says! */
 				break;
@@ -869,7 +869,7 @@ int Scott::PerformLine(int ct) {
 				break;
 			}
 			case 63:
-				doneit:				Output("The game is now over.\n");
+				doneit:				output("The game is now over.\n");
 									glk_exit();
 									break;
 			case 64:
@@ -886,16 +886,16 @@ int Scott::PerformLine(int ct) {
 					i++;
 				}
 				if (Options&YOUARE)
-					Output("You have stored ");
+					output("You have stored ");
 				else
-					Output("I've stored ");
-				OutputNumber(n);
-				Output(" treasures.  On a scale of 0 to 100, that rates ");
-				OutputNumber((n * 100) / GameHeader.Treasures);
-				Output(".\n");
+					output("I've stored ");
+				outputNumber(n);
+				output(" treasures.  On a scale of 0 to 100, that rates ");
+				outputNumber((n * 100) / GameHeader.Treasures);
+				output(".\n");
 				if (n == GameHeader.Treasures)
 				{
-					Output("Well done.\n");
+					output("Well done.\n");
 					goto doneit;
 				}
 				break;
@@ -905,9 +905,9 @@ int Scott::PerformLine(int ct) {
 				int i = 0;
 				int f = 0;
 				if (Options&YOUARE)
-					Output("You are carrying:\n");
+					output("You are carrying:\n");
 				else
-					Output("I'm carrying:\n");
+					output("I'm carrying:\n");
 				while (i <= GameHeader.NumItems)
 				{
 					if (Items[i].Location == CARRIED)
@@ -915,18 +915,18 @@ int Scott::PerformLine(int ct) {
 						if (f == 1)
 						{
 							if (Options & TRS80_STYLE)
-								Output(". ");
+								output(". ");
 							else
-								Output(" - ");
+								output(" - ");
 						}
 						f = 1;
-						Output(Items[i].Text);
+						output(Items[i].Text);
 					}
 					i++;
 				}
 				if (f == 0)
-					Output("Nothing");
-				Output(".\n");
+					output("Nothing");
+				output(".\n");
 				break;
 			}
 			case 67:
@@ -941,10 +941,10 @@ int Scott::PerformLine(int ct) {
 				BitFlags &= ~(1 << LIGHTOUTBIT);
 				break;
 			case 70:
-				ClearScreen(); /* pdd. */
+				clearScreen(); /* pdd. */
 				break;
 			case 71:
-				SaveGame();
+				saveGame();
 				break;
 			case 72:
 			{
@@ -976,7 +976,7 @@ int Scott::PerformLine(int ct) {
 					CurrentCounter--;
 				break;
 			case 78:
-				OutputNumber(CurrentCounter);
+				outputNumber(CurrentCounter);
 				break;
 			case 79:
 				CurrentCounter = param[pptr++];
@@ -1011,14 +1011,14 @@ int Scott::PerformLine(int ct) {
 				know if there is a maximum value to limit too */
 				break;
 			case 84:
-				Output(NounText);
+				output(NounText);
 				break;
 			case 85:
-				Output(NounText);
-				Output("\n");
+				output(NounText);
+				output("\n");
 				break;
 			case 86:
-				Output("\n");
+				output("\n");
 				break;
 			case 87:
 			{
@@ -1031,7 +1031,7 @@ int Scott::PerformLine(int ct) {
 				break;
 			}
 			case 88:
-				Delay(2);
+				delay(2);
 				break;
 			case 89:
 				pptr++;
@@ -1052,7 +1052,7 @@ int Scott::PerformLine(int ct) {
 	return 1 + continuation;
 }
 
-int Scott::PerformActions(int vb, int no) {
+int Scott::performActions(int vb, int no) {
 	static int disable_sysfunc = 0;	/* Recursion lock */
 	int d = BitFlags&(1 << DARKBIT);
 
@@ -1060,7 +1060,7 @@ int Scott::PerformActions(int vb, int no) {
 	int fl;
 	int doagain = 0;
 	if (vb == 1 && no == -1) {
-		Output("Give me a direction too.");
+		output("Give me a direction too.");
 		return 0;
 	}
 	if (vb == 1 && no >= 1 && no <= 6) {
@@ -1069,7 +1069,7 @@ int Scott::PerformActions(int vb, int no) {
 			Items[LIGHT_SOURCE].Location == CARRIED)
 			d = 0;
 		if (d)
-			Output("Dangerous to move in the dark! ");
+			output("Dangerous to move in the dark! ");
 		nl = Rooms[MyLoc].Exits[no - 1];
 		if (nl != 0) {
 			MyLoc = nl;
@@ -1077,15 +1077,15 @@ int Scott::PerformActions(int vb, int no) {
 		}
 		if (d) {
 			if (Options&YOUARE)
-				Output("You fell down and broke your neck. ");
+				output("You fell down and broke your neck. ");
 			else
-				Output("I fell down and broke my neck. ");
+				output("I fell down and broke my neck. ");
 			glk_exit();
 		}
 		if (Options&YOUARE)
-			Output("You can't go in that direction. ");
+			output("You can't go in that direction. ");
 		else
-			Output("I can't go in that direction. ");
+			output("I can't go in that direction. ");
 		return 0;
 	}
 
@@ -1103,12 +1103,12 @@ int Scott::PerformActions(int vb, int no) {
 		nv = vv % 150;
 		vv /= 150;
 		if ((vv == vb) || (doagain&&Actions[ct].Vocab == 0)) {
-			if ((vv == 0 && RandomPercent(nv)) || doagain ||
+			if ((vv == 0 && randomPercent(nv)) || doagain ||
 					(vv != 0 && (nv == no || nv == 0))) {
 				int f2;
 				if (fl == -1)
 					fl = -2;
-				if ((f2 = PerformLine(ct)) > 0) {
+				if ((f2 = performLine(ct)) > 0) {
 					/* ahah finally figured it out ! */
 					fl = 0;
 					if (f2 == 2)
@@ -1143,57 +1143,57 @@ int Scott::PerformActions(int vb, int no) {
 					int f = 0;
 
 					if (d) {
-						Output("It is dark.\n");
+						output("It is dark.\n");
 						return 0;
 					}
 					while (i <= GameHeader.NumItems) {
 						if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
-							no = WhichWord(Items[i].AutoGet, Nouns);
+							no = whichWord(Items[i].AutoGet, Nouns);
 							disable_sysfunc = 1;	/* Don't recurse into auto get ! */
-							PerformActions(vb, no);	/* Recursively check each items table code */
+							performActions(vb, no);	/* Recursively check each items table code */
 							disable_sysfunc = 0;
-							if (CountCarried() == GameHeader.MaxCarry) {
+							if (countCarried() == GameHeader.MaxCarry) {
 								if (Options&YOUARE)
-									Output("You are carrying too much. ");
+									output("You are carrying too much. ");
 								else
-									Output("I've too much to carry. ");
+									output("I've too much to carry. ");
 								return 0;
 							}
 							Items[i].Location = CARRIED;
-							Output(Items[i].Text);
-							Output(": O.K.\n");
+							output(Items[i].Text);
+							output(": O.K.\n");
 							f = 1;
 						}
 						i++;
 					}
 					if (f == 0)
-						Output("Nothing taken.");
+						output("Nothing taken.");
 					return 0;
 				}
 				if (no == -1)
 				{
-					Output("What ? ");
+					output("What ? ");
 					return 0;
 				}
-				if (CountCarried() == GameHeader.MaxCarry)
+				if (countCarried() == GameHeader.MaxCarry)
 				{
 					if (Options&YOUARE)
-						Output("You are carrying too much. ");
+						output("You are carrying too much. ");
 					else
-						Output("I've too much to carry. ");
+						output("I've too much to carry. ");
 					return 0;
 				}
-				item = MatchUpItem(NounText, MyLoc);
+				item = matchUpItem(NounText, MyLoc);
 				if (item == -1)
 				{
 					if (Options&YOUARE)
-						Output("It is beyond your power to do that. ");
+						output("It is beyond your power to do that. ");
 					else
-						Output("It's beyond my power to do that. ");
+						output("It's beyond my power to do that. ");
 					return 0;
 				}
 				Items[item].Location = CARRIED;
-				Output("O.K. ");
+				output("O.K. ");
 				return 0;
 			}
 			if (vb == 18) {
@@ -1202,35 +1202,35 @@ int Scott::PerformActions(int vb, int no) {
 					int f = 0;
 					while (i <= GameHeader.NumItems) {
 						if (Items[i].Location == CARRIED && Items[i].AutoGet && Items[i].AutoGet[0] != '*') {
-							no = WhichWord(Items[i].AutoGet, Nouns);
+							no = whichWord(Items[i].AutoGet, Nouns);
 							disable_sysfunc = 1;
-							PerformActions(vb, no);
+							performActions(vb, no);
 							disable_sysfunc = 0;
 							Items[i].Location = MyLoc;
-							Output(Items[i].Text);
-							Output(": O.K.\n");
+							output(Items[i].Text);
+							output(": O.K.\n");
 							f = 1;
 						}
 						i++;
 					}
 					if (f == 0)
-						Output("Nothing dropped.\n");
+						output("Nothing dropped.\n");
 					return 0;
 				}
 				if (no == -1) {
-					Output("What ? ");
+					output("What ? ");
 					return 0;
 				}
-				item = MatchUpItem(NounText, CARRIED);
+				item = matchUpItem(NounText, CARRIED);
 				if (item == -1) {
 					if (Options&YOUARE)
-						Output("It's beyond your power to do that.\n");
+						output("It's beyond your power to do that.\n");
 					else
-						Output("It's beyond my power to do that.\n");
+						output("It's beyond my power to do that.\n");
 					return 0;
 				}
 				Items[item].Location = MyLoc;
-				Output("O.K. ");
+				output("O.K. ");
 				return 0;
 			}
 		}
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
index 52ad560..35a84fa 100644
--- a/engines/gargoyle/scott/scott.h
+++ b/engines/gargoyle/scott/scott.h
@@ -39,11 +39,13 @@ namespace Scott {
 #define DARKBIT		15
 #define LIGHTOUTBIT	16		// Light gone out
 
-#define YOUARE		1		// You are not I am
-#define SCOTTLIGHT	2		// Authentic Scott Adams light messages
-#define DEBUGGING	4		// Info from database load
-#define TRS80_STYLE	8		// Display in style used on TRS-80
-#define PREHISTORIC_LAMP 16	// Destroy the lamp (very old databases)
+enum GameOption {
+	YOUARE      = 1,		///< You are not I am
+	SCOTTLIGHT  = 2,		///< Authentic Scott Adams light messages
+	DEBUGGING   = 4,		///< Info from database load
+	TRS80_STYLE = 8,		///< Display in style used on TRS-80
+	PREHISTORIC_LAMP = 16	///< Destroy the lamp (very old databases)
+};
 
 #define TRS80_LINE	"\n<------------------------------------------------------------>\n"
 #define MyLoc	(GameHeader.PlayerRoom)
@@ -52,7 +54,7 @@ struct Header {
  	int Unknown;
 	int NumItems;
 	int NumActions;
-	int NumWords;			// Smaller of verb/noun is padded to same size
+	int NumWords;			///< Smaller of verb/noun is padded to same size
 	int NumRooms;
 	int MaxCarry;
 	int PlayerRoom;
@@ -135,27 +137,27 @@ private:
 	 */
 	void initialize();
 
-	void Display(winid_t w, const char *fmt, ...);
-	void Delay(int seconds);
-	void Fatal(const char *x);
-	void ClearScreen(void);
-	void *MemAlloc(int size);
-	bool RandomPercent(uint n);
-	int CountCarried(void);
-	const char *MapSynonym(const char *word);
-	int MatchUpItem(const char *text, int loc);
-	char *ReadString(Common::SeekableReadStream *f);
-	void LoadDatabase(Common::SeekableReadStream *f, int loud);
-	void Output(const char *a);
-	void OutputNumber(int a);
-	void Look(void);
-	int WhichWord(const char *word, const char **list);
-	void LineInput(char *buf, size_t n);
-	void SaveGame(void);
-	void LoadGame(void);
-	int GetInput(int *vb, int *no);
-	int PerformLine(int ct);
-	int PerformActions(int vb, int no);
+	void display(winid_t w, const char *fmt, ...);
+	void delay(int seconds);
+	void fatal(const char *x);
+	void clearScreen(void);
+	void *memAlloc(int size);
+	bool randomPercent(uint n);
+	int countCarried(void);
+	const char *mapSynonym(const char *word);
+	int matchUpItem(const char *text, int loc);
+	char *readString(Common::SeekableReadStream *f);
+	void loadDatabase(Common::SeekableReadStream *f, int loud);
+	void output(const char *a);
+	void outputNumber(int a);
+	void look(void);
+	int whichWord(const char *word, const char **list);
+	void lineInput(char *buf, size_t n);
+	void saveGame(void);
+	void loadGame(void);
+	int getInput(int *vb, int *no);
+	int performLine(int ct);
+	int performActions(int vb, int no);
 
 	int xstrcasecmp(const char *, const char *);
 	int xstrncasecmp(const char *, const char *, size_t);


Commit: aafaace942f6291682d4d2fb2d0af944844bb906
    https://github.com/scummvm/scummvm/commit/aafaace942f6291682d4d2fb2d0af944844bb906
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Switch command line options setup to use ConfigMan

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


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index ef25495..a19fb00 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -21,26 +21,11 @@
  */
 
 #include "gargoyle/scott/scott.h"
+#include "common/config-manager.h"
 
 namespace Gargoyle {
 namespace Scott {
 
-/*
-glkunix_argumentlist_t glkunix_arguments[] =
-{
-	{ "-y",		glkunix_arg_NoValue,		"-y		Generate 'You are', 'You are carrying' type messages for games that use these instead (eg Robin Of Sherwood)" },
-	{ "-i",		glkunix_arg_NoValue,		"-i		Generate 'I am' type messages (default)" },
-	{ "-d",		glkunix_arg_NoValue,		"-d		Debugging info on load " },
-	{ "-s",		glkunix_arg_NoValue,		"-s		Generate authentic Scott Adams driver light messages rather than other driver style ones (Light goes out in n turns..)" },
-	{ "-t",		glkunix_arg_NoValue,		"-t		Generate TRS80 style display (terminal width is 64 characters; a line <-----------------> is displayed after the top stuff; objects have periods after them instead of hyphens" },
-	{ "-p",		glkunix_arg_NoValue,		"-p		Use for prehistoric databases which don't use bit 16" },
-	{ "-w",		glkunix_arg_NoValue,		"-w		Disable upper window" },
-	{ "",		glkunix_arg_ValueFollows,	"filename	file to load" },
-
-	{ nullptr, glkunix_arg_End, nullptr }
-};
-*/
-
 Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
 		Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
 		Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
@@ -139,61 +124,20 @@ Distributed under the GNU software license\n\n");
 }
 
 void Scott::initialize() {
-	/*
-	int argc = data->argc;
-	char **argv = data->argv;
-
-	if (argc < 1)
-		return 0;
-
-	while (argv[1])
-	{
-		if (*argv[1] != '-')
-			break;
-		switch (argv[1][1])
-		{
-		case 'y':
+	if (ConfMan.hasKey("YOUARE")) {
+		if (ConfMan.getBool("YOUARE"))
 			Options |= YOUARE;
-			break;
-		case 'i':
-			Options &= ~YOUARE;
-			break;
-		case 'd':
-			Options |= DEBUGGING;
-			break;
-		case 's':
-			Options |= SCOTTLIGHT;
-			break;
-		case 't':
-			Options |= TRS80_STYLE;
-			break;
-		case 'p':
-			Options |= PREHISTORIC_LAMP;
-			break;
-		case 'w':
-			split_screen = 0;
-			break;
-		}
-		argv++;
-		argc--;
-	}
-
-	if (argc == 2)
-	{
-		game_file = argv[1];
-#ifdef GARGLK
-		const char *s;
-		if ((s = strrchr(game_file, '/')) != nullptr || (s = strrchr(game_file, '\\')) != nullptr)
-		{
-			garglk_set_story_name(s + 1);
-		}
 		else
-		{
-			garglk_set_story_name(game_file);
-		}
-#endif
+			Options &= ~YOUARE;
 	}
-	*/
+	if (gDebugLevel > 0)
+		Options |= DEBUGGING;
+	if (ConfMan.hasKey("SCOTTLIGHT") && ConfMan.getBool("SCOTTLIGHT"))
+		Options |= SCOTTLIGHT;
+	if (ConfMan.hasKey("TRS80_STYLE") && ConfMan.getBool("TRS80_STYLE"))
+		Options |= TRS80_STYLE;
+	if (ConfMan.hasKey("PREHISTORIC_LAMP") && ConfMan.getBool("PREHISTORIC_LAMP"))
+		Options |= PREHISTORIC_LAMP;
 }
 
 void Scott::display(winid_t w, const char *fmt, ...) {
@@ -336,7 +280,7 @@ char *Scott::readString(Common::SeekableReadStream *f) {
 	return t;
 }
 
-void Scott::loadDatabase(Common::SeekableReadStream *f, int loud) {
+void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	int unused, ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
 	int ct;
 	int lo;
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
index 35a84fa..f2fe769 100644
--- a/engines/gargoyle/scott/scott.h
+++ b/engines/gargoyle/scott/scott.h
@@ -147,7 +147,7 @@ private:
 	const char *mapSynonym(const char *word);
 	int matchUpItem(const char *text, int loc);
 	char *readString(Common::SeekableReadStream *f);
-	void loadDatabase(Common::SeekableReadStream *f, int loud);
+	void loadDatabase(Common::SeekableReadStream *f, bool loud);
 	void output(const char *a);
 	void outputNumber(int a);
 	void look(void);


Commit: 99266b85868beaf1baa37ddc9764ae289c315881
    https://github.com/scummvm/scummvm/commit/99266b85868beaf1baa37ddc9764ae289c315881
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Beginnings of file reference handling

Changed paths:
  A engines/gargoyle/files.cpp
  A engines/gargoyle/files.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/module.mk
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/files.cpp b/engines/gargoyle/files.cpp
new file mode 100644
index 0000000..c3f1367
--- /dev/null
+++ b/engines/gargoyle/files.cpp
@@ -0,0 +1,97 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 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 "gargoyle/files.h"
+#include "gui/saveload.h"
+#include "common/translation.h"
+
+namespace Gargoyle {
+
+frefid_t Files::prompt(glui32 usage, FileMode fmode, glui32 rock) {
+	switch (usage & fileusage_TypeMask) {
+	case fileusage_SavedGame: {
+		if (fmode == filemode_Write) {
+			// Select a savegame slot
+			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+
+			int slot = dialog->runModalWithCurrentTarget();
+			if (slot >= 0) {
+				Common::String desc = dialog->getResultString();
+				return createRef(slot, desc, usage, rock);
+			}
+		} else if (fmode == filemode_Read) {
+			// Load a savegame slot
+			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+
+			int slot = dialog->runModalWithCurrentTarget();
+			if (slot >= 0) {
+				return createRef(slot, "", usage, rock);
+			}
+		} else {
+			error("Unsupport file mode");
+		}
+		break;
+	}
+
+	case fileusage_Transcript:
+		return createRef("transcript.txt", fmode, rock);
+
+	default:
+		error("Unsupport file mode");
+		break;
+	}
+
+	return nullptr;
+}
+
+frefid_t Files::createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock) {
+	_fileReferences.push_back(FileReference());
+	frefid_t fref = &_fileReferences.back();
+
+	fref->_slotNumber = slot;
+	fref->_description = desc;
+	fref->_textMode = ((usage & fileusage_TextMode) != 0);
+	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
+	return fref;
+}
+
+frefid_t Files::createRef(const Common::String &filename, glui32 usage, glui32 rock) {
+	_fileReferences.push_back(FileReference());
+	frefid_t fref = &_fileReferences.back();
+
+	fref->_filename = filename;
+	fref->_textMode = ((usage & fileusage_TextMode) != 0);
+	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
+	return fref;
+}
+
+void Files::deleteRef(frefid_t fref) {
+	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
+		if (&_fileReferences[idx] == fref) {
+			_fileReferences.remove_at(idx);
+			return;
+		}
+	}
+}
+
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/files.h b/engines/gargoyle/files.h
new file mode 100644
index 0000000..cfd757b
--- /dev/null
+++ b/engines/gargoyle/files.h
@@ -0,0 +1,98 @@
+/* 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 GARGOYLE_FILES_H
+#define GARGOYLE_FILES_H
+
+#include "gargoyle/glk_types.h"
+#include "common/array.h"
+#include "common/str.h"
+
+namespace Gargoyle {
+
+enum FileUsage {
+	fileusage_Data        = 0x00,
+	fileusage_SavedGame   = 0x01,
+	fileusage_Transcript  = 0x02,
+	fileusage_InputRecord = 0x03,
+	fileusage_TypeMask    = 0x0f,
+
+	fileusage_TextMode    = 0x100,
+	fileusage_BinaryMode  = 0x000,
+};
+
+enum FileMode {
+	filemode_Write       = 0x01,
+	filemode_Read        = 0x02,
+	filemode_ReadWrite   = 0x03,
+	filemode_WriteAppend = 0x05,
+};
+
+enum SeekMode {
+	seekmode_Start   = 0,
+	seekmode_Current = 1,
+	seekmode_End     = 2,
+};
+
+
+/**
+ * File details
+ */
+struct FileReference {
+	glui32 _rock;
+	int _slotNumber;
+	Common::String _description;
+	Common::String _filename;
+	FileUsage _fileType;
+	bool _textMode;
+	gidispatch_rock_t _dispRock;
+};
+typedef FileReference *frefid_t;
+
+class Files {
+private:
+	Common::Array<FileReference> _fileReferences;
+public:
+	/**
+	 * Prompt for a file
+	 */
+	frefid_t prompt(glui32 usage, FileMode fmode, glui32 rock);
+
+	/**
+	 * Create a new file reference
+	 */
+	frefid_t createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock);
+
+	/**
+	 * Create a new file reference
+	 */
+	frefid_t createRef(const Common::String &filename, glui32 usage, glui32 rock);
+
+	/**
+	 * Delete a file reference
+	 */
+	void deleteRef(frefid_t fref);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 3135fcb..503cf1c 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -32,6 +32,7 @@
 #include "gargoyle/clipboard.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/events.h"
+#include "gargoyle/files.h"
 #include "gargoyle/picture.h"
 #include "gargoyle/screen.h"
 #include "gargoyle/streams.h"
@@ -54,6 +55,7 @@ GargoyleEngine::~GargoyleEngine() {
 	delete _clipboard;
 	delete _conf;
 	delete _events;
+	delete _files;
 	delete _picList;
 	delete _screen;
 	delete _streams;
@@ -74,6 +76,7 @@ void GargoyleEngine::initialize() {
 
 	_clipboard = new Clipboard();
 	_events = new Events();
+	_files = new Files();
 	_picList = new PicList();
 	_streams = new Streams();
 	_windows = new Windows(_screen);
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 9677d67..3538b4c 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -36,6 +36,7 @@ namespace Gargoyle {
 class Clipboard;
 class Conf;
 class Events;
+class Files;
 class PicList;
 class Screen;
 class Streams;
@@ -97,6 +98,7 @@ public:
 	Clipboard *_clipboard;
 	Conf *_conf;
 	Events *_events;
+	Files *_files;
 	PicList *_picList;
 	Screen *_screen;
 	Streams *_streams;
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index bda0fa7..2f9f57e 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -479,7 +479,7 @@ frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui3
 }
 
 void Glk::glk_fileref_destroy(frefid_t fref) {
-	// TODO
+	_files->deleteRef(fref);
 }
 
 frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 6bfffd6..0124cb1 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -24,6 +24,7 @@
 #define GARGOYLE_GLK_H
 
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/files.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/time.h"
 #include "gargoyle/windows.h"
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 274443a..6ea4408 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -146,30 +146,6 @@ enum WinMethod {
 	winmethod_BorderMask = 0x100,
 };
 
-enum FileUsage {
-	fileusage_Data        = 0x00,
-	fileusage_SavedGame   = 0x01,
-	fileusage_Transcript  = 0x02,
-	fileusage_InputRecord = 0x03,
-	fileusage_TypeMask    = 0x0f,
-
-	fileusage_TextMode    = 0x100,
-	fileusage_BinaryMode  = 0x000,
-};
-
-enum FileMode {
-	filemode_Write       = 0x01,
-	filemode_Read        = 0x02,
-	filemode_ReadWrite   = 0x03,
-	filemode_WriteAppend = 0x05,
-};
-
-enum SeekMode {
-	seekmode_Start   = 0,
-	seekmode_Current = 1,
-	seekmode_End     = 2,
-};
-
 enum StyleHint {
 	stylehint_Indentation     = 0,
 	stylehint_ParaIndentation = 1,
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index f4422b0..306ac8b 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS := \
 	conf.o \
 	detection.o \
 	events.o \
+	files.o \
 	fonts.o \
 	gargoyle.o \
 	glk.o \
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index de8b704..08d97cb 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "common/savefile.h"
+#include "gargoyle/files.h"
 #include "gargoyle/glk_types.h"
 
 namespace Gargoyle {
@@ -32,15 +33,6 @@ namespace Gargoyle {
 class Window;
 class Streams;
 
-struct FileReference {
-	glui32 _rock;
-	Common::String _filename;
-	int _fileType;
-	int _textMode;
-	gidispatch_rock_t _dispRock;
-};
-typedef FileReference *frefid_t;
-
 struct StreamResult {
 	uint32 _readCount;
 	uint32 _writeCount;


Commit: a24b27b14ac72f5f88d9dce57f79f5b9039fe482
    https://github.com/scummvm/scummvm/commit/a24b27b14ac72f5f88d9dce57f79f5b9039fe482
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Create glk fileref methods

Changed paths:
    engines/gargoyle/files.cpp
    engines/gargoyle/files.h
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h


diff --git a/engines/gargoyle/files.cpp b/engines/gargoyle/files.cpp
index c3f1367..e14a315 100644
--- a/engines/gargoyle/files.cpp
+++ b/engines/gargoyle/files.cpp
@@ -21,12 +21,15 @@
  */
 
 #include "gargoyle/files.h"
+#include "gargoyle/gargoyle.h"
 #include "gui/saveload.h"
+#include "common/file.h"
+#include "common/savefile.h"
 #include "common/translation.h"
 
 namespace Gargoyle {
 
-frefid_t Files::prompt(glui32 usage, FileMode fmode, glui32 rock) {
+frefid_t Files::createByPrompt(glui32 usage, FileMode fmode, glui32 rock) {
 	switch (usage & fileusage_TypeMask) {
 	case fileusage_SavedGame: {
 		if (fmode == filemode_Write) {
@@ -64,34 +67,94 @@ frefid_t Files::prompt(glui32 usage, FileMode fmode, glui32 rock) {
 }
 
 frefid_t Files::createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock) {
-	_fileReferences.push_back(FileReference());
-	frefid_t fref = &_fileReferences.back();
-
+	frefid_t fref = new FileReference();
 	fref->_slotNumber = slot;
 	fref->_description = desc;
 	fref->_textMode = ((usage & fileusage_TextMode) != 0);
 	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
+
+	_fileReferences.push_back(FileRefArray::value_type(fref));
 	return fref;
 }
 
 frefid_t Files::createRef(const Common::String &filename, glui32 usage, glui32 rock) {
-	_fileReferences.push_back(FileReference());
-	frefid_t fref = &_fileReferences.back();
-
+	frefid_t fref = new FileReference();
 	fref->_filename = filename;
 	fref->_textMode = ((usage & fileusage_TextMode) != 0);
 	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
+
+	_fileReferences.push_back(FileRefArray::value_type(fref));
 	return fref;
 }
 
+frefid_t Files::createTemp(glui32 usage, glui32 rock) {
+	return createRef(Common::String::format("%s.tmp", g_vm->getTargetName().c_str()),
+		usage, rock);
+}
+
+frefid_t Files::createFromRef(frefid_t fref, glui32 usage, glui32 rock) {
+	return createRef(fref->_filename, usage, rock);
+}
+
 void Files::deleteRef(frefid_t fref) {
 	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
-		if (&_fileReferences[idx] == fref) {
+		if (_fileReferences[idx].get() == fref) {
 			_fileReferences.remove_at(idx);
 			return;
 		}
 	}
 }
 
+frefid_t Files::iterate(frefid_t fref, glui32 *rock) {
+	// Find reference following the specified one
+	int index = -1;
+	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
+		if (fref == nullptr || _fileReferences[idx].get() == fref) {
+			if (idx < (_fileReferences.size() - 1))
+				index = idx + 1;
+			break;
+		}
+	}
+
+	if (index != -1) {
+		if (rock)
+			*rock = _fileReferences[index].get()->_rock;
+		return _fileReferences[index].get();
+	}
+
+	if (rock)
+		*rock = 0;
+	return nullptr;
+}
+
+/*--------------------------------------------------------------------------*/
+
+const Common::String FileReference::getSaveName() const {
+	assert(_slotNumber != -1);
+	return Common::String::format("%s.%.3d", g_vm->getTargetName().c_str(), _slotNumber);
+}
+
+bool FileReference::exists() const {
+	Common::String filename;
+
+	if (_slotNumber == -1) {
+		if (Common::File::exists(_filename))
+			return true;
+		filename = _filename;
+	} else {
+		filename = getSaveName();
+	}
+
+	// Check for a savegame (or other file in the save folder) with that name
+	Common::InSaveFile *inSave = g_system->getSavefileManager()->openForLoading(filename);
+	bool result = inSave != nullptr;
+	delete inSave;
+	return result;
+}
+
+void FileReference::deleteFile() {
+	Common::String filename = (_slotNumber == -1) ? _filename : getSaveName();
+	g_system->getSavefileManager()->removeSavefile(filename);
+}
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/files.h b/engines/gargoyle/files.h
index cfd757b..7e7ebfb 100644
--- a/engines/gargoyle/files.h
+++ b/engines/gargoyle/files.h
@@ -25,6 +25,7 @@
 
 #include "gargoyle/glk_types.h"
 #include "common/array.h"
+#include "common/ptr.h"
 #include "common/str.h"
 
 namespace Gargoyle {
@@ -65,17 +66,39 @@ struct FileReference {
 	FileUsage _fileType;
 	bool _textMode;
 	gidispatch_rock_t _dispRock;
+
+	/**
+	 * Constructor
+	 */
+	FileReference() : _rock(0), _slotNumber(-1), _fileType(fileusage_Data), _textMode(false) {}
+
+	/**
+	 * Get savegame filename
+	 */
+	const Common::String getSaveName() const;
+
+	/**
+	 * Returns true if the given file exists
+	 */
+	bool exists() const;
+
+	/**
+	 * Delete the given file
+	 */
+	void deleteFile();
 };
+
 typedef FileReference *frefid_t;
+typedef Common::Array< Common::SharedPtr<FileReference> > FileRefArray;
 
 class Files {
 private:
-	Common::Array<FileReference> _fileReferences;
+	FileRefArray _fileReferences;
 public:
 	/**
-	 * Prompt for a file
+	 * Prompt for a savegame to load or save, and populate a file reference from the result
 	 */
-	frefid_t prompt(glui32 usage, FileMode fmode, glui32 rock);
+	frefid_t createByPrompt(glui32 usage, FileMode fmode, glui32 rock);
 
 	/**
 	 * Create a new file reference
@@ -88,9 +111,25 @@ public:
 	frefid_t createRef(const Common::String &filename, glui32 usage, glui32 rock);
 
 	/**
+	 * Create a new temporary file reference
+	 */
+	frefid_t createTemp(glui32 usage, glui32 rock);
+
+	/**
+	 * Create a new file reference from an old one
+	 */
+	frefid_t createFromRef(frefid_t fref, glui32 usage, glui32 rock);
+
+	/**
 	 * Delete a file reference
 	 */
 	void deleteRef(frefid_t fref);
+
+	/**
+	 * Iterates to the next file reference following the specified one,
+	 * or the first if null is passed
+	 */
+	frefid_t iterate(frefid_t fref, glui32 *rock);
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 3538b4c..651499e 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -138,6 +138,11 @@ public:
 	 * Returns the primary filename for the game
 	 */
 	const Common::String &GargoyleEngine::getFilename() const;
+
+	/**
+	 * Return the game engine's target name
+	 */
+	const Common::String &getTargetName() const { return _targetName; }
 };
 
 extern GargoyleEngine *g_vm;
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 2f9f57e..7805207 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -459,23 +459,31 @@ glui32 Glk::glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *res
 }
 
 frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
-	// TODO
-	return nullptr;
+	return _files->createTemp(usage, rock);
 }
 
-frefid_t Glk::glk_fileref_create_by_name(glui32 usage, char *name, glui32 rock) {
-	// TODO
-	return nullptr;
+frefid_t Glk::glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock) {
+	// Take out all dangerous characters
+	Common::String tempName(name);
+	for (uint idx = 0; idx < tempName.size(); ++idx) {
+		if (tempName[idx] == '/' || tempName[idx] == '\\' || tempName[idx] == ':')
+			tempName.setChar(idx, '-');
+	}
+
+	return _files->createRef(tempName, usage, rock);
 }
 
 frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock) {
-	// TODO
-	return nullptr;
+	return _files->createByPrompt(usage, fmode, rock);
 }
 
 frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
-	// TODO
-	return nullptr;
+	if (!fref) {
+		warning("fileref_create_from_fileref: invalid ref");
+		return nullptr;
+	} else {
+		return _files->createFromRef(fref, usage, rock);
+	}
 }
 
 void Glk::glk_fileref_destroy(frefid_t fref) {
@@ -483,22 +491,24 @@ void Glk::glk_fileref_destroy(frefid_t fref) {
 }
 
 frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
-	// TODO
-	return nullptr;
+	return _files->iterate(fref, rockptr);
 }
 
 glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
-	// TODO
-	return 0;
+	if (!fref) {
+		warning("fileref_get_rock: invalid ref.");
+		return 0;
+	} else {
+		return fref->_rock;
+	}
 }
 
 void Glk::glk_fileref_delete_file(frefid_t fref) {
-	// TODO
+	fref->deleteFile();
 }
 
 glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
-	// TODO
-	return 0;
+	return fref->exists();
 }
 
 void Glk::glk_select(event_t *event) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 0124cb1..ccd2e5b 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -112,16 +112,12 @@ public:
 		glsi32 val);
 	void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint);
 	glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2);
-	glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint,
-		glui32 *result);
+	glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result);
 
 	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
-	frefid_t glk_fileref_create_by_name(glui32 usage, char *name,
-		glui32 rock);
-	frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode,
-		glui32 rock);
-	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref,
-		glui32 rock);
+	frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock);
+	frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock);
+	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock);
 	void glk_fileref_destroy(frefid_t fref);
 	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
 	glui32 glk_fileref_get_rock(frefid_t fref);


Commit: 063cfb35efa155e6383a0262b20baa656732a89a
    https://github.com/scummvm/scummvm/commit/063cfb35efa155e6383a0262b20baa656732a89a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added glk style methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 7805207..3148fa1 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -444,18 +444,74 @@ void Glk::glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val
 	// TODO
 }
 
-void Glk::glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint) {
+void Glk::glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint) {
 	// TODO
 }
 
-glui32 Glk::glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) {
-	// TODO
-	return 0;
+glui32 Glk::glk_style_distinguish(winid_t win, glui32 style1, glui32 style2) {
+	const WindowStyle *styles = win->getStyles();
+	if (!styles)
+		return false;
+
+	return styles[style1] == styles[style2] ? 0 : 1;
 }
 
-glui32 Glk::glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result) {
-	// TODO
-	return 0;
+bool Glk::glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result) {
+	const WindowStyle *styles = win->getStyles();
+	if (!styles)
+		return false;
+
+	switch (hint) {
+	case stylehint_Indentation:
+	case stylehint_ParaIndentation:
+		*result = 0;
+		break;
+
+	case stylehint_Justification:
+		*result = stylehint_just_LeftFlush;
+		break;
+
+	case stylehint_Size:
+		*result = 1;
+		break;
+
+	case stylehint_Weight:
+		*result =
+			(styles[style].font == PROPB || styles[style].font == PROPZ ||
+				styles[style].font == MONOB || styles[style].font == MONOZ);
+		break;
+
+	case stylehint_Oblique:
+		*result =
+			(styles[style].font == PROPI || styles[style].font == PROPZ ||
+				styles[style].font == MONOI || styles[style].font == MONOZ);
+		break;
+
+	case stylehint_Proportional:
+		*result =
+			(styles[style].font == PROPR || styles[style].font == PROPI ||
+				styles[style].font == PROPB || styles[style].font == PROPZ);
+		break;
+
+	case stylehint_TextColor:
+		*result =
+			(styles[style].fg[0] << 16) | (styles[style].fg[1] << 8) | (styles[style].fg[2]);
+		break;
+
+	case stylehint_BackColor:
+		*result =
+			(styles[style].bg[0] << 16) | (styles[style].bg[1] << 8) | (styles[style].bg[2]);
+		break;
+
+	case stylehint_ReverseColor:
+		*result = styles[style].reverse;
+		break;
+
+	default:
+		return false;
+	}
+
+	return true;
 }
 
 frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index ccd2e5b..bef6280 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -110,9 +110,9 @@ public:
 
 	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
 		glsi32 val);
-	void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint);
-	glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2);
-	glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result);
+	void glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint);
+	glui32 glk_style_distinguish(winid_t win, glui32 style1, glui32 style2);
+	bool glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result);
 
 	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
 	frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock);
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index fdca1d8..5359d4e 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -126,9 +126,6 @@ public:
 	glui32 _echoLineInput;
 	glui32 *_lineTerminators;
 
-	/* style hints and settings */
-	WindowStyle _styles[style_NUMSTYLES];
-
 	/* for copy selection */
 	glui32 *_copyBuf;
 	int _copyPos;
@@ -219,6 +216,11 @@ public:
 	virtual void cancelCharEvent() override { _charRequest = _charRequestUni = false; }
 
 	virtual void flowBreak() override;
+
+	/**
+	 * Returns a pointer to the styles for the window
+	 */
+	virtual const WindowStyle *getStyles() const override { return _styles; }
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 283922c..351d9fb 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -39,7 +39,7 @@ TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows,
 	_inArrayRock.num = 0;
 	_lineTerminators = nullptr;
 
-	Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], styles);
+	Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], _styles);
 }
 
 TextGridWindow::~TextGridWindow() {
@@ -598,9 +598,9 @@ void TextGridWindow::redraw() {
 			for (b = 0; b < _width; b++) {
 				if (ln->_attrs[a] != ln->_attrs[b]) {
 					link = ln->_attrs[a].hyper;
-					font = ln->_attrs[a].attrFont(styles);
-					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
-					bgcolor = ln->_attrs[a].attrBg(styles);
+					font = ln->_attrs[a].attrFont(_styles);
+					fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
+					bgcolor = ln->_attrs[a].attrBg(_styles);
 					w = (b - a) * g_conf->_cellW;
 					screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
 					o = x;
@@ -620,9 +620,9 @@ void TextGridWindow::redraw() {
 				}
 			}
 			link = ln->_attrs[a].hyper;
-			font = ln->_attrs[a].attrFont(styles);
-			fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(styles);
-			bgcolor = ln->_attrs[a].attrBg(styles);
+			font = ln->_attrs[a].attrFont(_styles);
+			fgcolor = link ? g_conf->_linkColor : ln->_attrs[a].attrFg(_styles);
+			bgcolor = ln->_attrs[a].attrBg(_styles);
 			w = (b - a) * g_conf->_cellW;
 			w += _bbox.right - (x + w);
 			screen.fillRect(Rect::fromXYWH(x, y, w, g_conf->_leading), bgcolor);
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 1fba707..0040b19 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -74,8 +74,6 @@ public:
 	Attributes _origAttr;
 	gidispatch_rock_t _inArrayRock;
 	glui32 *_lineTerminators;
-
-	WindowStyle styles[style_NUMSTYLES]; ///< style hints and settings
 public:
 	/**
 	 * Constructor
@@ -167,6 +165,11 @@ public:
 	virtual void requestHyperlinkEvent() override { _hyperRequest = true; }
 
 	virtual void cancelCharEvent() override { _charRequest = _charRequestUni = false; }
+
+	/**
+	 * Returns a pointer to the styles for the window
+	 */
+	virtual const WindowStyle *getStyles() const override { return _styles; }
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 6d09d82..8911881 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -618,6 +618,11 @@ void Window::setBackgroundColor(glui32 color) {
 	warning("setBackgroundColor: not a graphics window");
 }
 
+const WindowStyle *Window::getStyles() const {
+	warning("getStyles: not a text window");
+	return nullptr;
+}
+
 void Window::setTerminatorsLineEvent(glui32 *keycodes, glui32 count) {
 	if (dynamic_cast<TextBufferWindow *>(this) || dynamic_cast<TextGridWindow *>(this)) {
 		delete _lineTerminatorsBase;
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 20ce03d..bd24892 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -222,7 +222,14 @@ struct WindowStyle {
 	FACES font;
 	byte bg[3];
 	byte fg[3];
-	int reverse;
+	bool reverse;
+
+	/**
+	 * Equality comparison
+	 */
+	bool operator==(const WindowStyle &src) const {
+		return !memcmp(this, &src, sizeof(WindowStyle));
+	}
 };
 
 /**
@@ -295,6 +302,8 @@ struct Attributes {
  * Window definition
  */
 class Window {
+protected:
+	WindowStyle _styles[style_NUMSTYLES]; ///< style hints and settings for grid and buffer windows
 public:
 	Windows *_windows;
 	glui32 _rock;
@@ -448,6 +457,11 @@ public:
 	virtual void fillRect(glui32 color, const Rect &box);
 
 	virtual void setBackgroundColor(glui32 color);
+
+	/**
+	 * Returns a pointer to the styles for the window
+	 */
+	virtual const WindowStyle *getStyles() const;
 };
 typedef Window *winid_t;
 


Commit: f91cdb19bf2f96ffcb50de7d796bc32fcda38f34
    https://github.com/scummvm/scummvm/commit/f91cdb19bf2f96ffcb50de7d796bc32fcda38f34
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add glk style hint methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 3148fa1..a50c7a6 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -440,12 +440,123 @@ glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
 	return 0;
 }
 
-void Glk::glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val) {
-	// TODO
+void Glk::glk_stylehint_set(glui32 wintype, glui32 style, glui32 hint, glsi32 val) {
+	WindowStyle *styles;
+	bool p, b, i;
+
+	if (wintype == wintype_AllTypes) {
+		glk_stylehint_set(wintype_TextGrid, style, hint, val);
+		glk_stylehint_set(wintype_TextBuffer, style, hint, val);
+		return;
+	}
+
+	if (wintype == wintype_TextGrid)
+		styles = g_conf->_gStyles;
+	else if (wintype == wintype_TextBuffer)
+		styles = g_conf->_tStyles;
+	else
+		return;
+
+	if (!g_conf->_styleHint)
+		return;
+
+	switch (hint) {
+	case stylehint_TextColor:
+		styles[style].fg[0] = (val >> 16) & 0xff;
+		styles[style].fg[1] = (val >> 8) & 0xff;
+		styles[style].fg[2] = (val) & 0xff;
+		break;
+
+	case stylehint_BackColor:
+		styles[style].bg[0] = (val >> 16) & 0xff;
+		styles[style].bg[1] = (val >> 8) & 0xff;
+		styles[style].bg[2] = (val) & 0xff;
+		break;
+
+	case stylehint_ReverseColor:
+		styles[style].reverse = (val != 0);
+		break;
+
+	case stylehint_Proportional:
+		if (wintype == wintype_TextBuffer) {
+			p = val > 0;
+			b = styles[style].isBold();
+			i = styles[style].isItalic();
+			styles[style].font = WindowStyle::makeFont(p, b, i);
+		}
+		break;
+
+	case stylehint_Weight:
+		p = styles[style].isProp();
+		b = val > 0;
+		i = styles[style].isItalic();
+		styles[style].font = WindowStyle::makeFont(p, b, i);
+		break;
+
+	case stylehint_Oblique:
+		p = styles[style].isProp();
+		b = styles[style].isBold();
+		i = val > 0;
+		styles[style].font = WindowStyle::makeFont(p, b, i);
+		break;
+	}
+
+	if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_BackColor) {
+		memcpy(g_conf->_windowColor, styles[style].bg, 3);
+	}
+
+	if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_TextColor) {
+		memcpy(g_conf->_moreColor, styles[style].fg, 3);
+		memcpy(g_conf->_caretColor, styles[style].fg, 3);
+	}
 }
 
 void Glk::glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint) {
-	// TODO
+	WindowStyle *styles;
+	const WindowStyle *defaults;
+
+	if (wintype == wintype_AllTypes) {
+		glk_stylehint_clear(wintype_TextGrid, style, hint);
+		glk_stylehint_clear(wintype_TextBuffer, style, hint);
+		return;
+	}
+
+	if (wintype == wintype_TextGrid) {
+		styles = g_conf->_gStyles;
+		defaults = g_conf->_gStylesDefault;
+	} else if (wintype == wintype_TextBuffer) {
+		styles = g_conf->_tStyles;
+		defaults = g_conf->_tStylesDefault;
+	} else {
+		return;
+	}
+
+	if (!g_conf->_styleHint)
+		return;
+
+	switch (hint) {
+	case stylehint_TextColor:
+		styles[style].fg[0] = defaults[style].fg[0];
+		styles[style].fg[1] = defaults[style].fg[1];
+		styles[style].fg[2] = defaults[style].fg[2];
+		break;
+
+	case stylehint_BackColor:
+		styles[style].bg[0] = defaults[style].bg[0];
+		styles[style].bg[1] = defaults[style].bg[1];
+		styles[style].bg[2] = defaults[style].bg[2];
+		break;
+
+	case stylehint_ReverseColor:
+		styles[style].reverse = defaults[style].reverse;
+		break;
+
+	case stylehint_Proportional:
+	case stylehint_Weight:
+	case stylehint_Oblique:
+		styles[style].font = defaults[style].font;
+		break;
+	}
 }
 
 glui32 Glk::glk_style_distinguish(winid_t win, glui32 style1, glui32 style2) {
diff --git a/engines/gargoyle/window_text_buffer.h b/engines/gargoyle/window_text_buffer.h
index 5359d4e..9f0e20c 100644
--- a/engines/gargoyle/window_text_buffer.h
+++ b/engines/gargoyle/window_text_buffer.h
@@ -126,6 +126,9 @@ public:
 	glui32 _echoLineInput;
 	glui32 *_lineTerminators;
 
+	/* style hints and settings */
+	WindowStyle _styles[style_NUMSTYLES];
+
 	/* for copy selection */
 	glui32 *_copyBuf;
 	int _copyPos;
diff --git a/engines/gargoyle/window_text_grid.h b/engines/gargoyle/window_text_grid.h
index 0040b19..8470c36 100644
--- a/engines/gargoyle/window_text_grid.h
+++ b/engines/gargoyle/window_text_grid.h
@@ -74,6 +74,8 @@ public:
 	Attributes _origAttr;
 	gidispatch_rock_t _inArrayRock;
 	glui32 *_lineTerminators;
+
+	WindowStyle _styles[style_NUMSTYLES]; ///< style hints and settings
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index bd24892..5830c7c 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -230,6 +230,42 @@ struct WindowStyle {
 	bool operator==(const WindowStyle &src) const {
 		return !memcmp(this, &src, sizeof(WindowStyle));
 	}
+
+	/**
+	 * Returns true if the font is proportinate
+	 */
+	bool isProp() const {
+		return font == PROPR || font == PROPI || font == PROPB || font == PROPZ;
+	}
+
+	/**
+	 * Returns true ifont the font is bold
+	 */
+	bool isBold() const {
+		return font == PROPB || font == PROPZ || font == MONOB || font == MONOZ;
+	}
+
+	/**
+	 * Returns true ifont the font is italic
+	 */
+	bool isItalic() const {
+		return font == PROPI || font == PROPZ || font == MONOI || font == MONOZ;
+	}
+
+	/**
+	 * Returns a font that has the following combination of proportinate, bold, and italic
+	 */
+	static FACES makeFont(bool p, bool b, bool i) {
+		if (p && !b && !i) return PROPR;
+		if (p && !b &&  i) return PROPI;
+		if (p &&  b && !i) return PROPB;
+		if (p &&  b &&  i) return PROPZ;
+		if (!p && !b && !i) return MONOR;
+		if (!p && !b &&  i) return MONOI;
+		if (!p &&  b && !i) return MONOB;
+		if (!p &&  b &&  i) return MONOZ;
+		return PROPR;
+	}
 };
 
 /**
@@ -302,8 +338,6 @@ struct Attributes {
  * Window definition
  */
 class Window {
-protected:
-	WindowStyle _styles[style_NUMSTYLES]; ///< style hints and settings for grid and buffer windows
 public:
 	Windows *_windows;
 	glui32 _rock;


Commit: 60146c383d4b69920c6ba326611f9de66a513529
    https://github.com/scummvm/scummvm/commit/60146c383d4b69920c6ba326611f9de66a513529
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Merge Files class into existing Streams class

Changed paths:
  R engines/gargoyle/files.cpp
  R engines/gargoyle/files.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/module.mk
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/files.cpp b/engines/gargoyle/files.cpp
deleted file mode 100644
index e14a315..0000000
--- a/engines/gargoyle/files.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/files.h"
-#include "gargoyle/gargoyle.h"
-#include "gui/saveload.h"
-#include "common/file.h"
-#include "common/savefile.h"
-#include "common/translation.h"
-
-namespace Gargoyle {
-
-frefid_t Files::createByPrompt(glui32 usage, FileMode fmode, glui32 rock) {
-	switch (usage & fileusage_TypeMask) {
-	case fileusage_SavedGame: {
-		if (fmode == filemode_Write) {
-			// Select a savegame slot
-			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
-
-			int slot = dialog->runModalWithCurrentTarget();
-			if (slot >= 0) {
-				Common::String desc = dialog->getResultString();
-				return createRef(slot, desc, usage, rock);
-			}
-		} else if (fmode == filemode_Read) {
-			// Load a savegame slot
-			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
-
-			int slot = dialog->runModalWithCurrentTarget();
-			if (slot >= 0) {
-				return createRef(slot, "", usage, rock);
-			}
-		} else {
-			error("Unsupport file mode");
-		}
-		break;
-	}
-
-	case fileusage_Transcript:
-		return createRef("transcript.txt", fmode, rock);
-
-	default:
-		error("Unsupport file mode");
-		break;
-	}
-
-	return nullptr;
-}
-
-frefid_t Files::createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock) {
-	frefid_t fref = new FileReference();
-	fref->_slotNumber = slot;
-	fref->_description = desc;
-	fref->_textMode = ((usage & fileusage_TextMode) != 0);
-	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
-
-	_fileReferences.push_back(FileRefArray::value_type(fref));
-	return fref;
-}
-
-frefid_t Files::createRef(const Common::String &filename, glui32 usage, glui32 rock) {
-	frefid_t fref = new FileReference();
-	fref->_filename = filename;
-	fref->_textMode = ((usage & fileusage_TextMode) != 0);
-	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
-
-	_fileReferences.push_back(FileRefArray::value_type(fref));
-	return fref;
-}
-
-frefid_t Files::createTemp(glui32 usage, glui32 rock) {
-	return createRef(Common::String::format("%s.tmp", g_vm->getTargetName().c_str()),
-		usage, rock);
-}
-
-frefid_t Files::createFromRef(frefid_t fref, glui32 usage, glui32 rock) {
-	return createRef(fref->_filename, usage, rock);
-}
-
-void Files::deleteRef(frefid_t fref) {
-	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
-		if (_fileReferences[idx].get() == fref) {
-			_fileReferences.remove_at(idx);
-			return;
-		}
-	}
-}
-
-frefid_t Files::iterate(frefid_t fref, glui32 *rock) {
-	// Find reference following the specified one
-	int index = -1;
-	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
-		if (fref == nullptr || _fileReferences[idx].get() == fref) {
-			if (idx < (_fileReferences.size() - 1))
-				index = idx + 1;
-			break;
-		}
-	}
-
-	if (index != -1) {
-		if (rock)
-			*rock = _fileReferences[index].get()->_rock;
-		return _fileReferences[index].get();
-	}
-
-	if (rock)
-		*rock = 0;
-	return nullptr;
-}
-
-/*--------------------------------------------------------------------------*/
-
-const Common::String FileReference::getSaveName() const {
-	assert(_slotNumber != -1);
-	return Common::String::format("%s.%.3d", g_vm->getTargetName().c_str(), _slotNumber);
-}
-
-bool FileReference::exists() const {
-	Common::String filename;
-
-	if (_slotNumber == -1) {
-		if (Common::File::exists(_filename))
-			return true;
-		filename = _filename;
-	} else {
-		filename = getSaveName();
-	}
-
-	// Check for a savegame (or other file in the save folder) with that name
-	Common::InSaveFile *inSave = g_system->getSavefileManager()->openForLoading(filename);
-	bool result = inSave != nullptr;
-	delete inSave;
-	return result;
-}
-
-void FileReference::deleteFile() {
-	Common::String filename = (_slotNumber == -1) ? _filename : getSaveName();
-	g_system->getSavefileManager()->removeSavefile(filename);
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/files.h b/engines/gargoyle/files.h
deleted file mode 100644
index 7e7ebfb..0000000
--- a/engines/gargoyle/files.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FILES_H
-#define GARGOYLE_FILES_H
-
-#include "gargoyle/glk_types.h"
-#include "common/array.h"
-#include "common/ptr.h"
-#include "common/str.h"
-
-namespace Gargoyle {
-
-enum FileUsage {
-	fileusage_Data        = 0x00,
-	fileusage_SavedGame   = 0x01,
-	fileusage_Transcript  = 0x02,
-	fileusage_InputRecord = 0x03,
-	fileusage_TypeMask    = 0x0f,
-
-	fileusage_TextMode    = 0x100,
-	fileusage_BinaryMode  = 0x000,
-};
-
-enum FileMode {
-	filemode_Write       = 0x01,
-	filemode_Read        = 0x02,
-	filemode_ReadWrite   = 0x03,
-	filemode_WriteAppend = 0x05,
-};
-
-enum SeekMode {
-	seekmode_Start   = 0,
-	seekmode_Current = 1,
-	seekmode_End     = 2,
-};
-
-
-/**
- * File details
- */
-struct FileReference {
-	glui32 _rock;
-	int _slotNumber;
-	Common::String _description;
-	Common::String _filename;
-	FileUsage _fileType;
-	bool _textMode;
-	gidispatch_rock_t _dispRock;
-
-	/**
-	 * Constructor
-	 */
-	FileReference() : _rock(0), _slotNumber(-1), _fileType(fileusage_Data), _textMode(false) {}
-
-	/**
-	 * Get savegame filename
-	 */
-	const Common::String getSaveName() const;
-
-	/**
-	 * Returns true if the given file exists
-	 */
-	bool exists() const;
-
-	/**
-	 * Delete the given file
-	 */
-	void deleteFile();
-};
-
-typedef FileReference *frefid_t;
-typedef Common::Array< Common::SharedPtr<FileReference> > FileRefArray;
-
-class Files {
-private:
-	FileRefArray _fileReferences;
-public:
-	/**
-	 * Prompt for a savegame to load or save, and populate a file reference from the result
-	 */
-	frefid_t createByPrompt(glui32 usage, FileMode fmode, glui32 rock);
-
-	/**
-	 * Create a new file reference
-	 */
-	frefid_t createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock);
-
-	/**
-	 * Create a new file reference
-	 */
-	frefid_t createRef(const Common::String &filename, glui32 usage, glui32 rock);
-
-	/**
-	 * Create a new temporary file reference
-	 */
-	frefid_t createTemp(glui32 usage, glui32 rock);
-
-	/**
-	 * Create a new file reference from an old one
-	 */
-	frefid_t createFromRef(frefid_t fref, glui32 usage, glui32 rock);
-
-	/**
-	 * Delete a file reference
-	 */
-	void deleteRef(frefid_t fref);
-
-	/**
-	 * Iterates to the next file reference following the specified one,
-	 * or the first if null is passed
-	 */
-	frefid_t iterate(frefid_t fref, glui32 *rock);
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 503cf1c..3135fcb 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -32,7 +32,6 @@
 #include "gargoyle/clipboard.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/events.h"
-#include "gargoyle/files.h"
 #include "gargoyle/picture.h"
 #include "gargoyle/screen.h"
 #include "gargoyle/streams.h"
@@ -55,7 +54,6 @@ GargoyleEngine::~GargoyleEngine() {
 	delete _clipboard;
 	delete _conf;
 	delete _events;
-	delete _files;
 	delete _picList;
 	delete _screen;
 	delete _streams;
@@ -76,7 +74,6 @@ void GargoyleEngine::initialize() {
 
 	_clipboard = new Clipboard();
 	_events = new Events();
-	_files = new Files();
 	_picList = new PicList();
 	_streams = new Streams();
 	_windows = new Windows(_screen);
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 651499e..88859b4 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -36,7 +36,6 @@ namespace Gargoyle {
 class Clipboard;
 class Conf;
 class Events;
-class Files;
 class PicList;
 class Screen;
 class Streams;
@@ -98,7 +97,6 @@ public:
 	Clipboard *_clipboard;
 	Conf *_conf;
 	Events *_events;
-	Files *_files;
 	PicList *_picList;
 	Screen *_screen;
 	Streams *_streams;
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index a50c7a6..f386f22 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -626,7 +626,7 @@ bool Glk::glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *resu
 }
 
 frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
-	return _files->createTemp(usage, rock);
+	return _streams->createTemp(usage, rock);
 }
 
 frefid_t Glk::glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock) {
@@ -637,11 +637,11 @@ frefid_t Glk::glk_fileref_create_by_name(glui32 usage, const char *name, glui32
 			tempName.setChar(idx, '-');
 	}
 
-	return _files->createRef(tempName, usage, rock);
+	return _streams->createRef(tempName, usage, rock);
 }
 
 frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock) {
-	return _files->createByPrompt(usage, fmode, rock);
+	return _streams->createByPrompt(usage, fmode, rock);
 }
 
 frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
@@ -649,16 +649,16 @@ frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui3
 		warning("fileref_create_from_fileref: invalid ref");
 		return nullptr;
 	} else {
-		return _files->createFromRef(fref, usage, rock);
+		return _streams->createFromRef(fref, usage, rock);
 	}
 }
 
 void Glk::glk_fileref_destroy(frefid_t fref) {
-	_files->deleteRef(fref);
+	_streams->deleteRef(fref);
 }
 
 frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
-	return _files->iterate(fref, rockptr);
+	return _streams->iterate(fref, rockptr);
 }
 
 glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 306ac8b..f4422b0 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -5,7 +5,6 @@ MODULE_OBJS := \
 	conf.o \
 	detection.o \
 	events.o \
-	files.o \
 	fonts.o \
 	gargoyle.o \
 	glk.o \
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index cdc2e7d..93335b9 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -25,6 +25,10 @@
 #include "gargoyle/events.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/windows.h"
+#include "gui/saveload.h"
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/translation.h"
 
 namespace Gargoyle {
 
@@ -975,4 +979,136 @@ Stream *Streams::getFirst(uint32 *rock) {
 	return _streamList;
 }
 
+
+frefid_t Streams::createByPrompt(glui32 usage, FileMode fmode, glui32 rock) {
+	switch (usage & fileusage_TypeMask) {
+	case fileusage_SavedGame: {
+		if (fmode == filemode_Write) {
+			// Select a savegame slot
+			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+
+			int slot = dialog->runModalWithCurrentTarget();
+			if (slot >= 0) {
+				Common::String desc = dialog->getResultString();
+				return createRef(slot, desc, usage, rock);
+			}
+		}
+		else if (fmode == filemode_Read) {
+			// Load a savegame slot
+			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+
+			int slot = dialog->runModalWithCurrentTarget();
+			if (slot >= 0) {
+				return createRef(slot, "", usage, rock);
+			}
+		}
+		else {
+			error("Unsupport file mode");
+		}
+		break;
+	}
+
+	case fileusage_Transcript:
+		return createRef("transcript.txt", fmode, rock);
+
+	default:
+		error("Unsupport file mode");
+		break;
+	}
+
+	return nullptr;
+}
+
+frefid_t Streams::createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock) {
+	frefid_t fref = new FileReference();
+	fref->_slotNumber = slot;
+	fref->_description = desc;
+	fref->_textMode = ((usage & fileusage_TextMode) != 0);
+	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
+
+	_fileReferences.push_back(FileRefArray::value_type(fref));
+	return fref;
+}
+
+frefid_t Streams::createRef(const Common::String &filename, glui32 usage, glui32 rock) {
+	frefid_t fref = new FileReference();
+	fref->_filename = filename;
+	fref->_textMode = ((usage & fileusage_TextMode) != 0);
+	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
+
+	_fileReferences.push_back(FileRefArray::value_type(fref));
+	return fref;
+}
+
+frefid_t Streams::createTemp(glui32 usage, glui32 rock) {
+	return createRef(Common::String::format("%s.tmp", g_vm->getTargetName().c_str()),
+		usage, rock);
+}
+
+frefid_t Streams::createFromRef(frefid_t fref, glui32 usage, glui32 rock) {
+	return createRef(fref->_filename, usage, rock);
+}
+
+void Streams::deleteRef(frefid_t fref) {
+	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
+		if (_fileReferences[idx].get() == fref) {
+			_fileReferences.remove_at(idx);
+			return;
+		}
+	}
+}
+
+frefid_t Streams::iterate(frefid_t fref, glui32 *rock) {
+	// Find reference following the specified one
+	int index = -1;
+	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
+		if (fref == nullptr || _fileReferences[idx].get() == fref) {
+			if (idx < (_fileReferences.size() - 1))
+				index = idx + 1;
+			break;
+		}
+	}
+
+	if (index != -1) {
+		if (rock)
+			*rock = _fileReferences[index].get()->_rock;
+		return _fileReferences[index].get();
+	}
+
+	if (rock)
+		*rock = 0;
+	return nullptr;
+}
+
+/*--------------------------------------------------------------------------*/
+
+const Common::String FileReference::getSaveName() const {
+	assert(_slotNumber != -1);
+	return Common::String::format("%s.%.3d", g_vm->getTargetName().c_str(), _slotNumber);
+}
+
+bool FileReference::exists() const {
+	Common::String filename;
+
+	if (_slotNumber == -1) {
+		if (Common::File::exists(_filename))
+			return true;
+		filename = _filename;
+	}
+	else {
+		filename = getSaveName();
+	}
+
+	// Check for a savegame (or other file in the save folder) with that name
+	Common::InSaveFile *inSave = g_system->getSavefileManager()->openForLoading(filename);
+	bool result = inSave != nullptr;
+	delete inSave;
+	return result;
+}
+
+void FileReference::deleteFile() {
+	Common::String filename = (_slotNumber == -1) ? _filename : getSaveName();
+	g_system->getSavefileManager()->removeSavefile(filename);
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 08d97cb..264190c 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -33,12 +33,74 @@ namespace Gargoyle {
 class Window;
 class Streams;
 
+enum FileUsage {
+	fileusage_Data = 0x00,
+	fileusage_SavedGame = 0x01,
+	fileusage_Transcript = 0x02,
+	fileusage_InputRecord = 0x03,
+	fileusage_TypeMask = 0x0f,
+
+	fileusage_TextMode = 0x100,
+	fileusage_BinaryMode = 0x000,
+};
+
+enum FileMode {
+	filemode_Write = 0x01,
+	filemode_Read = 0x02,
+	filemode_ReadWrite = 0x03,
+	filemode_WriteAppend = 0x05,
+};
+
+enum SeekMode {
+	seekmode_Start = 0,
+	seekmode_Current = 1,
+	seekmode_End = 2,
+};
+
 struct StreamResult {
 	uint32 _readCount;
 	uint32 _writeCount;
 };
 typedef StreamResult stream_result_t;
 
+
+/**
+ * File details
+ */
+struct FileReference {
+	glui32 _rock;
+	int _slotNumber;
+	Common::String _description;
+	Common::String _filename;
+	FileUsage _fileType;
+	bool _textMode;
+	gidispatch_rock_t _dispRock;
+
+	/**
+	 * Constructor
+	 */
+	FileReference() : _rock(0), _slotNumber(-1), _fileType(fileusage_Data), _textMode(false) {}
+
+	/**
+	 * Get savegame filename
+	 */
+	const Common::String getSaveName() const;
+
+	/**
+	 * Returns true if the given file exists
+	 */
+	bool exists() const;
+
+	/**
+	 * Delete the given file
+	 */
+	void deleteFile();
+};
+
+typedef FileReference *frefid_t;
+typedef Common::Array< Common::SharedPtr<FileReference> > FileRefArray;
+
+
 /**
  * Base class for streams
  */
@@ -342,6 +404,7 @@ class Streams {
 private:
 	Stream *_streamList;
 	Stream *_currentStream;
+	FileRefArray _fileReferences;
 private:
 	/**
 	 * Adds a created stream to the list
@@ -397,6 +460,42 @@ public:
 	 * Gets the current output stream
 	 */
 	Stream *getCurrent() const { return _currentStream; }
+
+	/**
+	 * Prompt for a savegame to load or save, and populate a file reference from the result
+	 */
+	frefid_t createByPrompt(glui32 usage, FileMode fmode, glui32 rock);
+
+	/**
+	 * Create a new file reference
+	 */
+	frefid_t createRef(int slot, const Common::String &desc, glui32 usage, glui32 rock);
+
+	/**
+	 * Create a new file reference
+	 */
+	frefid_t createRef(const Common::String &filename, glui32 usage, glui32 rock);
+
+	/**
+	 * Create a new temporary file reference
+	 */
+	frefid_t createTemp(glui32 usage, glui32 rock);
+
+	/**
+	 * Create a new file reference from an old one
+	 */
+	frefid_t createFromRef(frefid_t fref, glui32 usage, glui32 rock);
+
+	/**
+	 * Delete a file reference
+	 */
+	void deleteRef(frefid_t fref);
+
+	/**
+	 * Iterates to the next file reference following the specified one,
+	 * or the first if null is passed
+	 */
+	frefid_t iterate(frefid_t fref, glui32 *rock);
 };
 
 } // End of namespace Gargoyle


Commit: db112fc9f02e2a49b47a62d85f4920332b285728
    https://github.com/scummvm/scummvm/commit/db112fc9f02e2a49b47a62d85f4920332b285728
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Adding file stream opening and closing

Changed paths:
    engines/gargoyle/events.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h
    engines/gargoyle/windows.cpp


diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 3860dc2..70e7af4 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -215,6 +215,16 @@ public:
 	 * Wait for a keyboard or mouse press
 	 */
 	void waitForPress();
+
+	/**
+	 * Get the total number of frames played
+	 */
+	uint32 getTotalPlayTicks() const { return _frameCounter; }
+
+	/**
+	 * Set the total number of frames played
+	 */
+	void Events::setTotalPlayTicks(uint frames) { _frameCounter = frames; }
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index f386f22..fb140a3 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -331,12 +331,11 @@ void Glk::glk_set_window(winid_t win) {
 }
 
 strid_t Glk::glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock) {
-	// TODO
-	return nullptr;
+	return _streams->openFileStream(fileref, fmode, rock, false);
 }
 
 strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock) {
-	return _streams->addMemoryStream(buf, buflen, fmode, rock, false);
+	return _streams->openMemoryStream(buf, buflen, fmode, rock, false);
 }
 
 void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
@@ -847,12 +846,11 @@ glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
 }
 
 strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock) {
-	// TODO
-	return nullptr;
+	return _streams->openFileStream(fileref, fmode, rock, true);
 }
 
 strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock) {
-	return _streams->addMemoryStream(buf, buflen, fmode, rock, true);
+	return _streams->openMemoryStream(buf, buflen, fmode, rock, true);
 }
 
 void Glk::glk_request_char_event_uni(winid_t win) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index bef6280..7312a35 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -24,7 +24,6 @@
 #define GARGOYLE_GLK_H
 
 #include "gargoyle/gargoyle.h"
-#include "gargoyle/files.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/time.h"
 #include "gargoyle/windows.h"
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 93335b9..99ced12 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -508,11 +508,47 @@ glui32 MemoryStream::getLineUni(glui32 *ubuf, glui32 len) {
 
 /*--------------------------------------------------------------------------*/
 
-FileStream::FileStream(Streams *streams, uint32 rock, bool unicode) :
-	Stream(streams, true, false, rock, unicode), _lastOp(0), _textFile(false) {
-	// TODO: Set up files
-	_outFile = nullptr;
-	_inFile = nullptr;
+FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 rock, bool unicode) :
+		Stream(streams, true, false, rock, unicode), _lastOp(0), _textFile(false),
+		_inFile(nullptr), _outFile(nullptr), _inStream(nullptr) {
+	Common::String fname = fref->_slotNumber == -1 ? fref->_filename : fref->getSaveName();
+
+	if (fmode == filemode_Write || fmode == filemode_ReadWrite || fmode == filemode_WriteAppend) {
+		_outFile = g_system->getSavefileManager()->openForSaving(fname, fref->_slotNumber != -1);
+		if (!_outFile)
+			error("Could open file for writing - %s", fname.c_str());
+
+		if (fref->_slotNumber != -1)
+			writeSavegameHeader(_outFile, fref->_description);
+	} else if (fmode == filemode_Read) {
+		if (_file.open(fname)) {
+			_inStream = &_file;
+		} else {
+			_inFile = g_system->getSavefileManager()->openForLoading(fname);
+			_inStream = _inFile;
+		}
+
+		if (!_inStream)
+			error("Could not open for reading - %s", fname.c_str());
+
+		if (_inFile) {
+			// It's a save file, so skip over the header
+			SavegameHeader header;
+			if (!readSavegameHeader(_inStream, header))
+				error("Invalid savegame");
+
+			g_vm->_events->setTotalPlayTicks(header._totalFrames);
+		}
+	}
+}
+
+FileStream::~FileStream() {
+	_file.close();
+	delete _inFile;
+	if (_outFile) {
+		_outFile->finalize();
+		delete _outFile;
+	}
 }
 
 void FileStream::ensureOp(FileMode mode) {
@@ -798,7 +834,6 @@ glsi32 FileStream::getCharUni() {
 	}
 }
 
-
 glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 	if (!_readable)
 		return 0;
@@ -926,6 +961,55 @@ glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
 	}
 }
 
+bool FileStream::readSavegameHeader(Common::SeekableReadStream *stream, SavegameHeader &header) {
+	header._totalFrames = 0;
+
+	// Validate the header Id
+	if (stream->readUint32BE() != MKTAG('G', 'A', 'R', 'G'))
+		return false;
+
+	// Check the savegame version
+	header._version = stream->readByte();
+	if (header._version > SAVEGAME_VERSION)
+		error("Savegame is too recent");
+
+	// Read in name
+	char c;
+	while ((c = stream->readByte()) != '\0')
+		header._saveName += c;
+
+	// Read in save date/time
+	header._year = stream->readUint16LE();
+	header._month = stream->readUint16LE();
+	header._day = stream->readUint16LE();
+	header._hour = stream->readUint16LE();
+	header._minute = stream->readUint16LE();
+	header._totalFrames = stream->readUint32LE();
+
+	return true;
+}
+
+void FileStream::writeSavegameHeader(Common::WriteStream *stream, const Common::String &saveName) {
+	// Write out a savegame header
+	stream->writeUint32BE(MKTAG('G', 'A', 'R', 'G'));
+
+	stream->writeByte(SAVEGAME_VERSION);
+
+	// Write savegame name
+	stream->write(saveName.c_str(), saveName.size());
+	stream->writeByte('\0');
+
+	// Write out the save date/time
+	TimeDate td;
+	g_system->getTimeAndDate(td);
+	stream->writeUint16LE(td.tm_year + 1900);
+	stream->writeUint16LE(td.tm_mon + 1);
+	stream->writeUint16LE(td.tm_mday);
+	stream->writeUint16LE(td.tm_hour);
+	stream->writeUint16LE(td.tm_min);
+	stream->writeUint32LE(g_vm->_events->getTotalPlayTicks());
+}
+
 /*--------------------------------------------------------------------------*/
 
 Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) {
@@ -936,13 +1020,19 @@ Streams::~Streams() {
 		delete _streamList;
 }
 
-WindowStream *Streams::addWindowStream(Window *window) {
+FileStream *Streams::openFileStream(frefid_t fref, glui32 fmode, glui32 rock, bool unicode) {
+	FileStream *stream = new FileStream(this, fref, fmode, rock, unicode);
+	addStream(stream);
+	return stream;
+}
+
+WindowStream *Streams::openWindowStream(Window *window) {
 	WindowStream *stream = new WindowStream(this, window);
 	addStream(stream);
 	return stream;
 }
 
-MemoryStream *Streams::addMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) {
+MemoryStream *Streams::openMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) {
 	MemoryStream *stream = new MemoryStream(this, buf, buflen, mode, rock, unicode);
 	addStream(stream);
 	return stream;
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 264190c..517192a 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -24,12 +24,15 @@
 #define GARGOYLE_STREAMS_H
 
 #include "common/scummsys.h"
+#include "common/file.h"
 #include "common/savefile.h"
-#include "gargoyle/files.h"
+#include "common/str.h"
 #include "gargoyle/glk_types.h"
 
 namespace Gargoyle {
 
+#define SAVEGAME_VERSION 1
+
 class Window;
 class Streams;
 
@@ -63,6 +66,19 @@ struct StreamResult {
 };
 typedef StreamResult stream_result_t;
 
+struct SavegameHeader {
+	uint8 _version;
+	Common::String _saveName;
+	int _year, _month, _day;
+	int _hour, _minute;
+	int _totalFrames;
+
+	/**
+	 * Constructor
+	 */
+	SavegameHeader() : _version(0), _year(0), _month(0), _day(0), _hour(0),
+		_minute(0), _totalFrames(0) {}
+};
 
 /**
  * File details
@@ -326,8 +342,10 @@ public:
  */
 class FileStream : public Stream {
 private:
+	Common::File _file;
 	Common::OutSaveFile *_outFile;
 	Common::InSaveFile *_inFile;
+	Common::SeekableReadStream *_inStream;
 	uint32 _lastOp;					///< 0, filemode_Write, or filemode_Read
 	bool _textFile;
 private:
@@ -347,9 +365,24 @@ private:
 	glsi32 getCharUtf8();
 public:
 	/**
+	 * Read a savegame header from a stream
+	 */
+	static bool readSavegameHeader(Common::SeekableReadStream *stream, SavegameHeader &header);
+
+	/**
+	 * Write out a savegame header
+	 */
+	static void writeSavegameHeader(Common::WriteStream *stream, const Common::String &saveName);
+public:
+	/**
 	 * Constructor
 	 */
-	FileStream(Streams *streams, uint32 rock = 0, bool unicode = true);
+	FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 rock, bool unicode);
+
+	/**
+	 * Destructor
+	 */
+	virtual ~FileStream();
 
 	/**
 	 * Write a character
@@ -427,14 +460,19 @@ public:
 	~Streams();
 
 	/**
-	 * Add a window stream
+	 * Open a file stream
+	 */
+	FileStream *openFileStream(frefid_t fref, glui32 fmode, glui32 rock, bool unicode);
+
+	/**
+	 * Open a window stream
 	 */
-	WindowStream *addWindowStream(Window *window);
+	WindowStream *openWindowStream(Window *window);
 
 	/**
-	 * Add a memory stream
+	 * Open a memory stream
 	 */
-	MemoryStream *addMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
+	MemoryStream *openMemoryStream(void *buf, size_t buflen, FileMode mode, uint32 rock = 0, bool unicode = true);
 
 	/**
 	 * Delete a stream
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index 8911881..b0f1cd7 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -502,7 +502,7 @@ Window::Window(Windows *windows, glui32 rock) : _windows(windows), _rock(rock),
 	_dispRock.num = 0;
 
 	Streams &streams = *g_vm->_streams;
-	_stream = streams.addWindowStream(this);
+	_stream = streams.openWindowStream(this);
 }
 
 Window::~Window() {


Commit: 30cd230d883091397c7e6ca73442d560824efd0d
    https://github.com/scummvm/scummvm/commit/30cd230d883091397c7e6ca73442d560824efd0d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fixes for file stream reading

Changed paths:
    engines/gargoyle/streams.cpp


diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 99ced12..7995f4d 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -658,21 +658,21 @@ glsi32 FileStream::getCharUtf8() {
 	glui32 res;
 	glui32 val0, val1, val2, val3;
 
-	if (_inFile->eos())
+	if (_inStream->eos())
 		return -1;
-	val0 = _inFile->readByte();
+	val0 = _inStream->readByte();
 	if (val0 < 0x80) {
 		res = val0;
 		return res;
 	}
 
 	if ((val0 & 0xe0) == 0xc0) {
-		if (_inFile->eos()) {
+		if (_inStream->eos()) {
 			warning("incomplete two-byte character");
 			return -1;
 		}
 
-		val1 = _inFile->readByte();
+		val1 = _inStream->readByte();
 		if ((val1 & 0xc0) != 0x80) {
 			warning("malformed two-byte character");
 			return '?';
@@ -684,9 +684,9 @@ glsi32 FileStream::getCharUtf8() {
 	}
 
 	if ((val0 & 0xf0) == 0xe0) {
-		val1 = _inFile->readByte();
-		val2 = _inFile->readByte();
-		if (_inFile->eos()) {
+		val1 = _inStream->readByte();
+		val2 = _inStream->readByte();
+		if (_inStream->eos()) {
 			warning("incomplete three-byte character");
 			return -1;
 		}
@@ -711,10 +711,10 @@ glsi32 FileStream::getCharUtf8() {
 			return '?';
 		}
 
-		val1 = _inFile->readByte();
-		val2 = _inFile->readByte();
-		val3 = _inFile->readByte();
-		if (_inFile->eos()) {
+		val1 = _inStream->readByte();
+		val2 = _inStream->readByte();
+		val3 = _inStream->readByte();
+		if (_inStream->eos()) {
 			warning("incomplete four-byte character");
 			return -1;
 		}
@@ -743,7 +743,7 @@ glsi32 FileStream::getCharUtf8() {
 }
 
 glui32 FileStream::getPosition() const {
-	return _outFile->pos();
+	return _outFile ? _outFile->pos() : _inStream->pos();
 }
 
 void FileStream::setPosition(glui32 pos, glui32 seekMode) {
@@ -751,9 +751,11 @@ void FileStream::setPosition(glui32 pos, glui32 seekMode) {
 	if (_unicode)
 		pos *= 4;
 	
-	error("FileStream::setPosition - seek not yet supported");
-//	fseek(str->file, pos, ((seekmode == seekmode_Current) ? 1 :
-	//		((seekmode == seekmode_End) ? 2 : 0)));
+	if (_inStream) {
+		_inStream->seek(pos, SEEK_SET);
+	} else {
+		error("seek not supported for writing files");
+	}
 }
 
 glsi32 FileStream::getChar() {
@@ -763,25 +765,25 @@ glsi32 FileStream::getChar() {
 	ensureOp(filemode_Read);
 	int res;
 	if (!_unicode) {
-		res = _inFile->readByte();
+		res = _inStream->readByte();
 	} else if (_textFile) {
 		res = getCharUtf8();
 	} else {
 		glui32 ch;
-		res = _inFile->readByte();
-		if (_inFile->eos())
+		res = _inStream->readByte();
+		if (_inStream->eos())
 			return -1;
 		ch = (res & 0xFF);
-		res = _inFile->readByte();
-		if (_inFile->eos())
+		res = _inStream->readByte();
+		if (_inStream->eos())
 			return -1;
 		ch = (ch << 8) | (res & 0xFF);
-		res = _inFile->readByte();
-		if (_inFile->eos())
+		res = _inStream->readByte();
+		if (_inStream->eos())
 			return -1;
 		ch = (ch << 8) | (res & 0xFF);
-		res = _inFile->readByte();
-		if (_inFile->eos())
+		res = _inStream->readByte();
+		if (_inStream->eos())
 			return -1;
 		ch = (ch << 8) | (res & 0xFF);
 		res = ch;
@@ -803,24 +805,24 @@ glsi32 FileStream::getCharUni() {
 	ensureOp(filemode_Read);
 	int res;
 	if (!_unicode) {
-		res = _inFile->readByte();
+		res = _inStream->readByte();
 	} else if (_textFile) {
 		res = getCharUtf8();
 	} else {
 		glui32 ch;
-		res = _inFile->readByte();
+		res = _inStream->readByte();
 		if (res == -1)
 			return -1;
 		ch = (res & 0xFF);
-		res = _inFile->readByte();
+		res = _inStream->readByte();
 		if (res == -1)
 			return -1;
 		ch = (ch << 8) | (res & 0xFF);
-		res = _inFile->readByte();
+		res = _inStream->readByte();
 		if (res == -1)
 			return -1;
 		ch = (ch << 8) | (res & 0xFF);
-		res = _inFile->readByte();
+		res = _inStream->readByte();
 		if (res == -1)
 			return -1;
 		ch = (ch << 8) | (res & 0xFF);
@@ -844,7 +846,7 @@ glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 		for (lx = 0; lx<len; lx++) {
 			int res;
 			glui32 ch;
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (res & 0xFF);
@@ -869,19 +871,19 @@ glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 		{
 			int res;
 			glui32 ch;
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (res & 0xFF);
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (ch << 8) | (res & 0xFF);
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (ch << 8) | (res & 0xFF);
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (ch << 8) | (res & 0xFF);
@@ -906,7 +908,7 @@ glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
 			int res;
 			glui32 ch;
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (res & 0xFF);
@@ -936,19 +938,19 @@ glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
 			int res;
 			glui32 ch;
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (res & 0xFF);
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (ch << 8) | (res & 0xFF);
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (ch << 8) | (res & 0xFF);
-			res = _inFile->readByte();
+			res = _inStream->readByte();
 			if (res == -1)
 				break;
 			ch = (ch << 8) | (res & 0xFF);


Commit: 8333aed5c2952ff7aa46870cac93acb595a67f14
    https://github.com/scummvm/scummvm/commit/8333aed5c2952ff7aa46870cac93acb595a67f14
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Adding extra fields to the savegame format for validation

Changed paths:
    engines/gargoyle/detection.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 63ada3e..ffd7fa9 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -23,11 +23,12 @@
 #include "gargoyle/gargoyle.h"
 
 #include "base/plugins.h"
+#include "common/md5.h"
+#include "common/memstream.h"
 #include "common/savefile.h"
 #include "common/str-array.h"
-#include "common/memstream.h"
-#include "engines/advancedDetector.h"
 #include "common/system.h"
+#include "engines/advancedDetector.h"
 #include "graphics/colormasks.h"
 #include "graphics/surface.h"
 
@@ -36,28 +37,33 @@
 namespace Gargoyle {
 
 struct GargoyleGameDescription {
-	ADGameDescription desc;
-	Common::String filename;
-	InterpreterType interpType;
+	ADGameDescription _desc;
+	Common::String _filename;
+	InterpreterType _interpType;
+	Common::String _md5;
 };
 
 const Common::String &GargoyleEngine::getFilename() const {
-	return _gameDescription->filename;
+	return _gameDescription->_filename;
 }
 uint32 GargoyleEngine::getFeatures() const {
-	return _gameDescription->desc.flags;
+	return _gameDescription->_desc.flags;
 }
 
 bool GargoyleEngine::isDemo() const {
-	return (bool)(_gameDescription->desc.flags & ADGF_DEMO);
+	return (bool)(_gameDescription->_desc.flags & ADGF_DEMO);
 }
 
 Common::Language GargoyleEngine::getLanguage() const {
-	return _gameDescription->desc.language;
+	return _gameDescription->_desc.language;
 }
 
 InterpreterType GargoyleEngine::getInterpreterType() const {
-	return _gameDescription->interpType;
+	return _gameDescription->_interpType;
+}
+
+const Common::String &GargoyleEngine::getGameMD5() const {
+	return _gameDescription->_md5;
 }
 
 } // End of namespace Gargoyle
@@ -117,9 +123,9 @@ bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
 
 bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
 	Gargoyle::GargoyleGameDescription *gd = (Gargoyle::GargoyleGameDescription *)desc;
-	gd->filename = ConfMan.get("filename");
+	gd->_filename = ConfMan.get("filename");
 
-	switch (gd->interpType) {
+	switch (gd->_interpType) {
 	case Gargoyle::INTERPRETER_SCOTT:
 		*engine = new Gargoyle::Scott::Scott(syst, gd);
 		break;
@@ -160,13 +166,15 @@ ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, con
 	static char gameId[100];
 	strcpy(gameId, ConfMan.get("gameid").c_str());
 	Common::String filename = ConfMan.get("filename");
-
-	if (parent.getChild(filename).exists()) {
-		gameDescription.desc.gameId = gameId;
-		gameDescription.desc.language = language;
-		gameDescription.desc.platform = platform;
-		gameDescription.desc.extra = extra.c_str();
-		gameDescription.filename = filename;
+	Common::File f;
+
+	if (f.open(parent.getChild(filename))) {
+		gameDescription._desc.gameId = gameId;
+		gameDescription._desc.language = language;
+		gameDescription._desc.platform = platform;
+		gameDescription._desc.extra = extra.c_str();
+		gameDescription._filename = filename;
+		gameDescription._md5 = Common::computeStreamMD5AsString(f, 5000);
 
 		ADDetectedGame dg((ADGameDescription *)&gameDescription);
 		detectedGames.push_back(dg);
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 88859b4..5ec804e 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -43,7 +43,21 @@ class Windows;
 class WindowMask;
 
 enum InterpreterType {
-	INTERPRETER_SCOTT
+	INTERPRETER_ADVSYS = 0,
+	INTERPRETER_AGILITY = 1,
+	INTERPRETER_ALAN2 = 2,
+	INTERPRETER_ALAN3 = 3,
+	INTERPRETER_BOCFEL = 4,
+	INTERPRETER_FROTZ = 5,
+	INTERPRETER_GEAS = 6,
+	INTERPRETER_HUGO = 7,
+	INTERPRETER_JACL = 8,
+	INTERPRETER_LEVEL9 = 9,
+	INTERPRETER_MAGNETIC = 10,
+	INTERPRETER_NITFOL = 11,
+	INTERPRETER_SCARE = 12,
+	INTERPRETER_SCOTT = 13,
+	INTERPRETER_TADS = 14
 };
 
 enum GargoyleDebugChannels {
@@ -133,6 +147,11 @@ public:
 	InterpreterType getInterpreterType() const;
 
 	/**
+	 * Returns the game's md5
+	 */
+	const Common::String &getGameMD5() const;
+
+	/**
 	 * Returns the primary filename for the game
 	 */
 	const Common::String &GargoyleEngine::getFilename() const;
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index fb140a3..2f0f0b4 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -59,7 +59,7 @@ Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 }
 
 void Glk::glk_exit(void) {
-	glk_put_string("[ press any key to exit ]");
+ 	glk_put_string("[ press any key to exit ]");
 	_events->waitForPress();
 
 	quitGame();
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 7995f4d..a8c85a1 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -963,6 +963,15 @@ glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
 	}
 }
 
+static Common::String readString(Common::ReadStream *src) {
+	char c;
+	Common::String result;
+	while ((c = src->readByte()) != 0)
+		result += c;
+
+	return result;
+}
+
 bool FileStream::readSavegameHeader(Common::SeekableReadStream *stream, SavegameHeader &header) {
 	header._totalFrames = 0;
 
@@ -975,10 +984,17 @@ bool FileStream::readSavegameHeader(Common::SeekableReadStream *stream, Savegame
 	if (header._version > SAVEGAME_VERSION)
 		error("Savegame is too recent");
 
+	// Read the interpreter, language, and game Id
+	header._interpType = stream->readByte();
+	header._language = stream->readByte();
+	header._md5 = readString(stream);
+
+	if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage()
+			|| header._md5 != g_vm->getGameMD5())
+		return false;
+
 	// Read in name
-	char c;
-	while ((c = stream->readByte()) != '\0')
-		header._saveName += c;
+	header._saveName = readString(stream);
 
 	// Read in save date/time
 	header._year = stream->readUint16LE();
@@ -994,9 +1010,15 @@ bool FileStream::readSavegameHeader(Common::SeekableReadStream *stream, Savegame
 void FileStream::writeSavegameHeader(Common::WriteStream *stream, const Common::String &saveName) {
 	// Write out a savegame header
 	stream->writeUint32BE(MKTAG('G', 'A', 'R', 'G'));
-
 	stream->writeByte(SAVEGAME_VERSION);
 
+	// Write out intrepreter type, language, and game Id
+	stream->writeByte(g_vm->getInterpreterType());
+	stream->writeByte(g_vm->getLanguage());
+	Common::String md5 = g_vm->getGameMD5();
+	stream->write(md5.c_str(), md5.size());
+	stream->writeByte('\0');
+
 	// Write savegame name
 	stream->write(saveName.c_str(), saveName.size());
 	stream->writeByte('\0');
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 517192a..dad6781 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -68,6 +68,9 @@ typedef StreamResult stream_result_t;
 
 struct SavegameHeader {
 	uint8 _version;
+	byte _interpType;
+	byte _language;
+	Common::String _md5;
 	Common::String _saveName;
 	int _year, _month, _day;
 	int _hour, _minute;
@@ -76,8 +79,8 @@ struct SavegameHeader {
 	/**
 	 * Constructor
 	 */
-	SavegameHeader() : _version(0), _year(0), _month(0), _day(0), _hour(0),
-		_minute(0), _totalFrames(0) {}
+	SavegameHeader() : _version(0), _interpType(0), _language(0), _year(0), _month(0), _day(0),
+		_hour(0), _minute(0), _totalFrames(0) {}
 };
 
 /**


Commit: ce582aab000187a646dab6cc978b821da4660940
    https://github.com/scummvm/scummvm/commit/ce582aab000187a646dab6cc978b821da4660940
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Further work on game detection

Changed paths:
    engines/gargoyle/detection.cpp


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index ffd7fa9..5167440 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -162,25 +162,32 @@ DetectedGames GargoyleMetaEngine::detectGames(const Common::FSList &fslist) cons
 static Gargoyle::GargoyleGameDescription gameDescription;
 
 ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
-	ADDetectedGames detectedGames;
 	static char gameId[100];
 	strcpy(gameId, ConfMan.get("gameid").c_str());
 	Common::String filename = ConfMan.get("filename");
+
+	Common::FSList fslist;
+	DetectedGames detectedGames;
+	fslist.push_back(parent.getChild(filename));
+	ADDetectedGames results;
 	Common::File f;
 
-	if (f.open(parent.getChild(filename))) {
+	Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
+	if (detectedGames.size() > 0 && f.open(parent.getChild(filename))) {
+		DetectedGame gd = detectedGames.front();
+
+		gameDescription._interpType = Gargoyle::INTERPRETER_SCOTT;
 		gameDescription._desc.gameId = gameId;
-		gameDescription._desc.language = language;
-		gameDescription._desc.platform = platform;
-		gameDescription._desc.extra = extra.c_str();
+		gameDescription._desc.language = gd.language;
+		gameDescription._desc.platform = gd.platform;
 		gameDescription._filename = filename;
 		gameDescription._md5 = Common::computeStreamMD5AsString(f, 5000);
 
 		ADDetectedGame dg((ADGameDescription *)&gameDescription);
-		detectedGames.push_back(dg);
+		results.push_back(dg);
 	}
 
-	return detectedGames;
+	return results;
 }
 
 #if PLUGIN_ENABLED_DYNAMIC(GARGOYLE)


Commit: 8bb3f55dff3dfe82a2d85783d06c17ffa8aa3f60
    https://github.com/scummvm/scummvm/commit/8bb3f55dff3dfe82a2d85783d06c17ffa8aa3f60
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Add custom game Ids for each known Scott Adams game

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


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 5167440..8c0580a 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -69,7 +69,25 @@ const Common::String &GargoyleEngine::getGameMD5() const {
 } // End of namespace Gargoyle
 
 static const PlainGameDescriptor gargoyleGames[] = {
-	{"scott", "Scott Adams Games"},
+	{"scottadams", "Scott Adams Games"},
+
+	// Scott Adams games
+	{ "adventureland", "Adventureland" },
+	{ "pirateadventure", "Pirate Adventure" },
+	{ "missionimpossible", "Mission Impossible" },
+	{ "voodoocastle", "Voodoo Castle" },
+	{ "thecount", "The Count" },
+	{ "strangeodyssey", "Strange Odyssey" },
+	{ "mysteryfunhouse", "Mystery Fun House" },
+	{ "pyramidofdoom", "Pyramid Of Doom" },
+	{ "ghosttown", "Ghost Town" },
+	{ "savageisland1", "Savage Island, Part 1" },
+	{ "savageisland2", "Savage Island, Part 2" },
+	{ "goldenvoyage", "The Golden Voyage" },
+	{ "adventure13", "Adventure 13" },
+	{ "adventure14", "Adventure 14" },
+	{ "buckaroobonzai", "Buckaroo Banzai" },
+
 	{0, 0}
 };
 
@@ -90,7 +108,7 @@ public:
 	}
 
 	virtual const char *getOriginalCopyright() const {
-		return "Gargoyle Engine (c)";
+		return "Gargoyle Engine (c) 2018";
 	}
 
 	virtual bool hasFeature(MetaEngineFeature f) const override;
diff --git a/engines/gargoyle/scott/detection.cpp b/engines/gargoyle/scott/detection.cpp
index 6c9f5c4..04d4f83 100644
--- a/engines/gargoyle/scott/detection.cpp
+++ b/engines/gargoyle/scott/detection.cpp
@@ -29,27 +29,28 @@ namespace Scott {
 
 struct ScottGame {
 	const char *_md5;
+	const char *_gameId;
 	int32 _filesize;
 	const char *_desc;
 };
 
 const ScottGame SCOTT_GAMES[] = {
-	{ "ae541fc1085da2f7d561b72ed20a6bc1", 18003, "Adventureland" },
-	{ "cbd47ab4fcfe00231ffd71d52378d410", 18482, "Pirate Adventure" },
-	{ "9251ab2c64e63559d8a6e9e6246760a5", 17227, "Mission Impossible" },
-	{ "be849c5747c7fc3b201984afb4403b8e", 18140, "Voodoo Castle" },
-	{ "85b75b6079b5ee572b5259b29a0e5d21", 19999, "The Count" },
-	{ "c423cae841ac1927b5b2e503607b21bc", 20115, "Strange Odyssey" },
-	{ "326b98b991d401605074e64d474ce566", 19700, "Mystery Fun House" },
-	{ "8ef9010399f055da9adb15ce7745a11c", 20320, "Pyramid Of Doom" },
-	{ "fcdcca8b2acf76ba2d0006cefa3630a1", 20687, "Ghost Town" },
-	{ "c8aaa80f07c40fa8e4b17432644919dc", 22669, "Save Island, Part 1" },
-	{ "2add0f28d9b236c866890cdf8d86ee60", 21169, "Savage Island, Part 2" },
-	{ "675126bd0477e8ed9230ad3db5afc45f", 21401, "The Golden Voyage" },
-	{ "0ef0def798d895ed766041fa99dd28a0", 22346, "Adventure 13" },
-	{ "0bf1bcc649422798332a38c88588fdff", 22087, "Adventure 14" },
-	{ "a0a5423967287dae9cbeb9abe8324479", 21038, "Buckaroo Banzai" },
-	{ nullptr, 0, nullptr }
+	{ "ae541fc1085da2f7d561b72ed20a6bc1", "adventureland", 18003, "Adventureland" },
+	{ "cbd47ab4fcfe00231ffd71d52378d410", "pirateadventure", 18482, "Pirate Adventure" },
+	{ "9251ab2c64e63559d8a6e9e6246760a5", "missionimpossible", 17227, "Mission Impossible" },
+	{ "be849c5747c7fc3b201984afb4403b8e", "voodoocastle", 18140, "Voodoo Castle" },
+	{ "85b75b6079b5ee572b5259b29a0e5d21", "thecount", 19999, "The Count" },
+	{ "c423cae841ac1927b5b2e503607b21bc", "strangeodyssey", 20115, "Strange Odyssey" },
+	{ "326b98b991d401605074e64d474ce566", "mysteryfunhouse", 19700, "Mystery Fun House" },
+	{ "8ef9010399f055da9adb15ce7745a11c", "pyramidofdoom", 20320, "Pyramid Of Doom" },
+	{ "fcdcca8b2acf76ba2d0006cefa3630a1", "ghosttown", 20687, "Ghost Town" },
+	{ "c8aaa80f07c40fa8e4b17432644919dc", "savageisland1", 22669, "Savage Island, Part 1" },
+	{ "2add0f28d9b236c866890cdf8d86ee60", "savageisland2", 21169, "Savage Island, Part 2" },
+	{ "675126bd0477e8ed9230ad3db5afc45f", "goldenvoyage", 21401, "The Golden Voyage" },
+	{ "0ef0def798d895ed766041fa99dd28a0", "adventure13", 22346, "Adventure 13" },
+	{ "0bf1bcc649422798332a38c88588fdff", "adventure14", 22087, "Adventure 14" },
+	{ "a0a5423967287dae9cbeb9abe8324479", "buckaroobonzai", 21038, "Buckaroo Banzai" },
+	{ nullptr, nullptr, 0, nullptr }
 };
 
 void ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
@@ -71,7 +72,7 @@ void ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 
 			if (p->_filesize) {
 				// Found a match
-				DetectedGame gd("scott", p->_desc, Common::EN_ANY, Common::kPlatformUnknown, "Scott");
+				DetectedGame gd(p->_gameId, p->_desc, Common::EN_ANY, Common::kPlatformUnknown);
 				gd.addExtraEntry("filename", file->getName());
 
 				gameList.push_back(gd);


Commit: d2554a73fb586166c774b7197f8e05d4c85387a2
    https://github.com/scummvm/scummvm/commit/d2554a73fb586166c774b7197f8e05d4c85387a2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix refreshing grid windows when text is changed

Changed paths:
    engines/gargoyle/detection.cpp
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/windows.cpp


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 8c0580a..04218b1 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -128,8 +128,7 @@ bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSupportsListSaves) ||
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSupportsDeleteSave) ||
-		(f == kSavesSupportMetaInfo) ||
-		(f == kSavesSupportThumbnail);
+		(f == kSavesSupportMetaInfo);
 }
 
 bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index d5eccfe..40888b1 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -32,17 +32,19 @@ Events::Events() : _forceClick(false), _currentEvent(nullptr), _timeouts(false),
 	_priorFrameTime(0), _frameCounter(0) {
 }
 
-bool Events::checkForNextFrameCounter() {
+void Events::checkForNextFrameCounter() {
 	// Check for next game frame
 	uint32 milli = g_system->getMillis();
 	if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) {
 		++_frameCounter;
 		_priorFrameTime = milli;
 
-		return true;
+		if (_redraw)
+			g_vm->_windows->redraw();
+		_redraw = false;
+		g_vm->_screen->update();
+		return;
 	}
-
-	return false;
 }
 
 void Events::getEvent(event_t *event, bool polled) {
@@ -106,11 +108,7 @@ void Events::dispatchEvent(Event &ev, bool polled) {
 
 void Events::pollEvents() {
 	Common::Event event;
-
-	if (checkForNextFrameCounter()) {
-		// Update the screen
-		g_vm->_screen->update();
-	}
+	checkForNextFrameCounter();
 
 	do {
 		g_system->getEventManager()->pollEvent(event);
@@ -232,11 +230,7 @@ void Events::waitForPress() {
 	do {
 		g_system->getEventManager()->pollEvent(e);
 		g_system->delayMillis(10);
-
-		if (checkForNextFrameCounter()) {
-			// Update the screen
-			g_vm->_screen->update();
-		}
+		checkForNextFrameCounter();
 	} while (!g_vm->shouldQuit() && e.type != Common::EVENT_KEYDOWN &&
 		e.type != Common::EVENT_LBUTTONDOWN && e.type != Common::EVENT_RBUTTONDOWN &&
 		e.type != Common::EVENT_MBUTTONDOWN);
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 70e7af4..1347200 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -153,11 +153,12 @@ private:
 	bool _timeouts;					///< Timer timeouts flag
 	uint32 _priorFrameTime;			///< Time of prior game frame
 	uint32 _frameCounter;			///< Frame counter
+	bool _redraw;					///< Screen needed redrawing
 private:
 	/**
 	 * Checks for whether it's time for the next game frame
 	 */
-	bool checkForNextFrameCounter();
+	void checkForNextFrameCounter();
 
 	/**
 	 * Dispatches an event
@@ -225,6 +226,11 @@ public:
 	 * Set the total number of frames played
 	 */
 	void Events::setTotalPlayTicks(uint frames) { _frameCounter = frames; }
+
+	/**
+	 * Flags the screen for redrawing
+	 */
+	void redraw() { _redraw = true; }
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 2f0f0b4..53c9093 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -279,16 +279,21 @@ winid_t Glk::glk_window_get_sibling(winid_t win) {
 void Glk::glk_window_clear(winid_t win) {
 	if (!win) {
 		warning("window_clear: invalid ref");
-	} else if (win->_lineRequest || win->_lineRequestUni) {
-		if (g_conf->_safeClicks && _events->_forceClick) {
-			glk_cancel_line_event(win, nullptr);
-			_events->_forceClick = false;
-
-			win->clear();
-		} else {
-			warning("window_clear: window has pending line request");
-			return;
+	} else {
+		if (win->_lineRequest || win->_lineRequestUni) {
+			if (g_conf->_safeClicks && _events->_forceClick) {
+				glk_cancel_line_event(win, nullptr);
+				_events->_forceClick = false;
+
+				win->clear();
+			} else {
+				warning("window_clear: window has pending line request");
+				return;
+			}
 		}
+
+		// Clear the window
+		win->clear();
 	}
 }
 
diff --git a/engines/gargoyle/windows.cpp b/engines/gargoyle/windows.cpp
index b0f1cd7..5bf2b3e 100644
--- a/engines/gargoyle/windows.cpp
+++ b/engines/gargoyle/windows.cpp
@@ -420,7 +420,7 @@ void Windows::redrawRect(const Rect &r) {
 }
 
 void Windows::repaint(const Rect &box) {
-	// No implementation
+	g_vm->_events->redraw();
 }
 
 byte *Windows::rgbShift(byte *rgb) {


Commit: 0ea7f92b67a8ba18c2b0420390db50d0352995b2
    https://github.com/scummvm/scummvm/commit/0ea7f92b67a8ba18c2b0420390db50d0352995b2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Added saveGameState and loadGameState

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


diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 5ec804e..373bf4c 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -127,6 +127,16 @@ public:
 	virtual ~GargoyleEngine();
 
 	/**
+	 * Returns true if a savegame can be loaded
+	 */
+	virtual bool canLoadGameStateCurrently() override { return true; }
+
+	/**
+	 * Returns true if the game can be saved
+	 */
+	virtual bool canSaveGameStateCurrently() override { return true; }
+
+	/**
 	 * Returns the bitset of game features
 	 */
 	uint32 getFeatures() const;
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index a19fb00..6f1b9ef 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -526,19 +526,29 @@ void Scott::lineInput(char *buf, size_t n) {
 }
 
 void Scott::saveGame(void) {
-	strid_t file;
-	frefid_t ref;
-	int ct;
-	Common::String msg;
-
-	ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame, filemode_Write, 0);
-	if (ref == nullptr) return;
+	frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
+		filemode_Write, 0);
+	if (ref == nullptr)
+		return;
 
-	file = glk_stream_open_file(ref, filemode_Write, 0);
+	int slot = ref->_slotNumber;
+	Common::String desc = ref->_description;
 	glk_fileref_destroy(ref);
-	if (file == nullptr) return;
 
-	for (ct = 0; ct < 16; ct++) {
+	saveGameState(slot, desc);
+}
+
+Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
+	Common::String msg;
+	FileReference ref;
+	ref._slotNumber = slot;
+	ref._description = desc;
+
+	strid_t file = glk_stream_open_file(&ref, filemode_Write, 0);
+	if (file == nullptr)
+		return Common::kWritingFailed;
+
+	for (int ct = 0; ct < 16; ct++) {
 		msg = Common::String::format("%d %d\n", Counters[ct], RoomSaved[ct]);
 		glk_put_string_stream(file, msg.c_str());
 	}
@@ -548,29 +558,42 @@ void Scott::saveGame(void) {
 		MyLoc, CurrentCounter, SavedRoom, GameHeader.LightTime);
 	glk_put_string_stream(file, msg.c_str());
 
-	for (ct = 0; ct <= GameHeader.NumItems; ct++) {
+	for (int ct = 0; ct <= GameHeader.NumItems; ct++) {
 		msg = Common::String::format("%hd\n", (short)Items[ct].Location);
 		glk_put_string_stream(file, msg.c_str());
 	}
 
 	glk_stream_close(file, nullptr);
 	output("Saved.\n");
+
+	return Common::kNoError;
 }
 
 void Scott::loadGame(void) {
+	frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
+		filemode_Read, 0);
+	if (ref == nullptr)
+		return;
+
+	int slotNumber = ref->_slotNumber;
+	glk_fileref_destroy(ref);
+
+	loadGameState(slotNumber);
+}
+
+Common::Error Scott::loadGameState(int slot) {
 	strid_t file;
-	frefid_t ref;
 	char buf[128];
 	int ct = 0;
 	short lo;
-	short DarkFlag;
+	short darkFlag;
 
-	ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame, filemode_Read, 0);
-	if (ref == nullptr) return;
+	FileReference ref;
+	ref._slotNumber = slot;
 
-	file = glk_stream_open_file(ref, filemode_Read, 0);
-	glk_fileref_destroy(ref);
-	if (file == nullptr) return;
+	file = glk_stream_open_file(&ref, filemode_Read, 0);
+	if (file == nullptr)
+		return Common::kReadingFailed;
 
 	for (ct = 0; ct<16; ct++) {
 		glk_get_line_stream(file, buf, sizeof buf);
@@ -579,17 +602,19 @@ void Scott::loadGame(void) {
 
 	glk_get_line_stream(file, buf, sizeof buf);
 	sscanf(buf, "%ld %hd %d %d %d %d\n",
-		&BitFlags, &DarkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
+		&BitFlags, &darkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
 		&GameHeader.LightTime);
 
-	/* Backward compatibility */
-	if (DarkFlag)
+	// Backward compatibility
+	if (darkFlag)
 		BitFlags |= (1 << 15);
 	for (ct = 0; ct <= GameHeader.NumItems; ct++) {
 		glk_get_line_stream(file, buf, sizeof buf);
 		sscanf(buf, "%hd\n", &lo);
 		Items[ct].Location = (unsigned char)lo;
 	}
+
+	return Common::kNoError;
 }
 
 int Scott::getInput(int *vb, int *no) {
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
index f2fe769..81847cc 100644
--- a/engines/gargoyle/scott/scott.h
+++ b/engines/gargoyle/scott/scott.h
@@ -172,6 +172,16 @@ public:
 	 * Execute the game
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) override;
+
+	/**
+	 * Load a savegame
+	 */
+	virtual Common::Error loadGameState(int slot) override;
+
+	/**
+	 * Save the game
+	 */
+	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
 };
 
 } // End of namespace Scott


Commit: 2e1192367779901e130bd3181ae3061b81116dee
    https://github.com/scummvm/scummvm/commit/2e1192367779901e130bd3181ae3061b81116dee
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Added startup savegame loading

Changed paths:
    engines/gargoyle/scott/scott.cpp


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 6f1b9ef..f92754f 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -68,6 +68,13 @@ Release 1.14, (c) 1993,1994,1995 Swansea University Computer Society.\n\
 Distributed under the GNU software license\n\n");
 	loadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
 
+	// Check for savegame
+	if (ConfMan.hasKey("save_slot")) {
+		int saveSlot = ConfMan.getInt("save_slot");
+		if (saveSlot >= 0)
+			loadGameState(saveSlot);
+	}
+
 	while (!shouldQuit()) {
 		glk_tick();
 


Commit: 77a4e99c31c6058217b8a55ba5d5d048dc1dea52
    https://github.com/scummvm/scummvm/commit/77a4e99c31c6058217b8a55ba5d5d048dc1dea52
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add savegame listing

Changed paths:
    engines/gargoyle/detection.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/scott/detection.h
    engines/gargoyle/streams.cpp


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 04218b1..7ccb2f6 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -154,7 +154,33 @@ bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const AD
 }
 
 SaveStateList GargoyleMetaEngine::listSaves(const char *target) const {
+	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+	Common::StringArray filenames;
+	Common::String saveDesc;
+	Common::String pattern = Common::String::format("%s.0##", target);
+	Gargoyle::SavegameHeader header;
+
+	filenames = saveFileMan->listSavefiles(pattern);
+
 	SaveStateList saveList;
+	for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+		const char *ext = strrchr(file->c_str(), '.');
+		int slot = ext ? atoi(ext + 1) : -1;
+
+		if (slot >= 0 && slot <= MAX_SAVES) {
+			Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file);
+
+			if (in) {
+				if (Gargoyle::FileStream::readSavegameHeader(in, header))
+					saveList.push_back(SaveStateDescriptor(slot, header._saveName));
+
+				delete in;
+			}
+		}
+	}
+
+	// Sort saves based on slot number.
+	Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
 	return saveList;
 }
 
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 373bf4c..0b8bca7 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -72,15 +72,6 @@ enum GargoyleDebugChannels {
 
 struct GargoyleGameDescription;
 
-struct GargoyleSavegameHeader {
-	uint8 _version;
-	Common::String _saveName;
-	Graphics::Surface *_thumbnail;
-	int _year, _month, _day;
-	int _hour, _minute;
-	int _totalFrames;
-};
-
 /**
  * Base class for the different interpreters
  */
diff --git a/engines/gargoyle/scott/detection.h b/engines/gargoyle/scott/detection.h
index 07f1476..e2aa93e 100644
--- a/engines/gargoyle/scott/detection.h
+++ b/engines/gargoyle/scott/detection.h
@@ -31,6 +31,9 @@ namespace Scott {
 
 class ScottMetaEngine {
 public:
+	/**
+	 * Detect Scott Adams games
+	 */
 	static void detectGames(const Common::FSList &fslist, DetectedGames &gameList);
 };
 
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index a8c85a1..0dc82ed 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -536,6 +536,9 @@ FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 roc
 			SavegameHeader header;
 			if (!readSavegameHeader(_inStream, header))
 				error("Invalid savegame");
+			if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage()
+				|| header._md5 != g_vm->getGameMD5())
+				error("Savegame is for a different game");
 
 			g_vm->_events->setTotalPlayTicks(header._totalFrames);
 		}
@@ -989,10 +992,6 @@ bool FileStream::readSavegameHeader(Common::SeekableReadStream *stream, Savegame
 	header._language = stream->readByte();
 	header._md5 = readString(stream);
 
-	if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage()
-			|| header._md5 != g_vm->getGameMD5())
-		return false;
-
 	// Read in name
 	header._saveName = readString(stream);
 


Commit: 9bd7bc87b6bc33321298ddeba6fa490c8a34d527
    https://github.com/scummvm/scummvm/commit/9bd7bc87b6bc33321298ddeba6fa490c8a34d527
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added savegame meta info retrieval

Changed paths:
    engines/gargoyle/detection.cpp


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 7ccb2f6..295e50b 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -125,10 +125,13 @@ public:
 
 bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
-	    (f == kSupportsListSaves) ||
+		(f == kSupportsListSaves) ||
 		(f == kSupportsLoadingDuringStartup) ||
 		(f == kSupportsDeleteSave) ||
-		(f == kSavesSupportMetaInfo);
+		(f == kSavesSupportMetaInfo) ||
+		(f == kSavesSupportCreationDate) ||
+		(f == kSavesSupportPlayTime) ||
+		(f == kSimpleSavesNames);
 }
 
 bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
@@ -192,6 +195,23 @@ void GargoyleMetaEngine::removeSaveState(const char *target, int slot) const {
 }
 
 SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+	Common::String filename = Common::String::format("%s.%03d", target, slot);
+	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
+
+	if (in) {
+		Gargoyle::SavegameHeader header;
+		if (Gargoyle::FileStream::readSavegameHeader(in, header)) {
+			// Create the return descriptor
+			SaveStateDescriptor desc(slot, header._saveName);
+			desc.setSaveDate(header._year, header._month, header._day);
+			desc.setSaveTime(header._hour, header._minute);
+			desc.setPlayTime(header._totalFrames * GAME_FRAME_TIME);
+
+			delete in;
+			return desc;
+		}
+	}
+
 	return SaveStateDescriptor();
 }
 


Commit: db61f4e0500c96503fbb69a78d7e7aafb2d71300
    https://github.com/scummvm/scummvm/commit/db61f4e0500c96503fbb69a78d7e7aafb2d71300
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add miscellaneous stream methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 53c9093..43b6d0a 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -184,35 +184,35 @@ winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size, glui32 w
 }
 
 void Glk::glk_window_close(winid_t win, stream_result_t *result) {
-	if (!win) {
-		warning("glk_window_close: invalid ref");
-	} else {
+	if (win) {
 		_windows->windowClose(win, result);
+	} else {
+		warning("glk_window_close: invalid ref");
 	}
 }
 
 void Glk::glk_window_get_size(winid_t win, glui32 *width, glui32 *height) {
-	if (!win) {
-		warning("window_get_size: invalid ref");
-	} else {
+	if (win) {
 		win->getSize(width, height);
+	} else {
+		warning("window_get_size: invalid ref");
 	}
 }
 
 void Glk::glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin) {
-	if (!win) {
-		warning("window_set_arrangement: invalid ref");
-	} else {
+	if (win) {
 		win->setArrangement(method, size, keywin);
+	} else {
+		warning("window_set_arrangement: invalid ref");
 	}
 }
 
 void Glk::glk_window_get_arrangement(winid_t win, glui32 *method,
 		glui32 *size, winid_t *keyWin) {
-	if (!win) {
-		warning("window_get_arrangement: invalid ref");
-	} else {
+	if (win) {
 		win->getArrangement(method, size, keyWin);
+	} else {
+		warning("window_get_arrangement: invalid ref");
 	}
 }
 
@@ -232,21 +232,21 @@ winid_t Glk::glk_window_iterate(winid_t win, glui32 *rock) {
 }
 
 glui32 Glk::glk_window_get_rock(winid_t win) {
-	if (!win) {
+	if (win) {
+		return win->_rock;
+	} else {
 		warning("window_get_rock: invalid ref.");
 		return 0;
 	}
-
-	return win->_rock;
 }
 
 glui32 Glk::glk_window_get_type(winid_t win) {
-	if (!win) {
+	if (win) {
+		return win->_type;
+	} else {
 		warning("window_get_parent: invalid ref");
 		return 0;
 	}
-
-	return win->_type;
 }
 
 winid_t Glk::glk_window_get_parent(winid_t win) {
@@ -298,27 +298,27 @@ void Glk::glk_window_clear(winid_t win) {
 }
 
 void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
-	if (!win) {
-		warning("window_move_cursor: invalid ref");
-	} else {
+	if (win) {
 		win->moveCursor(Point(xpos, ypos));
+	} else {
+		warning("window_move_cursor: invalid ref");
 	}
 }
 
 strid_t Glk::glk_window_get_stream(winid_t win) {
-	if (!win) {
+	if (win) {
+		return win->_stream;
+	} else {
 		warning("window_get_stream: invalid ref");
 		return nullptr;
 	}
-
-	return win->_stream;
 }
 
 void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
-	if (!win) {
-		warning("window_set_echo_stream: invalid window id");
-	} else {
+	if (win) {
 		win->_echoStream = str;
+	} else {
+		warning("window_set_echo_stream: invalid window id");
 	}
 }
 
@@ -361,19 +361,19 @@ glui32 Glk::glk_stream_get_rock(strid_t str) const {
 }
 
 void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode) {
-	if (!str) {
-		warning("stream_set_position: invalid ref");
-	} else {
+	if (str) {
 		str->setPosition(pos, seekMode);
+	} else {
+		warning("stream_set_position: invalid ref");
 	}
 }
 
 glui32 Glk::glk_stream_get_position(strid_t str) const {
-	if (!str) {
+	if (str) {
+		return str->getPosition();
+	} else {
 		warning("stream_get_position: invalid ref");
 		return 0;
-	} else {
-		return str->getPosition();
 	}
 }
 
@@ -427,21 +427,29 @@ void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
 
 glsi32 Glk::glk_get_char_stream(strid_t str) {
 	if (str) {
+		return str->getChar();
+	} else {
 		warning("get_char_stream: invalid ref");
 		return -1;
-	} else {
-		return str->getChar();
 	}
 }
 
 glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
-	// TODO
-	return 0;
+	if (str) {
+		return str->getLine(buf, len);
+	} else {
+		warning("get_line_stream: invalid ref");
+		return 0;
+	}
 }
 
 glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
-	// TODO
-	return 0;
+	if (str) {
+		return str->getBuffer(buf, len);
+	} else {
+		warning("get_line_stream: invalid ref");
+		return 0;
+	}
 }
 
 void Glk::glk_stylehint_set(glui32 wintype, glui32 style, glui32 hint, glsi32 val) {
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 0dc82ed..24917c5 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -400,6 +400,63 @@ glsi32 MemoryStream::getCharUni() {
 	}
 }
 
+glui32 MemoryStream::getBuffer(char *buf, glui32 len) {
+	if (!_readable)
+		return 0;
+
+	if (_bufPtr >= _bufEnd) {
+		len = 0;
+	} else {
+		if (!_unicode) {
+			unsigned char *bp = (unsigned char *)_bufPtr;
+			if (bp + len > (unsigned char *)_bufEnd) {
+				glui32 lx;
+				lx = (bp + len) - (unsigned char *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+
+			if (len) {
+				memcpy(buf, bp, len);
+				bp += len;
+				if (bp >(unsigned char *)_bufEof)
+					_bufEof = bp;
+			}
+
+			_readCount += len;
+			_bufPtr = bp;
+		} else {
+			glui32 *bp = (glui32 *)_bufPtr;
+			if (bp + len > (glui32 *)_bufEnd) {
+				glui32 lx;
+				lx = (bp + len) - (glui32 *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+			if (len) {
+				glui32 i;
+				for (i = 0; i < len; i++) {
+					glui32 ch = *bp++;
+					if (ch > 0xff)
+						ch = '?';
+					*buf++ = (char)ch;
+				}
+				if (bp > (glui32 *)_bufEof)
+					_bufEof = bp;
+			}
+
+			_readCount += len;
+			_bufPtr = bp;
+		}
+	}
+
+	return len;
+}
+
 glui32 MemoryStream::getBufferUni(glui32 *buf, glui32 len) {
 	if (!_readable)
 		return 0;
@@ -452,6 +509,66 @@ glui32 MemoryStream::getBufferUni(glui32 *buf, glui32 len) {
 	return len;
 }
 
+glui32 MemoryStream::getLine(char *buf, glui32 len) {
+	glui32 lx;
+	bool gotNewline;
+
+	if (len == 0)
+		return 0;
+
+	len -= 1; /* for the terminal null */
+	if (!_unicode) {
+		if (_bufPtr >= _bufEnd) {
+			len = 0;
+		} else {
+			if ((char *)_bufPtr + len > (char *)_bufEnd) {
+				lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+		}
+
+		gotNewline = false;
+		for (lx = 0; lx < len && !gotNewline; lx++) {
+			buf[lx] = ((char *)_bufPtr)[lx];
+			gotNewline = (buf[lx] == '\n');
+		}
+
+		buf[lx] = '\0';
+		_bufPtr = ((char *)_bufPtr) + lx;
+	} else {
+		if (_bufPtr >= _bufEnd) {
+			len = 0;
+		} else {
+			if ((char *)_bufPtr + len > (char *)_bufEnd) {
+				lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
+				if (lx < len)
+					len -= lx;
+				else
+					len = 0;
+			}
+		}
+
+		gotNewline = false;
+		for (lx = 0; lx < len && !gotNewline; lx++) {
+			glui32 ch;
+			ch = ((glui32 *)_bufPtr)[lx];
+			if (ch >= 0x100)
+				ch = '?';
+			buf[lx] = (char)ch;
+			gotNewline = (ch == '\n');
+		}
+
+		buf[lx] = '\0';
+		_bufPtr = ((glui32 *)_bufPtr) + lx;
+	}
+
+	_readCount += lx;
+	return lx;
+}
+
 glui32 MemoryStream::getLineUni(glui32 *ubuf, glui32 len) {
 	bool gotNewline;
 	int lx;
@@ -509,8 +626,8 @@ glui32 MemoryStream::getLineUni(glui32 *ubuf, glui32 len) {
 /*--------------------------------------------------------------------------*/
 
 FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 rock, bool unicode) :
-		Stream(streams, true, false, rock, unicode), _lastOp(0), _textFile(false),
-		_inFile(nullptr), _outFile(nullptr), _inStream(nullptr) {
+		Stream(streams, fmode == filemode_Read, fmode != filemode_Read, rock, unicode),  _lastOp(0),
+		_textFile(fref->_textMode), _inFile(nullptr), _outFile(nullptr), _inStream(nullptr) {
 	Common::String fname = fref->_slotNumber == -1 ? fref->_filename : fref->getSaveName();
 
 	if (fmode == filemode_Write || fmode == filemode_ReadWrite || fmode == filemode_WriteAppend) {
@@ -839,6 +956,56 @@ glsi32 FileStream::getCharUni() {
 	}
 }
 
+glui32 FileStream::getBuffer(char *buf, glui32 len) {
+	ensureOp(filemode_Read);
+	if (!_unicode) {
+		glui32 res;
+		res = _inStream->read(buf, len);
+		_readCount += res;
+		return res;
+	} else if (_textFile) {
+		glui32 lx;
+		for (lx = 0; lx<len; lx++) {
+			glui32 ch;
+			ch = getCharUtf8();
+			if (ch == -1)
+				break;
+			_readCount++;
+			if (ch >= 0x100)
+				ch = '?';
+			buf[lx] = (char)ch;
+		}
+		return lx;
+	} else {
+		glui32 lx;
+		for (lx = 0; lx < len; lx++) {
+			int res;
+			glui32 ch;
+			res = _inStream->readByte();
+			if (res == -1)
+				break;
+			ch = (res & 0xFF);
+			res = _inStream->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			res = _inStream->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			res = _inStream->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			_readCount++;
+			if (ch >= 0x100)
+				ch = '?';
+			buf[lx] = (char)ch;
+		}
+		return lx;
+	}
+}
+
 glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 	if (!_readable)
 		return 0;
@@ -897,6 +1064,76 @@ glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 	}
 }
 
+glui32 FileStream::getLine(char *buf, glui32 len) {
+	glui32 lx;
+	bool gotNewline;
+
+	if (len == 0)
+		return 0;
+
+	ensureOp(filemode_Read);
+	if (!_unicode) {
+		char *res = buf;
+		for (; len > 0; ++res, --len) {
+			*res = _inStream->readByte();
+			if (*res == '\n')
+				break;
+		}
+		*res = '\0';
+
+		lx = strlen(buf);
+		_readCount += lx;
+		return lx;
+	} else if (_textFile) {
+		len -= 1; // for the terminal null
+		gotNewline = false;
+		for (lx = 0; lx < len && !gotNewline; lx++) {
+			glui32 ch;
+			ch = getCharUtf8();
+			if (ch == -1)
+				break;
+			_readCount++;
+			if (ch >= 0x100)
+				ch = '?';
+			buf[lx] = (char)ch;
+			gotNewline = (ch == '\n');
+		}
+		buf[lx] = '\0';
+		return lx;
+	} else {
+		len -= 1; // for the terminal null
+		gotNewline = false;
+		for (lx = 0; lx < len && !gotNewline; lx++) {
+			int res;
+			glui32 ch;
+			res = _inStream->readByte();
+			if (res == -1)
+				break;
+			ch = (res & 0xFF);
+			res = _inStream->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			res = _inStream->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			res = _inStream->readByte();
+			if (res == -1)
+				break;
+			ch = (ch << 8) | (res & 0xFF);
+			_readCount++;
+			if (ch >= 0x100)
+				ch = '?';
+			buf[lx] = (char)ch;
+			gotNewline = (ch == '\n');
+		}
+
+		buf[lx] = '\0';
+		return lx;
+	}
+}
+
 glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
 	bool gotNewline;
 	int lx;
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index dad6781..16f63db 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -101,6 +101,13 @@ struct FileReference {
 	FileReference() : _rock(0), _slotNumber(-1), _fileType(fileusage_Data), _textMode(false) {}
 
 	/**
+	 * Constructor
+	 */
+	FileReference(int slot, const Common::String &desc, glui32 usage, glui32 rock = 0) :
+		_rock(rock), _slotNumber(slot), _description(desc),
+		_fileType((FileUsage)(usage & fileusage_TypeMask)), _textMode(usage & fileusage_TextMode) {}
+
+	/**
 	 * Get savegame filename
 	 */
 	const Common::String getSaveName() const;
@@ -217,11 +224,21 @@ public:
 	virtual glsi32 getCharUni() { return -1; }
 
 	/**
+	 * Get a buffer
+	 */
+	virtual glui32 getBuffer(char *buf, glui32 len) { return 0; }
+
+	/**
 	 * Get a unicode buffer
 	 */
 	virtual glui32 getBufferUni(glui32 *buf, glui32 len) { return 0; }
 
 	/**
+	 * Get a line
+	 */
+	virtual glui32 getLine(char *buf, glui32 len) { return 0; }
+
+	/**
 	 * Get a unicode line
 	 */
 	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) { return 0; }
@@ -330,11 +347,21 @@ public:
 	virtual glsi32 getCharUni() override;
 
 	/**
+	 * Get a buffer
+	 */
+	virtual glui32 getBuffer(char *buf, glui32 len) override;
+
+	/**
 	 * Get a unicode buffer
 	 */
 	virtual glui32 getBufferUni(glui32 *buf, glui32 len) override;
 
 	/**
+	 * Get a line
+	 */
+	virtual glui32 getLine(char *buf, glui32 len) override;
+
+	/**
 	 * Get a unicode line
 	 */
 	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) override;
@@ -422,11 +449,21 @@ public:
 	virtual glsi32 getCharUni() override;
 
 	/**
+	 * Get a buffer
+	 */
+	virtual glui32 getBuffer(char *buf, glui32 len) override;
+
+	/**
 	 * Get a unicode buffer
 	 */
 	virtual glui32 getBufferUni(glui32 *buf, glui32 len) override;
 
 	/**
+	 * Get a line
+	 */
+	virtual glui32 getLine(char *buf, glui32 len) override;
+
+	/**
 	 * Get a unicode line
 	 */
 	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) override;


Commit: 75f1ac769ba8158ff73f724a91e9544b29943905
    https://github.com/scummvm/scummvm/commit/75f1ac769ba8158ff73f724a91e9544b29943905
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Fix loading and saving games

Changed paths:
    engines/gargoyle/scott/scott.cpp


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index f92754f..b0c3444 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -37,6 +37,7 @@ Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst,
 
 void Scott::runGame(Common::SeekableReadStream *gameFile) {
 	int vb, no;
+	int saveSlot;
 	initialize();
 
 	Bottom = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
@@ -62,24 +63,26 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 		Top = Bottom;
 	}
 
+	// Check for savegame
+	saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
+
 	output("\
 Scott Free, A Scott Adams game driver in C.\n\
 Release 1.14, (c) 1993,1994,1995 Swansea University Computer Society.\n\
 Distributed under the GNU software license\n\n");
 	loadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
 
-	// Check for savegame
-	if (ConfMan.hasKey("save_slot")) {
-		int saveSlot = ConfMan.getInt("save_slot");
-		if (saveSlot >= 0)
-			loadGameState(saveSlot);
-	}
-
 	while (!shouldQuit()) {
 		glk_tick();
 
 		performActions(0, 0);
 
+		if (saveSlot >= 0) {
+			// Load any savegame during startup
+			loadGameState(saveSlot);
+			saveSlot = -1;
+		}
+
 		look();
 
 		if (getInput(&vb, &no) == -1)
@@ -547,9 +550,7 @@ void Scott::saveGame(void) {
 
 Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
 	Common::String msg;
-	FileReference ref;
-	ref._slotNumber = slot;
-	ref._description = desc;
+	FileReference ref(slot, desc, fileusage_TextMode | fileusage_SavedGame);
 
 	strid_t file = glk_stream_open_file(&ref, filemode_Write, 0);
 	if (file == nullptr)
@@ -595,8 +596,7 @@ Common::Error Scott::loadGameState(int slot) {
 	short lo;
 	short darkFlag;
 
-	FileReference ref;
-	ref._slotNumber = slot;
+	FileReference ref(slot, "", fileusage_SavedGame | fileusage_TextMode);
 
 	file = glk_stream_open_file(&ref, filemode_Read, 0);
 	if (file == nullptr)


Commit: 6b39263dab79ab20118bc2bb17677a0c5b14e84c
    https://github.com/scummvm/scummvm/commit/6b39263dab79ab20118bc2bb17677a0c5b14e84c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Workaround to show caret for second line entry onwards

Not sure how original GLK code properly re-renders the caret,
since the line isn't marked as dirty once input starts.
This workaround marks the line as dirty when a line request is
done, which allows the line to be redrawn with the caret

Changed paths:
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 61cf92a..9b32393 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -637,6 +637,9 @@ void TextBufferWindow::requestLineEvent(char *buf, glui32 maxlen, glui32 initlen
 		putText(buf, initlen, _inCurs, 0);
 	}
 
+	// WORKAROUND: Mark bottom line as dirty so caret will be drawn
+	_lines[0]._dirty = true;
+
 	_echoLineInput = _echoLineInputBase;
 
 	if (_lineTerminatorsBase && _termCt) {
@@ -690,6 +693,9 @@ void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 in
 		putTextUni(buf, initlen, _inCurs, 0);
 	}
 
+	// WORKAROUND: Mark bottom line as dirty so caret will be drawn
+	_lines[0]._dirty = true;
+
 	_echoLineInput = _echoLineInputBase;
 
 	if (_lineTerminatorsBase && _termCt) {


Commit: 135c3adc94fdb29241b8bf6b4828a68136ca4a40
    https://github.com/scummvm/scummvm/commit/135c3adc94fdb29241b8bf6b4828a68136ca4a40
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: In progress adding mouse cursors

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 40888b1..db5d8a6 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -22,14 +22,75 @@
 
 #include "gargoyle/events.h"
 #include "gargoyle/clipboard.h"
+#include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/screen.h"
 #include "gargoyle/windows.h"
+#include "graphics/cursorman.h"
 
 namespace Gargoyle {
 
+const byte ARROW[] = {
+	// byte 1: number of skipped pixels
+	// byte 2: number of plotted pixels
+	// then, pixels
+	0, 2, 0xF7, 5,
+	0, 3, 0xF7, 0xF7, 5,
+	0, 3, 0xF7, 0xF7, 5,
+	0, 4, 0xF7, 0xF7, 0xF7, 5,
+	0, 4, 0xF7, 0xF7, 0xF7, 5,
+	0, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	2, 3, 0xF7, 0xF7, 5,
+	3, 3, 0xF7, 0xF7, 5,
+	3, 3, 0xF7, 0xF7, 5,
+	4, 2, 0xF7, 5
+};
+
 Events::Events() : _forceClick(false), _currentEvent(nullptr), _timeouts(false),
-	_priorFrameTime(0), _frameCounter(0) {
+		_priorFrameTime(0), _frameCounter(0), _cursorId(CURSOR_NONE) {
+	initializeCursors();
+}
+
+Events::~Events() {
+	for (int idx = 1; idx < 3; ++idx)
+		_cursors[idx].free();
+}
+
+void Events::initializeCursors() {
+	const Graphics::PixelFormat format = g_system->getScreenFormat();
+	const int WHITE = format.RGBToColor(0xff, 0xff, 0xff);
+	const int BLACK = 0;
+	const int TRANSPARENT = format.RGBToColor(0x80, 0x80, 0x80);
+
+	// Setup arrow cursor
+	Surface &arr = _cursors[CURSOR_ARROW];
+	arr.create(8, 16, g_system->getScreenFormat());
+	arr.fillRect(Common::Rect(0, 0, 8, 16), TRANSPARENT);
+
+	const byte *p = ARROW;
+	for (int y = 0; y < 16; ++y) {
+		int offset = *p++;
+		int len = *p++;
+
+		for (int x = offset; x < (offset  + len); ++x, ++p) {
+			arr.hLine(x, y, x, (*p == 0xf7) ? WHITE : BLACK);
+		}
+	}
+
+	// Setup selection cusor sized to the vertical line size
+	Surface &sel = _cursors[CURSOR_SELECTION];
+	sel.create(5, g_conf->_leading, g_system->getScreenFormat());
+	sel.fillRect(Common::Rect(0, 0, sel.w, sel.h), TRANSPARENT);
+	sel.hLine(0, 0, 4, 0);
+	sel.hLine(0, sel.h - 1, 4, 0);
+	sel.vLine(2, 1, sel.h - 1, 0);
+	sel._hotspot = Common::Point(2, sel.h - 1);
 }
 
 void Events::checkForNextFrameCounter() {
@@ -115,14 +176,25 @@ void Events::pollEvents() {
 
 		switch (event.type) {
 		case Common::EVENT_KEYDOWN:
+			setCursor(CURSOR_NONE);
 			handleKeyDown(event.kbd);
 			return;
+
+		case Common::EVENT_LBUTTONDOWN:
+		case Common::EVENT_RBUTTONDOWN:
+			// TODO
+			return;
+
 		case Common::EVENT_WHEELUP:
 		case Common::EVENT_WHEELDOWN:
+			setCursor(CURSOR_NONE);
 			handleScroll(event.type == Common::EVENT_WHEELUP);
 			return;
-		case Common::EVENT_LBUTTONDOWN:
-		case Common::EVENT_RBUTTONDOWN:
+
+		case Common::EVENT_MOUSEMOVE:
+			if (_cursorId == CURSOR_NONE)
+				setCursor(CURSOR_ARROW);
+			break;
 
 		default:
 			break;
@@ -236,4 +308,22 @@ void Events::waitForPress() {
 		e.type != Common::EVENT_MBUTTONDOWN);
 }
 
+void Events::setCursor(CursorId cursorId) {
+	if (cursorId != _cursorId) {
+		if (cursorId == CURSOR_NONE) {
+			CursorMan.showMouse(false);
+		} else {
+			if (!CursorMan.isVisible())
+				CursorMan.showMouse(true);
+
+			const Surface &s = _cursors[cursorId];
+			const int TRANSPARENT = s.format.RGBToColor(0x80, 0x80, 0x80);
+
+			CursorMan.replaceCursor(s.getPixels(), s.w, s.h, s._hotspot.x, s._hotspot.y, TRANSPARENT, true, &s.format);
+		}
+
+		_cursorId = cursorId;
+	}
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 1347200..5406e3c 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -24,6 +24,7 @@
 #define GARGOYLE_EVENTS_H
 
 #include "common/events.h"
+#include "graphics/surface.h"
 #include "gargoyle/utils.h"
 
 namespace Gargoyle {
@@ -93,6 +94,12 @@ enum Keycode {
 	keycode_MAXVAL = 28U
 };
 
+enum CursorId {
+	CURSOR_NONE = 0,
+	CURSOR_ARROW = 1,
+	CURSOR_SELECTION = 2
+};
+
 /**
  * Event structure
  */
@@ -146,6 +153,9 @@ public:
  * Events manager
  */
 class Events {
+	struct Surface : public Graphics::Surface {
+		Common::Point _hotspot;
+	};
 private:
 	EventQueue _eventsPolled;		///< User generated events
 	EventQueue _eventsLogged;		///< Custom events generated by game code
@@ -154,8 +164,15 @@ private:
 	uint32 _priorFrameTime;			///< Time of prior game frame
 	uint32 _frameCounter;			///< Frame counter
 	bool _redraw;					///< Screen needed redrawing
+	CursorId _cursorId;				///< Current cursor Id
+	Surface _cursors[3];			///< Cursor pixel data
 private:
 	/**
+	 * Initialize the cursor graphics
+	 */
+	void initializeCursors();
+
+	/**
 	 * Checks for whether it's time for the next game frame
 	 */
 	void checkForNextFrameCounter();
@@ -203,6 +220,11 @@ public:
 	Events();
 
 	/**
+	 * Destructor
+	 */
+	~Events();
+
+	/**
 	  * Get any pending event
 	  */
 	void getEvent(event_t *event, bool polled);
@@ -231,6 +253,11 @@ public:
 	 * Flags the screen for redrawing
 	 */
 	void redraw() { _redraw = true; }
+
+	/**
+	 * Sets the current cursor
+	 */
+	void setCursor(CursorId cursorId);
 };
 
 } // End of namespace Gargoyle


Commit: c5234c9f727a23c435f03c0cbab85e0fd88e5a94
    https://github.com/scummvm/scummvm/commit/c5234c9f727a23c435f03c0cbab85e0fd88e5a94
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix display of mouse cursor

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index db5d8a6..4c5fb10 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -34,22 +34,23 @@ const byte ARROW[] = {
 	// byte 1: number of skipped pixels
 	// byte 2: number of plotted pixels
 	// then, pixels
-	0, 2, 0xF7, 5,
-	0, 3, 0xF7, 0xF7, 5,
-	0, 3, 0xF7, 0xF7, 5,
-	0, 4, 0xF7, 0xF7, 0xF7, 5,
-	0, 4, 0xF7, 0xF7, 0xF7, 5,
-	0, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	2, 3, 0xF7, 0xF7, 5,
-	3, 3, 0xF7, 0xF7, 5,
-	3, 3, 0xF7, 0xF7, 5,
-	4, 2, 0xF7, 5
+	0, 1, 5,
+	0, 2, 5, 5,
+	0, 3, 5, 0xF7, 5,
+	0, 3, 5, 0xF7, 5,
+	0, 4, 5, 0xF7, 0xF7, 5,
+	0, 4, 5, 0xF7, 0xF7, 5,
+	0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
+	0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
+	0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 7, 5, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
+	0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
+	2, 3, 5, 0xF7, 5,
+	3, 3, 5, 0xF7, 5,
+	3, 3, 5, 0xF7, 5,
+	4, 2, 5, 5
 };
 
 Events::Events() : _forceClick(false), _currentEvent(nullptr), _timeouts(false),
@@ -84,13 +85,15 @@ void Events::initializeCursors() {
 	}
 
 	// Setup selection cusor sized to the vertical line size
-	Surface &sel = _cursors[CURSOR_SELECTION];
+	Surface &sel = _cursors[CURSOR_IBEAM];
 	sel.create(5, g_conf->_leading, g_system->getScreenFormat());
 	sel.fillRect(Common::Rect(0, 0, sel.w, sel.h), TRANSPARENT);
 	sel.hLine(0, 0, 4, 0);
 	sel.hLine(0, sel.h - 1, 4, 0);
 	sel.vLine(2, 1, sel.h - 1, 0);
 	sel._hotspot = Common::Point(2, sel.h - 1);
+
+	// TODO: Hyperlink hand cursor
 }
 
 void Events::checkForNextFrameCounter() {
@@ -169,9 +172,9 @@ void Events::dispatchEvent(Event &ev, bool polled) {
 
 void Events::pollEvents() {
 	Common::Event event;
-	checkForNextFrameCounter();
 
 	do {
+		checkForNextFrameCounter();
 		g_system->getEventManager()->pollEvent(event);
 
 		switch (event.type) {
@@ -192,8 +195,7 @@ void Events::pollEvents() {
 			return;
 
 		case Common::EVENT_MOUSEMOVE:
-			if (_cursorId == CURSOR_NONE)
-				setCursor(CURSOR_ARROW);
+			handleMouseMove(event.mouse);
 			break;
 
 		default:
@@ -267,6 +269,9 @@ void Events::handleScroll(bool wheelUp) {
 }
 
 void Events::handleMouseMove(const Point &pos) {
+	if (_cursorId == CURSOR_NONE)
+		setCursor(CURSOR_ARROW);
+
 	// hyperlinks and selection
 	// TODO: Properly handle commented out lines
 	if (g_vm->_copySelect) {
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 5406e3c..5da4e53 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -94,10 +94,14 @@ enum Keycode {
 	keycode_MAXVAL = 28U
 };
 
+/**
+ * List of cursors
+ */
 enum CursorId {
 	CURSOR_NONE = 0,
 	CURSOR_ARROW = 1,
-	CURSOR_SELECTION = 2
+	CURSOR_IBEAM = 2,
+	CURSOR_HAND = 3
 };
 
 /**
@@ -165,7 +169,7 @@ private:
 	uint32 _frameCounter;			///< Frame counter
 	bool _redraw;					///< Screen needed redrawing
 	CursorId _cursorId;				///< Current cursor Id
-	Surface _cursors[3];			///< Cursor pixel data
+	Surface _cursors[4];			///< Cursor pixel data
 private:
 	/**
 	 * Initialize the cursor graphics


Commit: 8e1be3a95c7395c83271e54f505ca10cc4c4c531
    https://github.com/scummvm/scummvm/commit/8e1be3a95c7395c83271e54f505ca10cc4c4c531
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Text selection highlighting is now working

Changed paths:
    engines/gargoyle/clipboard.h
    engines/gargoyle/events.cpp
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_graphics.h
    engines/gargoyle/window_mask.cpp
    engines/gargoyle/window_mask.h
    engines/gargoyle/window_pair.cpp
    engines/gargoyle/window_pair.h
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/clipboard.h b/engines/gargoyle/clipboard.h
index 23e7aaf..18519c7 100644
--- a/engines/gargoyle/clipboard.h
+++ b/engines/gargoyle/clipboard.h
@@ -29,6 +29,9 @@ namespace Gargoyle {
 
 enum ClipSource { PRIMARY = 0, CLIPBOARD = 1 };
 
+/**
+ * Handles selection of text, and copying to and from the clipboard
+ */
 class Clipboard {
 private:
 	Common::Array<uint32> _text;
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 4c5fb10..7d6bf39 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -185,9 +185,13 @@ void Events::pollEvents() {
 
 		case Common::EVENT_LBUTTONDOWN:
 		case Common::EVENT_RBUTTONDOWN:
-			// TODO
+			handleButtonDown(event.type == Common::EVENT_LBUTTONDOWN, event.mouse);
 			return;
 
+		case Common::EVENT_LBUTTONUP:
+		case Common::EVENT_RBUTTONUP:
+			handleButtonUp(event.type == Common::EVENT_LBUTTONUP, event.mouse);
+
 		case Common::EVENT_WHEELUP:
 		case Common::EVENT_WHEELDOWN:
 			setCursor(CURSOR_NONE);
@@ -287,16 +291,18 @@ void Events::handleMouseMove(const Point &pos) {
 }
 
 void Events::handleButtonDown(bool isLeft, const Point &pos) {
-	if (isLeft)
+	if (isLeft) {
+		setCursor(CURSOR_IBEAM);
 		g_vm->_windows->inputHandleClick(pos);
-	else
+	} else {
 		g_vm->_clipboard->receive(PRIMARY);
+	}
 }
 
 void Events::handleButtonUp(bool isLeft, const Point &pos) {
 	if (isLeft) {
+		setCursor(CURSOR_ARROW);
 		g_vm->_copySelect = false;
-		//gdk_window_set_cursor((GTK_WIDGET(widget)->window), nullptr);
 		g_vm->_clipboard->send(PRIMARY);
 	}
 }
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index b31d620..3286876 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "gargoyle/window_graphics.h"
+#include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/screen.h"
 
@@ -247,4 +248,25 @@ void GraphicsWindow::setBackgroundColor(glui32 color) {
 	_bgnd[2] = (color >> 0) & 0xff;
 }
 
+void GraphicsWindow::click(const Point &newPos) {
+	Point diff = newPos - Point(_bbox.left, _bbox.top);
+
+	if (_mouseRequest) {
+		g_vm->_events->store(evtype_MouseInput, this, diff.x, diff.y);
+		_mouseRequest = false;
+		if (g_conf->_safeClicks)
+			g_vm->_events->_forceClick = true;
+	}
+
+	if (_hyperRequest) {
+		glui32 linkval = g_vm->_windowMask->getHyperlink(newPos);
+		if (linkval) {
+			g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
+			_hyperRequest = false;
+			if (g_conf->_safeClicks)
+				g_vm->_events->_forceClick = 1;
+		}
+	}
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_graphics.h b/engines/gargoyle/window_graphics.h
index f271444..ebcf45b 100644
--- a/engines/gargoyle/window_graphics.h
+++ b/engines/gargoyle/window_graphics.h
@@ -68,6 +68,11 @@ public:
 	}
 
 	/**
+	 * Click the window
+	 */
+	virtual void click(const Point &newPos) override;
+
+	/**
 	 * Cancel a mouse event
 	 */
 	virtual void cancelMouseEvent() override { _mouseRequest = false; }
diff --git a/engines/gargoyle/window_mask.cpp b/engines/gargoyle/window_mask.cpp
index 6c85789..495abc6 100644
--- a/engines/gargoyle/window_mask.cpp
+++ b/engines/gargoyle/window_mask.cpp
@@ -28,11 +28,8 @@
 
 namespace Gargoyle {
 
-int WindowMask::_lastX;
-int WindowMask::_lastY;
-
 WindowMask::WindowMask() : _hor(0), _ver(0), _links(nullptr) {
-	_lastX = _lastY = 0;
+	_last.x = _last.y = 0;
 	resize(g_system->getWidth(), g_system->getHeight());
 }
 
@@ -96,15 +93,13 @@ void WindowMask::putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1
 	}
 }
 
-glui32 WindowMask::getHyperlink(const Point &pos) {
+glui32 WindowMask::getHyperlink(const Point &pos) const {
 	if (!_hor || !_ver) {
 		warning("getHyperlink: struct not initialized");
 		return 0;
 	}
 
-	if (pos.x >= (int16)_hor
-		|| pos.y >= (int16)_ver
-		|| !_links[pos.x]) {
+	if (pos.x >= (int16)_hor || pos.y >= (int16)_ver || !_links[pos.x]) {
 		warning("getHyperlink: invalid range given");
 		return 0;
 	}
@@ -123,8 +118,8 @@ void WindowMask::startSelection(const Point &pos) {
 	tx = MIN(pos.x, (int16)_hor);
 	ty = MIN(pos.y, (int16)_ver);
 
-	_select.left = _lastX = tx;
-	_select.top = _lastY = ty;
+	_select.left = _last.x = tx;
+	_select.top = _last.y = ty;
 	_select.right = 0;
 	_select.bottom = 0;
 
@@ -134,7 +129,7 @@ void WindowMask::startSelection(const Point &pos) {
 void WindowMask::moveSelection(const Point &pos) {
 	int tx, ty;
 
-	if (ABS(pos.x - _lastX) < 5 && abs(pos.y - _lastY) < 5)
+	if (ABS(pos.x - _last.x) < 5 && ABS(pos.y - _last.y) < 5)
 		return;
 
 	if (!_hor || !_ver) {
@@ -145,70 +140,35 @@ void WindowMask::moveSelection(const Point &pos) {
 	tx = MIN(pos.x, (int16)_hor);
 	ty = MIN(pos.y, (int16)_ver);
 
-	_select.right = _lastX = tx;
-	_select.bottom = _lastY = ty;
+	_select.right = _last.x = tx;
+	_select.bottom = _last.y = ty;
 
 	g_vm->_windows->selectionChanged();
 }
 
 void WindowMask::clearSelection() {
-	if (_select.left || _select.right
-		|| _select.top || _select.bottom)
+	if (!_select.isEmpty())
 		Windows::_forceRedraw = true;
 
-	_select.left = 0;
-	_select.top = 0;
-	_select.right = 0;
-	_select.bottom = 0;
+	_select = Rect();
 	g_vm->_windows->clearClaimSelect();
 }
 
-int WindowMask::checkSelection(uint x0, uint y0, uint x1, uint y1) {
-	uint cx0, cx1, cy0, cy1;
-
-	cx0 = _select.left < _select.right
-		? _select.left
-		: _select.right;
-
-	cx1 = _select.left < _select.right
-		? _select.right
-		: _select.left;
-
-	cy0 = _select.top < _select.bottom
-		? _select.top
-		: _select.bottom;
-
-	cy1 = _select.top < _select.bottom
-		? _select.bottom
-		: _select.top;
-
-	if (!cx0 || !cx1 || !cy0 || !cy1)
+bool WindowMask::checkSelection(const Rect &r) const {
+	Rect select(MIN(_select.left, _select.right), MAX(_select.left, _select.right),
+		MIN(_select.top, _select.bottom), MAX(_select.top, _select.bottom));
+	if (select.isEmpty())
 		return false;
 
-	if (cx0 >= x0 && cx0 <= x1
-		&& cy0 >= y0 && cy0 <= y1)
-		return true;
-
-	if (cx0 >= x0 && cx0 <= x1
-		&& cy1 >= y0 && cy1 <= y1)
-		return true;
-
-	if (cx1 >= x0 && cx1 <= x1
-		&& cy0 >= y0 && cy0 <= y1)
-		return true;
-
-	if (cx1 >= x0 && cx1 <= x1
-		&& cy1 >= y0 && cy1 <= y1)
-		return true;
-
-	return false;
+	return select.intersects(r);
 }
 
-int WindowMask::getSelection(uint x0, uint y0, uint x1, uint y1, int *rx0, int *rx1) {
+bool WindowMask::getSelection(const Rect &r, int *rx0, int *rx1) const {
 	uint row, upper, lower, above, below;
-	int row_selected, found_left, found_right;
+	bool row_selected, found_left, found_right;
 	int from_right, from_below, is_above, is_below;
 	uint cx0, cx1, cy0, cy1;
+	uint x0 = r.left, y0 = r.top, x1 = r.right, y1 = r.bottom;
 
 	row = (y0 + y1) / 2;
 	upper = row - (row - y0) / 2;
@@ -216,21 +176,10 @@ int WindowMask::getSelection(uint x0, uint y0, uint x1, uint y1, int *rx0, int *
 	above = upper - (g_conf->_leading) / 2;
 	below = lower + (g_conf->_leading) / 2;
 
-	cx0 = _select.left < _select.right
-		? _select.left
-		: _select.right;
-
-	cx1 = _select.left < _select.right
-		? _select.right
-		: _select.left;
-
-	cy0 = _select.top < _select.bottom
-		? _select.top
-		: _select.bottom;
-
-	cy1 = _select.top < _select.bottom
-		? _select.bottom
-		: _select.top;
+	cx0 = MIN(_select.left, _select.right);
+	cx1 = MAX(_select.left, _select.right);
+	cy0 = MIN(_select.top, _select.bottom);
+	cy1 = MAX(_select.top, _select.bottom);
 
 	row_selected = false;
 
diff --git a/engines/gargoyle/window_mask.h b/engines/gargoyle/window_mask.h
index e9030a2..67d68dc 100644
--- a/engines/gargoyle/window_mask.h
+++ b/engines/gargoyle/window_mask.h
@@ -36,8 +36,7 @@ public:
 	size_t _hor, _ver;
 	glui32 **_links;
 	Rect _select;
-
-	static int _lastX, _lastY;
+	Point _last;
 public:
 	/**
 	 * Constructor
@@ -51,17 +50,28 @@ public:
 
 	void putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1);
 
-	glui32 getHyperlink(const Point &pos);
+	glui32 getHyperlink(const Point &pos) const;
 
+	/**
+	 * Start selecting an area of the screen
+	 * @param pos		Position to start selection area at
+	 */
 	void startSelection(const Point &pos);
 
+	/**
+	 * Move the end point of the selection area
+	 * @param pos		Position to end selection area at
+	 */
 	void moveSelection(const Point &pos);
 
 	void clearSelection();
 
-	int checkSelection(uint x0, uint y0, uint x1, uint y1);
+	/**
+	 * Checks whether the passed area intersects the selection area
+	 */
+	bool checkSelection(const Rect &r) const;
 
-	int getSelection(uint x0, uint y0, uint x1, uint y1, int *rx0, int *rx1);
+	bool getSelection(const Rect &r, int *rx0, int *rx1) const;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_pair.cpp b/engines/gargoyle/window_pair.cpp
index 26903cd..33914c5 100644
--- a/engines/gargoyle/window_pair.cpp
+++ b/engines/gargoyle/window_pair.cpp
@@ -225,4 +225,12 @@ void PairWindow::setArrangement(glui32 method, glui32 size, Window *keyWin) {
 	_windows->rearrange();
 }
 
+void PairWindow::click(const Point &newPos) {
+	if (_child1->_bbox.contains(newPos))
+		_child1->click(newPos);
+
+	if (_child2->_bbox.contains(newPos))
+		_child2->click(newPos);
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_pair.h b/engines/gargoyle/window_pair.h
index a28f7fd0..a5b9081 100644
--- a/engines/gargoyle/window_pair.h
+++ b/engines/gargoyle/window_pair.h
@@ -61,6 +61,11 @@ public:
 	virtual void getArrangement(glui32 *method, glui32 *size, Window **keyWin) override;
 
 	virtual void setArrangement(glui32 method, glui32 size, Window *keyWin) override;
+
+	/**
+	 * Click the window
+	 */
+	virtual void click(const Point &newPos) override;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index 9b32393..a8ac85e 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -793,7 +793,8 @@ void TextBufferWindow::redraw() {
     unsigned char *color;
     int i;
     int hx0, hx1, hy0, hy1;
-    int selbuf, selrow, selchar, sx0, sx1, selleft, selright;
+    int selrow, selchar, sx0, sx1, selleft, selright;
+	bool selBuf;
     int tx, tsc, tsw, lsc, rsc;
 	Screen &screen = *g_vm->_screen;
 
@@ -814,17 +815,16 @@ void TextBufferWindow::redraw() {
     pw = x1 - x0 - 2 * GLI_SUBPIX;
 
     // check if any part of buffer is selected
-    selbuf = g_vm->_windowMask->checkSelection(x0/GLI_SUBPIX,y0,x1/GLI_SUBPIX,y1);
+    selBuf = g_vm->_windowMask->checkSelection(Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
 
     for (i = _scrollPos + _height - 1; i >= _scrollPos; i--) {
         // top of line
         y = y0 + (_height - (i - _scrollPos) - 1) * g_conf->_leading;
 
         // check if part of line is selected
-        if (selbuf) {
-            selrow = g_vm->_windowMask->getSelection(x0/GLI_SUBPIX, y,
-                    x1/GLI_SUBPIX, y + g_conf->_leading,
-                    &sx0, &sx1);
+        if (selBuf) {
+            selrow = g_vm->_windowMask->getSelection(Rect(x0 / GLI_SUBPIX, y,
+				x1 / GLI_SUBPIX, y + g_conf->_leading), &sx0, &sx1);
             selleft = (sx0 == x0/GLI_SUBPIX);
             selright = (sx1 == x1/GLI_SUBPIX);
         } else {
@@ -1141,7 +1141,7 @@ void TextBufferWindow::redraw() {
     }
 
     // send selected text to clipboard
-    if (selbuf && _copyPos) {
+    if (selBuf && _copyPos) {
         Windows::_claimSelect = true;
 
 		g_vm->_clipboard->store(_copyBuf, _copyPos);


Commit: 88315e7349a011d8168dc3e4c562f40e6e51c1c1
    https://github.com/scummvm/scummvm/commit/88315e7349a011d8168dc3e4c562f40e6e51c1c1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix selection area getting removed when mouse button was released

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/window_mask.h
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 7d6bf39..4618df4 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -191,6 +191,7 @@ void Events::pollEvents() {
 		case Common::EVENT_LBUTTONUP:
 		case Common::EVENT_RBUTTONUP:
 			handleButtonUp(event.type == Common::EVENT_LBUTTONUP, event.mouse);
+			return;
 
 		case Common::EVENT_WHEELUP:
 		case Common::EVENT_WHEELDOWN:
diff --git a/engines/gargoyle/window_mask.h b/engines/gargoyle/window_mask.h
index 67d68dc..502ed49 100644
--- a/engines/gargoyle/window_mask.h
+++ b/engines/gargoyle/window_mask.h
@@ -64,6 +64,9 @@ public:
 	 */
 	void moveSelection(const Point &pos);
 
+	/**
+	 * Remove any previously selected area
+	 */
 	void clearSelection();
 
 	/**
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index a8ac85e..c6dde5b 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -365,9 +365,9 @@ void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldle
 void TextBufferWindow::touch(int line) {
 	_lines[line]._dirty = true;
 	g_vm->_windowMask->clearSelection();
-	//int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
-	//_windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
-	redraw();
+
+	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
+	_windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
 }
 
 glui32 TextBufferWindow::getSplit(glui32 size, bool vertical) const {


Commit: 596a36aef02e3bdd41f15c1b2f142acbd49f50f0
    https://github.com/scummvm/scummvm/commit/596a36aef02e3bdd41f15c1b2f142acbd49f50f0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Allow video mode to be specified in the configuration

Changed paths:
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h


diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 3135fcb..1a817c6 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -68,7 +68,7 @@ void GargoyleEngine::initialize() {
 	DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling");
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
 
-	initGraphics(640, 480, false);
+	initGraphicsMode();
 	_conf = new Conf();
 	_screen = new Screen();
 
@@ -80,6 +80,22 @@ void GargoyleEngine::initialize() {
 	_windowMask = new WindowMask();
 }
 
+void GargoyleEngine::initGraphicsMode() {
+	uint width = ConfMan.hasKey("width") ? ConfMan.getInt("width") : 640;
+	uint height = ConfMan.hasKey("height") ? ConfMan.getInt("height") : 480;
+	Common::List<Graphics::PixelFormat> formats = g_system->getSupportedFormats();
+	Graphics::PixelFormat format = formats.front();
+
+	for (Common::List<Graphics::PixelFormat>::iterator i = formats.begin(); i != formats.end(); ++i) {
+		if ((*i).bytesPerPixel > 1) {
+			format = *i;
+			break;
+		}
+	}
+
+	initGraphics(width, height, &format);
+}
+
 Common::Error GargoyleEngine::run() {
 	initialize();
 
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 0b8bca7..728dc0f 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -81,6 +81,11 @@ private:
 	 * Handles basic initialization
 	 */
 	void initialize();
+
+	/**
+	 * Setup the video mode
+	 */
+	void initGraphicsMode();
 protected:
 	const GargoyleGameDescription *_gameDescription;
 	Common::RandomSource _random;


Commit: 65167bd202f20e345bc9cdb2f0fe36c5abe28fe3
    https://github.com/scummvm/scummvm/commit/65167bd202f20e345bc9cdb2f0fe36c5abe28fe3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added a fonts.dat Zip archive containing the fonts

Changed paths:
  A dists/engine-data/fonts.dat
    dists/scummvm.rc
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h


diff --git a/dists/engine-data/fonts.dat b/dists/engine-data/fonts.dat
new file mode 100644
index 0000000..b7dde60
Binary files /dev/null and b/dists/engine-data/fonts.dat differ
diff --git a/dists/scummvm.rc b/dists/scummvm.rc
index 65e7f00..6d7782a 100644
--- a/dists/scummvm.rc
+++ b/dists/scummvm.rc
@@ -34,6 +34,9 @@ cryo.dat               FILE    "dists/engine-data/cryo.dat"
 #if ENABLE_DRASCULA   == STATIC_PLUGIN
 drascula.dat           FILE    "dists/engine-data/drascula.dat"
 #endif
+#if ENABLE_GARGOYLE   == STATIC_PLUGIN
+fonts.dat              FILE   "dists/engine-data/fonts.dat"
+#endif
 #if ENABLE_HUGO       == STATIC_PLUGIN
 hugo.dat               FILE    "dists/engine-data/hugo.dat"
 #endif
diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 8172c78..857ef15 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -23,36 +23,20 @@
 #include "gargoyle/fonts.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/conf.h"
-#include "common/file.h"
+#include "gargoyle/gargoyle.h"
+#include "common/memstream.h"
+#include "common/unzip.h"
 #include "graphics/fonts/ttf.h"
+#include "graphics/fontman.h"
 
 namespace Gargoyle {
 
-const char *gli_conf_propr = "NotoSerif-Regular";
-const char *gli_conf_propb = "NotoSerif-Bold";
-const char *gli_conf_propi = "NotoSerif-Italic";
-const char *gli_conf_propz = "NotoSerif-BoldItalic";
+#define FONTS_VERSION 1.0
+#define FONTS_FILENAME "fonts.dat"
 
-const char *gli_conf_monor = "GoMono-Regular";
-const char *gli_conf_monob = "GoMono-Bold";
-const char *gli_conf_monoi = "GoMono-Italic";
-const char *gli_conf_monoz = "GoMono-BoldItalic";
-
-Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface) {
-	double monoAspect = g_conf->_monoAspect;
-	double propAspect = g_conf->_propAspect;
-	double monoSize = g_conf->_monoSize;
-	double propSize = g_conf->_propSize;
-
-	_fontTable[0] = loadFont(MONOR, monoSize, monoAspect, FONTR);
-	_fontTable[1] = loadFont(MONOB, monoSize, monoAspect, FONTB);
-	_fontTable[2] = loadFont(MONOI, monoSize, monoAspect, FONTI);
-	_fontTable[3] = loadFont(MONOZ, monoSize, monoAspect, FONTZ);
-
-	_fontTable[4] = loadFont(PROPR, propSize, propAspect, FONTR);
-	_fontTable[5] = loadFont(PROPB, propSize, propAspect, FONTB);
-	_fontTable[6] = loadFont(PROPI, propSize, propAspect, FONTI);
-	_fontTable[7] = loadFont(PROPZ, propSize, propAspect, FONTZ);
+Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface), _fontsMissing(false) {
+	if (!loadFonts())
+		error("Could not load data file");
 
 	if (!g_conf->_leading)
 		g_conf->_leading = g_conf->_propSize + 2;
@@ -68,6 +52,63 @@ Fonts::~Fonts() {
 		delete _fontTable[idx];
 }
 
+bool Fonts::loadFonts() {
+	Common::Archive *archive = nullptr;
+
+	if (!Common::File::exists(FONTS_FILENAME) || (archive = Common::makeZipArchive(FONTS_FILENAME)) == nullptr)
+		return false;
+
+	// Open the version.txt file within it to validate the version
+	Common::File f;
+	if (!f.open("version.txt", *archive)) {
+		delete archive;
+		return false;
+	}
+
+	// Validate the version
+	char buffer[4];
+	f.read(buffer, 3);
+	buffer[3] = '\0';
+
+	if (Common::String(buffer) != "1.0") {
+		delete archive;
+		return false;
+	}
+
+	// R ead in the fonts
+	double monoAspect = g_conf->_monoAspect;
+	double propAspect = g_conf->_propAspect;
+	double monoSize = g_conf->_monoSize;
+	double propSize = g_conf->_propSize;
+
+	_fontTable[0] = loadFont(MONOR, archive, monoSize, monoAspect, FONTR);
+	_fontTable[1] = loadFont(MONOB, archive, monoSize, monoAspect, FONTB);
+	_fontTable[2] = loadFont(MONOI, archive, monoSize, monoAspect, FONTI);
+	_fontTable[3] = loadFont(MONOZ, archive, monoSize, monoAspect, FONTZ);
+
+	_fontTable[4] = loadFont(PROPR, archive, propSize, propAspect, FONTR);
+	_fontTable[5] = loadFont(PROPB, archive, propSize, propAspect, FONTB);
+	_fontTable[6] = loadFont(PROPI, archive, propSize, propAspect, FONTI);
+	_fontTable[7] = loadFont(PROPZ, archive, propSize, propAspect, FONTZ);
+
+	delete archive;
+	return true;
+}
+
+const Graphics::Font *Fonts::loadFont(FACES face, Common::Archive *archive, double size, double aspect, int
+ style) {
+	const char *const FILENAMES[8] = {
+		"GoMono-Regular.ttf", "GoMono-Bold.ttf", "GoMono-Italic.ttf", "GoMono-Bold-Italic.ttf",
+		"NotoSerif-Regular.ttf", "NotoSerif-Bold.ttf", "NotoSerif-Italic.ttf", "NotoSerif-Bold-Italic.ttf"
+	};
+	
+	Common::File f;
+	if (!f.open(FILENAMES[face], *archive))
+		error("Could not load font");
+
+	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
+}
+
 FACES Fonts::getId(const Common::String &name) {
 	if (name == "monor") return MONOR;
 	if (name == "monob") return MONOB;
@@ -80,28 +121,9 @@ FACES Fonts::getId(const Common::String &name) {
 	return MONOR;
 }
 
-Graphics::Font *Fonts::loadFont(FACES face, double size, double aspect, int style) {
-	static const char *const MAP[8] = {
-		"Go-Mono-Regular",
-		"Go-Mono-Bold",
-		"Go-Mono-Italic",
-		"Go-Mono-BoldItalic",
-		"NotoSerif-Regular",
-		"NotoSerif-Bold",
-		"NotoSerif-Italic",
-		"NotoSerif-BoldItalic"
-	};
-
-	Common::File f;
-	if (!f.open(Common::String::format("%s.ttf", MAP[face])))
-		return nullptr;
-
-	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
-}
-
 int Fonts::drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
 	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
-	Graphics::Font *font = _fontTable[fontIdx];
+	const Graphics::Font *font = _fontTable[fontIdx];
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
 
@@ -111,7 +133,7 @@ int Fonts::drawString(const Point &pos, int fontIdx, const byte *rgb, const Comm
 
 int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
 	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
-	Graphics::Font *font = _fontTable[fontIdx];
+	const Graphics::Font *font = _fontTable[fontIdx];
 	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
 
@@ -121,13 +143,13 @@ int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const C
 
 size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {
 	// TODO: Handle spw
-	Graphics::Font *font = _fontTable[fontIdx];
+	const Graphics::Font *font = _fontTable[fontIdx];
 	return font->getStringWidth(text) * GLI_SUBPIX;
 }
 
 size_t Fonts::stringWidthUni(int fontIdx, const Common::U32String &text, int spw) {
 	// TODO: Handle spw
-	Graphics::Font *font = _fontTable[fontIdx];
+	const Graphics::Font *font = _fontTable[fontIdx];
 	return font->getStringWidth(text) * GLI_SUBPIX;
 }
 
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index dc0bfd8..2f2cb41 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -25,6 +25,9 @@
 
 #include "gargoyle/glk_types.h"
 #include "gargoyle/utils.h"
+#include "common/archive.h"
+#include "common/array.h"
+#include "common/file.h"
 #include "common/str.h"
 #include "common/ustr.h"
 #include "graphics/font.h"
@@ -37,12 +40,24 @@ enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
 enum TYPES { MONOF, PROPF };
 enum STYLES { FONTR, FONTB, FONTI, FONTZ };
 
+/**
+ * Fonts manager
+ */
 class Fonts {
 private:
 	Graphics::ManagedSurface *_surface;
-	Graphics::Font *_fontTable[FONTS_TOTAL];
+	const Graphics::Font *_fontTable[FONTS_TOTAL];
+	bool _fontsMissing;
 private:
-	Graphics::Font *loadFont(FACES face, double size, double aspect, int style);
+	/**
+	 * Load all the fonts
+	 */
+	bool loadFonts();
+
+	/**
+	 * Load a single font
+	 */
+	const Graphics::Font *loadFont(FACES face, Common::Archive *archive, double size, double aspect, int style);
 public:
 	/**
 	 * Get the index/id of a font by name
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 1a817c6..59a39b4 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -106,4 +106,16 @@ Common::Error GargoyleEngine::run() {
 	return Common::kNoError;
 }
 
+void GargoyleEngine::GUIError(const char *msg, ...) {
+	char buffer[STRINGBUFLEN];
+	va_list va;
+
+	// Generate the full error message
+	va_start(va, msg);
+	vsnprintf(buffer, STRINGBUFLEN, msg, va);
+	va_end(va);
+
+	GUIErrorMessage(buffer);
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 728dc0f..f65ce94 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -166,6 +166,11 @@ public:
 	 * Return the game engine's target name
 	 */
 	const Common::String &getTargetName() const { return _targetName; }
+
+	/**
+	 * Display a message in a GUI dialog
+	 */
+	void GUIError(const char *msg, ...);
 };
 
 extern GargoyleEngine *g_vm;


Commit: d6538380d7afe31b571945bf642496a08af09dfc
    https://github.com/scummvm/scummvm/commit/d6538380d7afe31b571945bf642496a08af09dfc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Implemented pasting from clipboard

Changed paths:
    engines/gargoyle/clipboard.cpp


diff --git a/engines/gargoyle/clipboard.cpp b/engines/gargoyle/clipboard.cpp
index 69ef8f8..81ef12f 100644
--- a/engines/gargoyle/clipboard.cpp
+++ b/engines/gargoyle/clipboard.cpp
@@ -21,6 +21,9 @@
  */
 
 #include "gargoyle/clipboard.h"
+#include "gargoyle/gargoyle.h"
+#include "gargoyle/windows.h"
+#include "common/system.h"
 
 namespace Gargoyle {
 
@@ -33,7 +36,16 @@ void Clipboard::send(ClipSource source) {
 }
 
 void Clipboard::receive(ClipSource source) {
-	// TODO
+	Windows &windows = *g_vm->_windows;
+
+	if (g_system->hasTextInClipboard()) {
+		Common::String text = g_system->getTextFromClipboard();
+		for (uint idx = 0; idx < text.size(); ++idx) {
+			uint c = text[idx];
+			if (c != '\r' && c != '\n' && c != '\b' && c != '\t')
+				windows.inputHandleKey(c);
+		}
+	}
 }
 
 } // End of namespace Gargoyle


Commit: c4bcb0882f6e8f7fba5c89b9a561bb91fb662f1a
    https://github.com/scummvm/scummvm/commit/c4bcb0882f6e8f7fba5c89b9a561bb91fb662f1a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Merge Clipboard and WindowMask into a new Selection class

Changed paths:
  A engines/gargoyle/selection.cpp
  A engines/gargoyle/selection.h
  R engines/gargoyle/clipboard.cpp
  R engines/gargoyle/clipboard.h
  R engines/gargoyle/window_mask.cpp
  R engines/gargoyle/window_mask.h
    engines/gargoyle/events.cpp
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/module.mk
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/clipboard.cpp b/engines/gargoyle/clipboard.cpp
deleted file mode 100644
index 81ef12f..0000000
--- a/engines/gargoyle/clipboard.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/clipboard.h"
-#include "gargoyle/gargoyle.h"
-#include "gargoyle/windows.h"
-#include "common/system.h"
-
-namespace Gargoyle {
-
-void Clipboard::store(const uint32 *text, size_t len) {
-	// TODO
-}
-
-void Clipboard::send(ClipSource source) {
-	// TODO
-}
-
-void Clipboard::receive(ClipSource source) {
-	Windows &windows = *g_vm->_windows;
-
-	if (g_system->hasTextInClipboard()) {
-		Common::String text = g_system->getTextFromClipboard();
-		for (uint idx = 0; idx < text.size(); ++idx) {
-			uint c = text[idx];
-			if (c != '\r' && c != '\n' && c != '\b' && c != '\t')
-				windows.inputHandleKey(c);
-		}
-	}
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/clipboard.h b/engines/gargoyle/clipboard.h
deleted file mode 100644
index 18519c7..0000000
--- a/engines/gargoyle/clipboard.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_CLIPBOARD_H
-#define GARGOYLE_CLIPBOARD_H
-
-#include "common/array.h"
-
-namespace Gargoyle {
-
-enum ClipSource { PRIMARY = 0, CLIPBOARD = 1 };
-
-/**
- * Handles selection of text, and copying to and from the clipboard
- */
-class Clipboard {
-private:
-	Common::Array<uint32> _text;
-public:
-	void store(const uint32 *text, size_t len);
-
-	void send(ClipSource source);
-
-	void receive(ClipSource source);
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 4618df4..bc56407 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -21,10 +21,10 @@
  */
 
 #include "gargoyle/events.h"
-#include "gargoyle/clipboard.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/screen.h"
+#include "gargoyle/selection.h"
 #include "gargoyle/windows.h"
 #include "graphics/cursorman.h"
 
@@ -217,15 +217,15 @@ void Events::handleKeyDown(const Common::KeyState &ks) {
 		if (ks.keycode == Common::KEYCODE_a)
 			windows.inputHandleKey(keycode_Home);
 		else if (ks.keycode == Common::KEYCODE_c)
-			clipboard.send(CLIPBOARD);
+			clipboard.clipboardSend(CLIPBOARD);
 		else if (ks.keycode == Common::KEYCODE_e)
 			windows.inputHandleKey(keycode_End);
 		else if (ks.keycode == Common::KEYCODE_u)
 			windows.inputHandleKey(keycode_Escape);
 		else if (ks.keycode == Common::KEYCODE_v)
-			clipboard.receive(CLIPBOARD);
+			clipboard.clipboardReceive(CLIPBOARD);
 		else if (ks.keycode == Common::KEYCODE_x)
-			clipboard.send(CLIPBOARD);
+			clipboard.clipboardSend(CLIPBOARD);
 		else if (ks.keycode == Common::KEYCODE_LEFT || ks.keycode == Common::KEYCODE_KP4)
 			windows.inputHandleKey(keycode_SkipWordLeft);
 		else if (ks.keycode == Common::KEYCODE_RIGHT || ks.keycode == Common::KEYCODE_KP6)
@@ -281,9 +281,9 @@ void Events::handleMouseMove(const Point &pos) {
 	// TODO: Properly handle commented out lines
 	if (g_vm->_copySelect) {
 		//gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_ibeam);
-		g_vm->_windowMask->moveSelection(pos);
+		g_vm->_selection->moveSelection(pos);
 	} else {
-		if (g_vm->_windowMask->getHyperlink(pos)) {
+		if (g_vm->_selection->getHyperlink(pos)) {
 			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_hand);
 		} else {
 			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), nullptr);
@@ -296,7 +296,7 @@ void Events::handleButtonDown(bool isLeft, const Point &pos) {
 		setCursor(CURSOR_IBEAM);
 		g_vm->_windows->inputHandleClick(pos);
 	} else {
-		g_vm->_clipboard->receive(PRIMARY);
+		g_vm->_clipboard->clipboardReceive(PRIMARY);
 	}
 }
 
@@ -304,7 +304,7 @@ void Events::handleButtonUp(bool isLeft, const Point &pos) {
 	if (isLeft) {
 		setCursor(CURSOR_ARROW);
 		g_vm->_copySelect = false;
-		g_vm->_clipboard->send(PRIMARY);
+		g_vm->_clipboard->clipboardSend(PRIMARY);
 	}
 }
 
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 59a39b4..9c00937 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -29,14 +29,13 @@
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
 #include "gargoyle/gargoyle.h"
-#include "gargoyle/clipboard.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/events.h"
 #include "gargoyle/picture.h"
 #include "gargoyle/screen.h"
+#include "gargoyle/selection.h"
 #include "gargoyle/streams.h"
 #include "gargoyle/windows.h"
-#include "gargoyle/window_mask.h"
 
 namespace Gargoyle {
 
@@ -44,8 +43,8 @@ GargoyleEngine *g_vm;
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _clipboard(nullptr),
-		_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr), _windows(nullptr),
-		_windowMask(nullptr), _copySelect(false), _terminated(false),
+		_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr),
+		_selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
 		gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
 	g_vm = this;
 }
@@ -56,9 +55,9 @@ GargoyleEngine::~GargoyleEngine() {
 	delete _events;
 	delete _picList;
 	delete _screen;
+	delete _selection;
 	delete _streams;
 	delete _windows;
-	delete _windowMask;
 }
 
 void GargoyleEngine::initialize() {
@@ -75,9 +74,9 @@ void GargoyleEngine::initialize() {
 	_clipboard = new Clipboard();
 	_events = new Events();
 	_picList = new PicList();
+	_selection = new Selection();
 	_streams = new Streams();
 	_windows = new Windows(_screen);
-	_windowMask = new WindowMask();
 }
 
 void GargoyleEngine::initGraphicsMode() {
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index f65ce94..793a353 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -38,9 +38,9 @@ class Conf;
 class Events;
 class PicList;
 class Screen;
+class Selection;
 class Streams;
 class Windows;
-class WindowMask;
 
 enum InterpreterType {
 	INTERPRETER_ADVSYS = 0,
@@ -109,9 +109,9 @@ public:
 	Events *_events;
 	PicList *_picList;
 	Screen *_screen;
+	Selection *_selection;
 	Streams *_streams;
 	Windows *_windows;
-	WindowMask *_windowMask;
 	bool _copySelect;
 	bool _terminated;
 	void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock);
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index f4422b0..bf4138a 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -1,7 +1,6 @@
 MODULE := engines/gargoyle
 
 MODULE_OBJS := \
-	clipboard.o \
 	conf.o \
 	detection.o \
 	events.o \
@@ -10,6 +9,7 @@ MODULE_OBJS := \
 	glk.o \
 	picture.o \
 	screen.o \
+	selection.o \
 	streams.o \
 	time.o \
 	unicode.o \
diff --git a/engines/gargoyle/selection.cpp b/engines/gargoyle/selection.cpp
new file mode 100644
index 0000000..2b72c1e
--- /dev/null
+++ b/engines/gargoyle/selection.cpp
@@ -0,0 +1,312 @@
+/* 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 "gargoyle/selection.h"
+#include "gargoyle/conf.h"
+#include "gargoyle/gargoyle.h"
+#include "gargoyle/windows.h"
+#include "common/system.h"
+
+namespace Gargoyle {
+
+void Clipboard::clipboardStore(const uint32 *text, size_t len) {
+	// TODO
+}
+
+void Clipboard::clipboardSend(ClipSource source) {
+	// TODO
+}
+
+void Clipboard::clipboardReceive(ClipSource source) {
+	Windows &windows = *g_vm->_windows;
+
+	if (g_system->hasTextInClipboard()) {
+		Common::String text = g_system->getTextFromClipboard();
+		for (uint idx = 0; idx < text.size(); ++idx) {
+			uint c = text[idx];
+			if (c != '\r' && c != '\n' && c != '\b' && c != '\t')
+				windows.inputHandleKey(c);
+		}
+	}
+}
+
+/*--------------------------------------------------------------------------*/
+
+WindowMask::WindowMask() : _hor(0), _ver(0), _links(nullptr) {
+	_last.x = _last.y = 0;
+	resize(g_system->getWidth(), g_system->getHeight());
+}
+
+void WindowMask::resize(size_t x, size_t y) {
+	// Deallocate old storage
+	for (size_t i = 0; i < _hor; i++) {
+		if (_links[i])
+			delete _links[i];
+	}
+
+	delete _links;
+
+	_hor = x + 1;
+	_ver = y + 1;
+
+	// allocate new storage
+	_links = new glui32 *[_hor];
+	if (!_links) {
+		warning("resize_mask: out of memory");
+		_hor = _ver = 0;
+		return;
+	}
+
+	for (size_t i = 0; i < _hor; i++) {
+		_links[i] = new glui32[_ver];
+		if (!_links[i]) {
+			warning("resize_mask: could not allocate new memory");
+			return;
+		}
+	}
+
+	_select.left = 0;
+	_select.top = 0;
+	_select.right = 0;
+	_select.bottom = 0;
+}
+
+void WindowMask::putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1) {
+	uint i, k;
+	size_t tx0 = x0 < x1 ? x0 : x1;
+	size_t tx1 = x0 < x1 ? x1 : x0;
+	size_t ty0 = y0 < y1 ? y0 : y1;
+	size_t ty1 = y0 < y1 ? y1 : y0;
+
+	if (!_hor || !_ver) {
+		warning("putHyperlink: struct not initialized");
+		return;
+	}
+
+	if (tx0 >= _hor
+		|| tx1 >= _hor
+		|| ty0 >= _ver || ty1 >= _ver
+		|| !_links[tx0] || !_links[tx1]) {
+		warning("putHyperlink: invalid range given");
+		return;
+	}
+
+	for (i = tx0; i < tx1; i++) {
+		for (k = ty0; k < ty1; k++)
+			_links[i][k] = linkval;
+	}
+}
+
+glui32 WindowMask::getHyperlink(const Point &pos) const {
+	if (!_hor || !_ver) {
+		warning("getHyperlink: struct not initialized");
+		return 0;
+	}
+
+	if (pos.x >= (int16)_hor || pos.y >= (int16)_ver || !_links[pos.x]) {
+		warning("getHyperlink: invalid range given");
+		return 0;
+	}
+
+	return _links[pos.x][pos.y];
+}
+
+/*--------------------------------------------------------------------------*/
+
+void Selection::startSelection(const Point &pos) {
+	int tx, ty;
+
+	if (!_hor || !_ver) {
+		warning("startSelection: mask not initialized");
+		return;
+	}
+
+	tx = MIN(pos.x, (int16)_hor);
+	ty = MIN(pos.y, (int16)_ver);
+
+	_select.left = _last.x = tx;
+	_select.top = _last.y = ty;
+	_select.right = 0;
+	_select.bottom = 0;
+
+	g_vm->_windows->selectionChanged();
+}
+
+void Selection::moveSelection(const Point &pos) {
+	int tx, ty;
+
+	if (ABS(pos.x - _last.x) < 5 && ABS(pos.y - _last.y) < 5)
+		return;
+
+	if (!_hor || !_ver) {
+		warning("moveSelection: mask not initialized");
+		return;
+	}
+
+	tx = MIN(pos.x, (int16)_hor);
+	ty = MIN(pos.y, (int16)_ver);
+
+	_select.right = _last.x = tx;
+	_select.bottom = _last.y = ty;
+
+	g_vm->_windows->selectionChanged();
+}
+
+void Selection::clearSelection() {
+	if (!_select.isEmpty())
+		Windows::_forceRedraw = true;
+
+	_select = Rect();
+	g_vm->_windows->clearClaimSelect();
+}
+
+bool Selection::checkSelection(const Rect &r) const {
+	Rect select(MIN(_select.left, _select.right), MAX(_select.left, _select.right),
+		MIN(_select.top, _select.bottom), MAX(_select.top, _select.bottom));
+	if (select.isEmpty())
+		return false;
+
+	return select.intersects(r);
+}
+
+bool Selection::getSelection(const Rect &r, int *rx0, int *rx1) const {
+	uint row, upper, lower, above, below;
+	bool row_selected, found_left, found_right;
+	int from_right, from_below, is_above, is_below;
+	uint cx0, cx1, cy0, cy1;
+	uint x0 = r.left, y0 = r.top, x1 = r.right, y1 = r.bottom;
+
+	row = (y0 + y1) / 2;
+	upper = row - (row - y0) / 2;
+	lower = row + (y1 - row) / 2;
+	above = upper - (g_conf->_leading) / 2;
+	below = lower + (g_conf->_leading) / 2;
+
+	cx0 = MIN(_select.left, _select.right);
+	cx1 = MAX(_select.left, _select.right);
+	cy0 = MIN(_select.top, _select.bottom);
+	cy1 = MAX(_select.top, _select.bottom);
+
+	row_selected = false;
+
+	if ((cy0 >= upper && cy0 <= lower)
+		|| (cy1 >= upper && cy1 <= lower))
+		row_selected = true;
+
+	if (row >= cy0 && row <= cy1)
+		row_selected = true;
+
+	if (!row_selected)
+		return false;
+
+	from_right = (_select.left != (int16)cx0);
+	from_below = (_select.top != (int16)cy0);
+	is_above = (above >= cy0 && above <= cy1);
+	is_below = (below >= cy0 && below <= cy1);
+
+	*rx0 = 0;
+	*rx1 = 0;
+
+	found_left = false;
+	found_right = false;
+
+	if (is_above && is_below) {
+		*rx0 = x0;
+		*rx1 = x1;
+		found_left = true;
+		found_right = true;
+	} else if (!is_above && is_below) {
+		if (from_below) {
+			if (from_right) {
+				*rx0 = cx0;
+				*rx1 = x1;
+				found_left = true;
+				found_right = true;
+			} else {
+				*rx0 = cx1;
+				*rx1 = x1;
+				found_left = true;
+				found_right = true;
+			}
+		} else {
+			if (from_right) {
+				*rx0 = cx1;
+				*rx1 = x1;
+				found_left = true;
+				found_right = true;
+			} else {
+				*rx1 = x1;
+				found_right = true;
+			}
+		}
+	} else if (is_above && !is_below) {
+		if (from_below) {
+			if (from_right) {
+				*rx0 = x0;
+				*rx1 = cx1;
+				found_left = true;
+				found_right = true;
+			} else {
+				*rx0 = x0;
+				*rx1 = cx0;
+				found_left = true;
+				found_right = true;
+			}
+		} else {
+			if (from_right) {
+				if (x0 > cx0)
+					return false;
+				*rx0 = x0;
+				*rx1 = cx0;
+				found_left = true;
+				found_right = true;
+			} else {
+				*rx0 = x0;
+				found_left = true;
+			}
+		}
+	}
+
+	if (found_left && found_right)
+		return true;
+
+	for (uint i = x0; i <= x1; i++) {
+		if (i >= cx0 && i <= cx1) {
+			if (!found_left) {
+				*rx0 = i;
+				found_left = true;
+				if (found_right)
+					return true;
+			} else {
+				if (!found_right)
+					*rx1 = i;
+			}
+		}
+	}
+
+	if (rx0 && !rx1)
+		*rx1 = x1;
+
+	return (rx0 && rx1);
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/selection.h b/engines/gargoyle/selection.h
new file mode 100644
index 0000000..bc56319
--- /dev/null
+++ b/engines/gargoyle/selection.h
@@ -0,0 +1,119 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_SELECTION_H
+#define GARGOYLE_SELECTION_H
+
+#include "gargoyle/glk_types.h"
+#include "gargoyle/utils.h"
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Gargoyle {
+
+enum ClipSource { PRIMARY = 0, CLIPBOARD = 1 };
+
+class Window;
+
+/**
+ * Acts as interface to and from the system's clipboard storage
+ */
+class Clipboard {
+private:
+	Common::Array<uint32> _text;
+public:
+	/**
+	 * Makes a copy of selected text in preparation for the user copying it
+	 * to the clpboard
+	 */
+	void clipboardStore(const uint32 *text, size_t len);
+
+	/**
+	 * Send previously designated text to the clipboard
+	 */
+	void clipboardSend(ClipSource source);
+
+	/**
+	 * Receive text from the clipboard, and paste it into the current window
+	 */
+	void clipboardReceive(ClipSource source);
+};
+
+/**
+ * Manages hyperlinks for the screen
+ */
+class WindowMask {
+public:
+	size_t _hor, _ver;
+	glui32 **_links;
+	Rect _select;
+	Point _last;
+public:
+	/**
+	 * Constructor
+	 */
+	WindowMask();
+
+	/**
+	 * Resize the links array
+	 */
+	void resize(size_t x, size_t y);
+
+	void putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1);
+
+	glui32 getHyperlink(const Point &pos) const;
+};
+
+/**
+ * Overall manager for selecting areas on the screen, copying to/from the clipboard,
+ * and managing hyperlinks
+ */
+class Selection : public Clipboard, public WindowMask {
+public:
+	/**
+	 * Start selecting an area of the screen
+	 * @param pos		Position to start selection area at
+	 */
+	void startSelection(const Point &pos);
+
+	/**
+	 * Move the end point of the selection area
+	 * @param pos		Position to end selection area at
+	 */
+	void moveSelection(const Point &pos);
+
+	/**
+	 * Remove any previously selected area
+	 */
+	void clearSelection();
+
+	/**
+	 * Checks whether the passed area intersects the selection area
+	 */
+	bool checkSelection(const Rect &r) const;
+
+	bool getSelection(const Rect &r, int *rx0, int *rx1) const;
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/window_graphics.cpp b/engines/gargoyle/window_graphics.cpp
index 3286876..a0d8b6d 100644
--- a/engines/gargoyle/window_graphics.cpp
+++ b/engines/gargoyle/window_graphics.cpp
@@ -147,7 +147,7 @@ void GraphicsWindow::eraseRect(bool whole, const Rect &box) {
 	hy1 = _bbox.top + y1;
 
 	/* zero out hyperlinks for these coordinates */
-	g_vm->_windowMask->putHyperlink(0, hx0, hy0, hx1, hy1);
+	g_vm->_selection->putHyperlink(0, hx0, hy0, hx1, hy1);
 
 	_surface->fillRect(Rect(x0, y0, x1, y1), MKTAG(_bgnd[0], _bgnd[1], _bgnd[2], 0));
 	touch();
@@ -177,7 +177,7 @@ void GraphicsWindow::fillRect(glui32 color, const Rect &box) {
 	hy1 = _bbox.top + y1;
 
 	/* zero out hyperlinks for these coordinates */
-	g_vm->_windowMask->putHyperlink(0, hx0, hy0, hx1, hy1);
+	g_vm->_selection->putHyperlink(0, hx0, hy0, hx1, hy1);
 
 	_surface->fillRect(Rect(x0, y0, x1, y1), MKTAG(col[0], col[1], col[2], 0));
 	touch();
@@ -229,7 +229,7 @@ void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int h
 	hy1 = _bbox.top + y1;
 
 	/* zero out or set hyperlink for these coordinates */
-	g_vm->_windowMask->putHyperlink(linkval, hx0, hy0, hx1, hy1);
+	g_vm->_selection->putHyperlink(linkval, hx0, hy0, hx1, hy1);
 
 	w = sx1 - sx0;
 	h = sy1 - sy0;
@@ -259,7 +259,7 @@ void GraphicsWindow::click(const Point &newPos) {
 	}
 
 	if (_hyperRequest) {
-		glui32 linkval = g_vm->_windowMask->getHyperlink(newPos);
+		glui32 linkval = g_vm->_selection->getHyperlink(newPos);
 		if (linkval) {
 			g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
 			_hyperRequest = false;
diff --git a/engines/gargoyle/window_mask.cpp b/engines/gargoyle/window_mask.cpp
deleted file mode 100644
index 495abc6..0000000
--- a/engines/gargoyle/window_mask.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/window_mask.h"
-#include "gargoyle/conf.h"
-#include "gargoyle/gargoyle.h"
-#include "gargoyle/windows.h"
-#include "common/system.h"
-
-namespace Gargoyle {
-
-WindowMask::WindowMask() : _hor(0), _ver(0), _links(nullptr) {
-	_last.x = _last.y = 0;
-	resize(g_system->getWidth(), g_system->getHeight());
-}
-
-void WindowMask::resize(size_t x, size_t y) {
-	// Deallocate old storage
-	for (size_t i = 0; i < _hor; i++) {
-		if (_links[i])
-			delete _links[i];
-	}
-
-	delete _links;
-
-	_hor = x + 1;
-	_ver = y + 1;
-
-	// allocate new storage
-	_links = new glui32 *[_hor];
-	if (!_links) {
-		warning("resize_mask: out of memory");
-		_hor = _ver = 0;
-		return;
-	}
-
-	for (size_t i = 0; i < _hor; i++) {
-		_links[i] = new glui32[_ver];
-		if (!_links[i]) {
-			warning("resize_mask: could not allocate new memory");
-			return;
-		}
-	}
-
-	_select.left = 0;
-	_select.top = 0;
-	_select.right = 0;
-	_select.bottom = 0;
-}
-
-void WindowMask::putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1) {
-	uint i, k;
-	size_t tx0 = x0 < x1 ? x0 : x1;
-	size_t tx1 = x0 < x1 ? x1 : x0;
-	size_t ty0 = y0 < y1 ? y0 : y1;
-	size_t ty1 = y0 < y1 ? y1 : y0;
-
-	if (!_hor || !_ver) {
-		warning("putHyperlink: struct not initialized");
-		return;
-	}
-
-	if (tx0 >= _hor
-		|| tx1 >= _hor
-		|| ty0 >= _ver || ty1 >= _ver
-		|| !_links[tx0] || !_links[tx1]) {
-		warning("putHyperlink: invalid range given");
-		return;
-	}
-
-	for (i = tx0; i < tx1; i++) {
-		for (k = ty0; k < ty1; k++)
-			_links[i][k] = linkval;
-	}
-}
-
-glui32 WindowMask::getHyperlink(const Point &pos) const {
-	if (!_hor || !_ver) {
-		warning("getHyperlink: struct not initialized");
-		return 0;
-	}
-
-	if (pos.x >= (int16)_hor || pos.y >= (int16)_ver || !_links[pos.x]) {
-		warning("getHyperlink: invalid range given");
-		return 0;
-	}
-
-	return _links[pos.x][pos.y];
-}
-
-void WindowMask::startSelection(const Point &pos) {
-	int tx, ty;
-
-	if (!_hor || !_ver) {
-		warning("startSelection: mask not initialized");
-		return;
-	}
-
-	tx = MIN(pos.x, (int16)_hor);
-	ty = MIN(pos.y, (int16)_ver);
-
-	_select.left = _last.x = tx;
-	_select.top = _last.y = ty;
-	_select.right = 0;
-	_select.bottom = 0;
-
-	g_vm->_windows->selectionChanged();
-}
-
-void WindowMask::moveSelection(const Point &pos) {
-	int tx, ty;
-
-	if (ABS(pos.x - _last.x) < 5 && ABS(pos.y - _last.y) < 5)
-		return;
-
-	if (!_hor || !_ver) {
-		warning("moveSelection: mask not initialized");
-		return;
-	}
-
-	tx = MIN(pos.x, (int16)_hor);
-	ty = MIN(pos.y, (int16)_ver);
-
-	_select.right = _last.x = tx;
-	_select.bottom = _last.y = ty;
-
-	g_vm->_windows->selectionChanged();
-}
-
-void WindowMask::clearSelection() {
-	if (!_select.isEmpty())
-		Windows::_forceRedraw = true;
-
-	_select = Rect();
-	g_vm->_windows->clearClaimSelect();
-}
-
-bool WindowMask::checkSelection(const Rect &r) const {
-	Rect select(MIN(_select.left, _select.right), MAX(_select.left, _select.right),
-		MIN(_select.top, _select.bottom), MAX(_select.top, _select.bottom));
-	if (select.isEmpty())
-		return false;
-
-	return select.intersects(r);
-}
-
-bool WindowMask::getSelection(const Rect &r, int *rx0, int *rx1) const {
-	uint row, upper, lower, above, below;
-	bool row_selected, found_left, found_right;
-	int from_right, from_below, is_above, is_below;
-	uint cx0, cx1, cy0, cy1;
-	uint x0 = r.left, y0 = r.top, x1 = r.right, y1 = r.bottom;
-
-	row = (y0 + y1) / 2;
-	upper = row - (row - y0) / 2;
-	lower = row + (y1 - row) / 2;
-	above = upper - (g_conf->_leading) / 2;
-	below = lower + (g_conf->_leading) / 2;
-
-	cx0 = MIN(_select.left, _select.right);
-	cx1 = MAX(_select.left, _select.right);
-	cy0 = MIN(_select.top, _select.bottom);
-	cy1 = MAX(_select.top, _select.bottom);
-
-	row_selected = false;
-
-	if ((cy0 >= upper && cy0 <= lower)
-		|| (cy1 >= upper && cy1 <= lower))
-		row_selected = true;
-
-	if (row >= cy0 && row <= cy1)
-		row_selected = true;
-
-	if (!row_selected)
-		return false;
-
-	from_right = (_select.left != (int16)cx0);
-	from_below = (_select.top != (int16)cy0);
-	is_above = (above >= cy0 && above <= cy1);
-	is_below = (below >= cy0 && below <= cy1);
-
-	*rx0 = 0;
-	*rx1 = 0;
-
-	found_left = false;
-	found_right = false;
-
-	if (is_above && is_below) {
-		*rx0 = x0;
-		*rx1 = x1;
-		found_left = true;
-		found_right = true;
-	} else if (!is_above && is_below) {
-		if (from_below) {
-			if (from_right) {
-				*rx0 = cx0;
-				*rx1 = x1;
-				found_left = true;
-				found_right = true;
-			} else {
-				*rx0 = cx1;
-				*rx1 = x1;
-				found_left = true;
-				found_right = true;
-			}
-		} else {
-			if (from_right) {
-				*rx0 = cx1;
-				*rx1 = x1;
-				found_left = true;
-				found_right = true;
-			} else {
-				*rx1 = x1;
-				found_right = true;
-			}
-		}
-	} else if (is_above && !is_below) {
-		if (from_below) {
-			if (from_right) {
-				*rx0 = x0;
-				*rx1 = cx1;
-				found_left = true;
-				found_right = true;
-			} else {
-				*rx0 = x0;
-				*rx1 = cx0;
-				found_left = true;
-				found_right = true;
-			}
-		} else {
-			if (from_right) {
-				if (x0 > cx0)
-					return false;
-				*rx0 = x0;
-				*rx1 = cx0;
-				found_left = true;
-				found_right = true;
-			} else {
-				*rx0 = x0;
-				found_left = true;
-			}
-		}
-	}
-
-	if (found_left && found_right)
-		return true;
-
-	for (uint i = x0; i <= x1; i++) {
-		if (i >= cx0 && i <= cx1) {
-			if (!found_left) {
-				*rx0 = i;
-				found_left = true;
-				if (found_right)
-					return true;
-			} else {
-				if (!found_right)
-					*rx1 = i;
-			}
-		}
-	}
-
-	if (rx0 && !rx1)
-		*rx1 = x1;
-
-	return (rx0 && rx1);
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/window_mask.h b/engines/gargoyle/window_mask.h
deleted file mode 100644
index 502ed49..0000000
--- a/engines/gargoyle/window_mask.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_WINDOW_MASK_H
-#define GARGOYLE_WINDOW_MASK_H
-
-#include "common/rect.h"
-#include "gargoyle/glk_types.h"
-#include "gargoyle/utils.h"
-
-namespace Gargoyle {
-
-class Window;
-
-class WindowMask {
-public:
-	size_t _hor, _ver;
-	glui32 **_links;
-	Rect _select;
-	Point _last;
-public:
-	/**
-	 * Constructor
-	 */
-	WindowMask();
-
-	/**
-	 * Resize the links array
-	 */
-	void resize(size_t x, size_t y);
-
-	void putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1);
-
-	glui32 getHyperlink(const Point &pos) const;
-
-	/**
-	 * Start selecting an area of the screen
-	 * @param pos		Position to start selection area at
-	 */
-	void startSelection(const Point &pos);
-
-	/**
-	 * Move the end point of the selection area
-	 * @param pos		Position to end selection area at
-	 */
-	void moveSelection(const Point &pos);
-
-	/**
-	 * Remove any previously selected area
-	 */
-	void clearSelection();
-
-	/**
-	 * Checks whether the passed area intersects the selection area
-	 */
-	bool checkSelection(const Rect &r) const;
-
-	bool getSelection(const Rect &r, int *rx0, int *rx1) const;
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index c6dde5b..a7e5365 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -21,10 +21,10 @@
  */
 
 #include "gargoyle/window_text_buffer.h"
-#include "gargoyle/clipboard.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/screen.h"
+#include "gargoyle/selection.h"
 #include "gargoyle/unicode.h"
 
 namespace Gargoyle {
@@ -232,7 +232,7 @@ void TextBufferWindow::reflow() {
 }
 
 void TextBufferWindow::touchScroll() {
-	g_vm->_windowMask->clearSelection();
+	g_vm->_selection->clearSelection();
 	_windows->repaint(_bbox);
 
 	for (int i = 0; i < _scrollMax; i++)
@@ -364,7 +364,7 @@ void TextBufferWindow::putTextUni(const glui32 *buf, int len, int pos, int oldle
 
 void TextBufferWindow::touch(int line) {
 	_lines[line]._dirty = true;
-	g_vm->_windowMask->clearSelection();
+	g_vm->_selection->clearSelection();
 
 	int y = _bbox.top + g_conf->_tMarginY + (_height - line - 1) * g_conf->_leading;
 	_windows->repaint(Rect(_bbox.left, y - 2, _bbox.right, y + g_conf->_leading + 2));
@@ -572,7 +572,7 @@ void TextBufferWindow::click(const Point &newPos) {
 		_windows->setFocus(this);
 
 	if (_hyperRequest) {
-		glui32 linkval = g_vm->_windowMask->getHyperlink(newPos);
+		glui32 linkval = g_vm->_selection->getHyperlink(newPos);
 		if (linkval) {
 			g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
 			_hyperRequest = false;
@@ -596,7 +596,7 @@ void TextBufferWindow::click(const Point &newPos) {
 
 	if (!gh && !gs) {
 		g_vm->_copySelect = true;
-		g_vm->_windowMask->startSelection(newPos);
+		g_vm->_selection->startSelection(newPos);
 	}
 }
 
@@ -815,7 +815,7 @@ void TextBufferWindow::redraw() {
     pw = x1 - x0 - 2 * GLI_SUBPIX;
 
     // check if any part of buffer is selected
-    selBuf = g_vm->_windowMask->checkSelection(Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
+    selBuf = g_vm->_selection->checkSelection(Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
 
     for (i = _scrollPos + _height - 1; i >= _scrollPos; i--) {
         // top of line
@@ -823,7 +823,7 @@ void TextBufferWindow::redraw() {
 
         // check if part of line is selected
         if (selBuf) {
-            selrow = g_vm->_windowMask->getSelection(Rect(x0 / GLI_SUBPIX, y,
+            selrow = g_vm->_selection->getSelection(Rect(x0 / GLI_SUBPIX, y,
 				x1 / GLI_SUBPIX, y + g_conf->_leading), &sx0, &sx1);
             selleft = (sx0 == x0/GLI_SUBPIX);
             selright = (sx1 == x1/GLI_SUBPIX);
@@ -947,7 +947,7 @@ void TextBufferWindow::redraw() {
         }
 
         // clear any stored hyperlink coordinates
-        g_vm->_windowMask->putHyperlink(0, x0/GLI_SUBPIX, y,
+        g_vm->_selection->putHyperlink(0, x0/GLI_SUBPIX, y,
                 x1/GLI_SUBPIX, y + g_conf->_leading);
 
         /*
@@ -970,7 +970,7 @@ void TextBufferWindow::redraw() {
                 if (link) {
                     screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
 						w / GLI_SUBPIX + 1, g_conf->_linkStyle), g_conf->_linkColor);
-                    g_vm->_windowMask->putHyperlink(link, x/GLI_SUBPIX, y,
+                    g_vm->_selection->putHyperlink(link, x/GLI_SUBPIX, y,
                             x/GLI_SUBPIX + w/GLI_SUBPIX,
                             y + g_conf->_leading);
                 }
@@ -986,7 +986,7 @@ void TextBufferWindow::redraw() {
         if (link) {
             screen.fillRect(Rect::fromXYWH(x / GLI_SUBPIX + 1, y + g_conf->_baseLine + 1,
                     w/GLI_SUBPIX + 1, g_conf->_linkStyle), g_conf->_linkColor);
-            g_vm->_windowMask->putHyperlink(link, x / GLI_SUBPIX, y,
+            g_vm->_selection->putHyperlink(link, x / GLI_SUBPIX, y,
                     x / GLI_SUBPIX + w / GLI_SUBPIX,
                     y + g_conf->_leading);
         }
@@ -1034,7 +1034,7 @@ void TextBufferWindow::redraw() {
         x = x0 + SLOP;
         y = y0 + (_height - 1) * g_conf->_leading;
 
-        g_vm->_windowMask->putHyperlink(0, x0/GLI_SUBPIX, y,
+        g_vm->_selection->putHyperlink(0, x0/GLI_SUBPIX, y,
                 x1/GLI_SUBPIX, y + g_conf->_leading);
 
         color = Windows::_overrideBgSet ? g_conf->_windowColor : _bgColor;
@@ -1080,7 +1080,7 @@ void TextBufferWindow::redraw() {
                 hx1 = x0/GLI_SUBPIX + ln->_lPic->w < x1/GLI_SUBPIX
                             ? x0/GLI_SUBPIX + ln->_lPic->w
                             : x1/GLI_SUBPIX;
-                g_vm->_windowMask->putHyperlink(link, hx0, hy0, hx1, hy1);
+                g_vm->_selection->putHyperlink(link, hx0, hy0, hx1, hy1);
             }
         }
 
@@ -1095,7 +1095,7 @@ void TextBufferWindow::redraw() {
                             ? x1/GLI_SUBPIX - ln->_rPic->w
                             : x0/GLI_SUBPIX;
                 hx1 = x1/GLI_SUBPIX;
-                g_vm->_windowMask->putHyperlink(link, hx0, hy0, hx1, hy1);
+                g_vm->_selection->putHyperlink(link, hx0, hy0, hx1, hy1);
             }
         }
     }
@@ -1114,7 +1114,7 @@ void TextBufferWindow::redraw() {
         y0 = _bbox.top + g_conf->_tMarginY;
         y1 = _bbox.bottom - g_conf->_tMarginY;
 
-        g_vm->_windowMask->putHyperlink(0, x0, y0, x1, y1);
+        g_vm->_selection->putHyperlink(0, x0, y0, x1, y1);
 
         y0 += g_conf->_scrollWidth / 2;
         y1 -= g_conf->_scrollWidth / 2;
@@ -1144,7 +1144,7 @@ void TextBufferWindow::redraw() {
     if (selBuf && _copyPos) {
         Windows::_claimSelect = true;
 
-		g_vm->_clipboard->store(_copyBuf, _copyPos);
+		g_vm->_clipboard->clipboardStore(_copyBuf, _copyPos);
         for (i = 0; i < _copyPos; i++)
             _copyBuf[i] = 0;
         _copyPos = 0;
diff --git a/engines/gargoyle/window_text_grid.cpp b/engines/gargoyle/window_text_grid.cpp
index 351d9fb..1e5e41d 100644
--- a/engines/gargoyle/window_text_grid.cpp
+++ b/engines/gargoyle/window_text_grid.cpp
@@ -23,8 +23,8 @@
 #include "gargoyle/window_text_grid.h"
 #include "gargoyle/conf.h"
 #include "gargoyle/gargoyle.h"
+#include "gargoyle/selection.h"
 #include "gargoyle/screen.h"
-#include "gargoyle/window_mask.h"
 
 namespace Gargoyle {
 
@@ -204,7 +204,7 @@ void TextGridWindow::click(const Point &newPos) {
 	}
 
 	if (_hyperRequest) {
-		glui32 linkval = g_vm->_windowMask->getHyperlink(newPos);
+		glui32 linkval = g_vm->_selection->getHyperlink(newPos);
 		if (linkval)
 		{
 			g_vm->_events->store(evtype_Hyperlink, this, linkval, 0);
@@ -592,7 +592,7 @@ void TextGridWindow::redraw() {
 			y = y0 + i * g_conf->_leading;
 
 			// clear any stored hyperlink coordinates
-			g_vm->_windowMask->putHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
+			g_vm->_selection->putHyperlink(0, x0, y, x0 + g_conf->_cellW * _width, y + g_conf->_leading);
 
 			a = 0;
 			for (b = 0; b < _width; b++) {
@@ -612,7 +612,7 @@ void TextGridWindow::redraw() {
 					if (link) {
 						screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w,
 							g_conf->_linkStyle), g_conf->_linkColor);
-						g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
+						g_vm->_selection->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 					}
 
 					x += w;
@@ -634,7 +634,7 @@ void TextGridWindow::redraw() {
 			if (link) {
 				screen.fillRect(Rect::fromXYWH(x, y + g_conf->_baseLine + 1, w, g_conf->_linkStyle),
 					g_conf->_linkColor);
-				g_vm->_windowMask->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
+				g_vm->_selection->putHyperlink(link, x, y, x + w, y + g_conf->_leading);
 			}
 		}
 	}
diff --git a/engines/gargoyle/windows.h b/engines/gargoyle/windows.h
index 5830c7c..0e4020b 100644
--- a/engines/gargoyle/windows.h
+++ b/engines/gargoyle/windows.h
@@ -30,8 +30,8 @@
 #include "gargoyle/events.h"
 #include "gargoyle/glk_types.h"
 #include "gargoyle/fonts.h"
+#include "gargoyle/selection.h"
 #include "gargoyle/streams.h"
-#include "gargoyle/window_mask.h"
 
 namespace Gargoyle {
 


Commit: 3c2fd3e74f9a205109ab9b57c5af893011560452
    https://github.com/scummvm/scummvm/commit/3c2fd3e74f9a205109ab9b57c5af893011560452
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add copying to clipboard

Changed paths:
    engines/gargoyle/selection.cpp
    engines/gargoyle/selection.h
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/selection.cpp b/engines/gargoyle/selection.cpp
index 2b72c1e..8ad3b76 100644
--- a/engines/gargoyle/selection.cpp
+++ b/engines/gargoyle/selection.cpp
@@ -28,23 +28,32 @@
 
 namespace Gargoyle {
 
-void Clipboard::clipboardStore(const uint32 *text, size_t len) {
-	// TODO
+void Clipboard::clipboardStore(const Common::U32String &text) {
+	_text = text;
 }
 
 void Clipboard::clipboardSend(ClipSource source) {
-	// TODO
+	if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
+		// Convert unicode string to standard string, since that's all ScummVM supports
+		Common::String text;
+		for (uint idx = 0; idx < _text.size(); ++idx)
+			text += (_text[idx] <= 0x7f) ? (char)_text[idx] : '?';
+
+		g_system->setTextInClipboard(text);
+	}
 }
 
 void Clipboard::clipboardReceive(ClipSource source) {
-	Windows &windows = *g_vm->_windows;
-
-	if (g_system->hasTextInClipboard()) {
-		Common::String text = g_system->getTextFromClipboard();
-		for (uint idx = 0; idx < text.size(); ++idx) {
-			uint c = text[idx];
-			if (c != '\r' && c != '\n' && c != '\b' && c != '\t')
-				windows.inputHandleKey(c);
+	if (g_system->hasFeature(OSystem::kFeatureClipboardSupport)) {
+		Windows &windows = *g_vm->_windows;
+
+		if (g_system->hasTextInClipboard()) {
+			Common::String text = g_system->getTextFromClipboard();
+			for (uint idx = 0; idx < text.size(); ++idx) {
+				uint c = text[idx];
+				if (c != '\r' && c != '\n' && c != '\b' && c != '\t')
+					windows.inputHandleKey(c);
+			}
 		}
 	}
 }
diff --git a/engines/gargoyle/selection.h b/engines/gargoyle/selection.h
index bc56319..2d3a208 100644
--- a/engines/gargoyle/selection.h
+++ b/engines/gargoyle/selection.h
@@ -27,6 +27,7 @@
 #include "gargoyle/utils.h"
 #include "common/array.h"
 #include "common/rect.h"
+#include "common/ustr.h"
 
 namespace Gargoyle {
 
@@ -39,13 +40,13 @@ class Window;
  */
 class Clipboard {
 private:
-	Common::Array<uint32> _text;
+	Common::U32String _text;
 public:
 	/**
 	 * Makes a copy of selected text in preparation for the user copying it
 	 * to the clpboard
 	 */
-	void clipboardStore(const uint32 *text, size_t len);
+	void clipboardStore(const Common::U32String &text);
 
 	/**
 	 * Send previously designated text to the clipboard
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index a7e5365..7e65059 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -1140,11 +1140,11 @@ void TextBufferWindow::redraw() {
         }
     }
 
-    // send selected text to clipboard
+    // Keep track of selected text to be ready when user copies it to the clipboard
     if (selBuf && _copyPos) {
         Windows::_claimSelect = true;
 
-		g_vm->_clipboard->clipboardStore(_copyBuf, _copyPos);
+		g_vm->_clipboard->clipboardStore(Common::U32String(_copyBuf, _copyPos));
         for (i = 0; i < _copyPos; i++)
             _copyBuf[i] = 0;
         _copyPos = 0;


Commit: 7cba554fc5ea46c3a6bbe23e073de16914d7895f
    https://github.com/scummvm/scummvm/commit/7cba554fc5ea46c3a6bbe23e073de16914d7895f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Don't leading & baseline for fonts go below minimum size

Changed paths:
    engines/gargoyle/fonts.cpp
    engines/gargoyle/fonts.h


diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
index 857ef15..9c07412 100644
--- a/engines/gargoyle/fonts.cpp
+++ b/engines/gargoyle/fonts.cpp
@@ -38,11 +38,14 @@ Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface), _fontsMissi
 	if (!loadFonts())
 		error("Could not load data file");
 
-	if (!g_conf->_leading)
-		g_conf->_leading = g_conf->_propSize + 2;
-	if (!g_conf->_baseLine)
-		g_conf->_baseLine = g_conf->_propSize + 0.5;
-
+	// TODO: See if there's any better way for getting the leading and baseline
+	Common::Rect r1 = _fontTable[7]->getBoundingBox('o');
+	Common::Rect r2 = _fontTable[7]->getBoundingBox('y');
+	double baseLine = (double)r1.bottom;
+	double leading = (double)r2.bottom + 2;
+
+	g_conf->_leading = MAX((double)g_conf->_leading, leading);
+	g_conf->_baseLine = MAX((double)g_conf->_baseLine, baseLine);
 	g_conf->_cellW = _fontTable[0]->getStringWidth("0");
 	g_conf->_cellH = g_conf->_leading;
 }
@@ -142,13 +145,11 @@ int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const C
 }
 
 size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {
-	// TODO: Handle spw
 	const Graphics::Font *font = _fontTable[fontIdx];
 	return font->getStringWidth(text) * GLI_SUBPIX;
 }
 
 size_t Fonts::stringWidthUni(int fontIdx, const Common::U32String &text, int spw) {
-	// TODO: Handle spw
 	const Graphics::Font *font = _fontTable[fontIdx];
 	return font->getStringWidth(text) * GLI_SUBPIX;
 }
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index 2f2cb41..1437bd8 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -98,19 +98,19 @@ public:
 	 * Get the width in pixels of a string
 	 * @param fontIdx   Which font to use
 	 * @param text		Text to get the width of
-	 * @param spw		???
+	 * @param spw		Delta X
 	 * @returns			Width of string multiplied by GLI_SUBPIX
 	 */
-	size_t stringWidth(int fontIdx, const Common::String &text, int spw = -1);
+	size_t stringWidth(int fontIdx, const Common::String &text, int spw = 0);
 
 	/**
 	 * Get the width in pixels of a unicode string
 	 * @param fontIdx   Which font to use
 	 * @param text		Text to get the width of
-	 * @param spw		???
+	 * @param spw		Delta X
 	 * @returns			Width of string multiplied by GLI_SUBPIX
 	 */
-	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = -1);
+	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = 0);
 };
 
 } // End of namespace Gargoyle


Commit: e208e147960ef820f73999026ab692db25fe2bcd
    https://github.com/scummvm/scummvm/commit/e208e147960ef820f73999026ab692db25fe2bcd
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Get proper baseline and leading from loaded fonts

Changed paths:
    engines/gargoyle/conf.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index ae67a8a..21f245a 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -79,7 +79,7 @@ Conf::Conf() {
 	get("morealign", _moreAlign);
 	get("monoaspect", _monoAspect, 1.0);
 	get("propaspect", _propAspect, 1.0);
-	get("monosize", _monoSize, 8);
+	get("monosize", _monoSize, 11);
 	get("monor", _monoR);
 	get("monob", _monoR);
 	get("monoi", _monoI);


Commit: f73d56f6bab4ca2642790cb8429b9fee3d5d2006
    https://github.com/scummvm/scummvm/commit/f73d56f6bab4ca2642790cb8429b9fee3d5d2006
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Setup default font colors to match garglk defaults

Changed paths:
    engines/gargoyle/conf.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 21f245a..98af236 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -43,23 +43,23 @@ WindowStyle T_STYLES[style_NUMSTYLES] = {
 	{ PROPZ,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
 	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
 	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
-	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Input
+	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x60,0x00 }, 0 }, ///< Input
 	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User1
 	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User2
 };
 
 WindowStyle G_STYLES[style_NUMSTYLES] = {
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Normal
-	{ MONOI,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Emphasized
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Preformatted
-	{ MONOB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Header
-	{ MONOB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Subheader
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Input
-	{ MONOR,{ 0x60,0x60,0x60 },{ 0xff,0xff,0xff }, 0 }, ///< User1
-	{ MONOR,{ 0x60,0x60,0x60 },{ 0xff,0xff,0xff }, 0 }, ///< User2
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Normal
+	{ MONOI,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Emphasized
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Preformatted
+	{ MONOB,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Header
+	{ MONOB,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Subheader
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Alert
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Note
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< BlockQuote
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Input
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< User1
+	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< User2
 };
 
 Conf *g_conf;


Commit: 671321c45fb52163022b51e4e128a02ec66dc28d
    https://github.com/scummvm/scummvm/commit/671321c45fb52163022b51e4e128a02ec66dc28d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix loading of font colors and styles from configuration

Changed paths:
    engines/gargoyle/conf.cpp


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 98af236..7add133 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -156,47 +156,44 @@ Conf::Conf() {
 	Common::copy(G_STYLES, G_STYLES + style_NUMSTYLES, _gStyles);
 
 	char buffer[255];
-	const char *const TG_COLOR[2] = { "tcolor", "gcolor" };
-	for (int idx = 0; idx < 2; ++idx) {
-		if (!ConfMan.hasKey(TG_COLOR[idx]))
-			continue;
-
-		strncpy(buffer, ConfMan.get(TG_COLOR[idx]).c_str(), 254);
-		buffer[255] = '\0';
-		char *style = strtok(buffer, "\r\n\t ");
-		char *fg = strtok(nullptr, "\r\n\t ");
-		char *bg = strtok(nullptr, "\r\n\t ");
-
-		int i = atoi(style);
-		if (i < 0 || i >= style_NUMSTYLES)
-			continue;
-
-		if (idx == 0) {
-			parseColor(fg, _tStyles[i].fg);
-			parseColor(bg, _tStyles[i].bg);
-		} else {
-			parseColor(fg, _gStyles[i].fg);
-			parseColor(bg, _gStyles[i].bg);
+	const char *const TG_COLOR[2] = { "tcolor_%d", "gcolor_%d" };
+	for (int tg = 0; tg < 2; ++tg) {
+		for (int style = 0; style <= 10; ++style) {
+			Common::String key = Common::String::format(TG_COLOR[tg], style);
+			if (!ConfMan.hasKey(key))
+				continue;
+
+			strncpy(buffer, ConfMan.get(key).c_str(), 254);
+			buffer[255] = '\0';
+			char *fg = strtok(buffer, "\r\n\t ");
+			char *bg = strtok(nullptr, "\r\n\t ");
+
+			if (tg == 0) {
+				parseColor(fg, _tStyles[style].fg);
+				parseColor(bg, _tStyles[style].bg);
+			} else {
+				parseColor(fg, _gStyles[style].fg);
+				parseColor(bg, _gStyles[style].bg);
+			}
 		}
 	}
 
-	const char *const TG_FONT[2] = { "tfont", "gfont" };
-	for (int idx = 0; idx < 2; ++idx) {
-		if (!ConfMan.hasKey(TG_FONT[idx]))
-			continue;
-
-		strncpy(buffer, ConfMan.get(TG_FONT[idx]).c_str(), 254);
-		buffer[255] = '\0';
-		char *style = strtok(buffer, "\r\n\t ");
-		char *font = strtok(nullptr, "\r\n\t ");
-		int i = atoi(style);
-		if (i < 0 || i >= style_NUMSTYLES)
-			continue;
-
-		if (idx == 0)
-			_tStyles[i].font = Fonts::getId(font);
-		else
-			_gStyles[i].font = Fonts::getId(font);
+	const char *const TG_FONT[2] = { "tfont_%d", "gfont_%d" };
+	for (int tg = 0; tg < 2; ++tg) {
+		for (int style = 0; style <= 10; ++style) {
+			Common::String key = Common::String::format(TG_FONT[tg], style);
+			if (!ConfMan.hasKey(key))
+				continue;
+
+			strncpy(buffer, ConfMan.get(key).c_str(), 254);
+			buffer[255] = '\0';
+			char *font = strtok(buffer, "\r\n\t ");
+
+			if (tg == 0)
+				_tStyles[style].font = Fonts::getId(font);
+			else
+				_gStyles[style].font = Fonts::getId(font);
+		}
 	}
 
 	Common::copy(_tStyles, _tStyles + style_NUMSTYLES, _tStylesDefault);


Commit: 43bee7a72783cd60b86249997bfdaf7af5e5d740
    https://github.com/scummvm/scummvm/commit/43bee7a72783cd60b86249997bfdaf7af5e5d740
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add GUI options to default detection entry

Changed paths:
    engines/gargoyle/detection.cpp


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 295e50b..af7cfc6 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -243,6 +243,7 @@ ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, con
 		gameDescription._desc.gameId = gameId;
 		gameDescription._desc.language = gd.language;
 		gameDescription._desc.platform = gd.platform;
+		gameDescription._desc.guiOptions = GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES);
 		gameDescription._filename = filename;
 		gameDescription._md5 = Common::computeStreamMD5AsString(f, 5000);
 


Commit: 601e1d486b038f121323e78ed68a154d6763c9f1
    https://github.com/scummvm/scummvm/commit/601e1d486b038f121323e78ed68a154d6763c9f1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add GLK timer intervals

Changed paths:
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/glk.cpp


diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index bc56407..520b6f2 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -53,8 +53,8 @@ const byte ARROW[] = {
 	4, 2, 5, 5
 };
 
-Events::Events() : _forceClick(false), _currentEvent(nullptr), _timeouts(false),
-		_priorFrameTime(0), _frameCounter(0), _cursorId(CURSOR_NONE) {
+Events::Events() : _forceClick(false), _currentEvent(nullptr), _cursorId(CURSOR_NONE),
+		_timerMilli(0), _timerTimeExpiry(0), _priorFrameTime(0), _frameCounter(0) {
 	initializeCursors();
 }
 
@@ -118,7 +118,7 @@ void Events::getEvent(event_t *event, bool polled) {
 	dispatchEvent(*_currentEvent, polled);
 
 	if (!polled) {
-		while (!g_vm->shouldQuit() && _currentEvent->type == evtype_None && !_timeouts) {
+		while (!g_vm->shouldQuit() && _currentEvent->type == evtype_None && !isTimerExpired()) {
 			pollEvents();
 			g_system->delayMillis(10);
 
@@ -129,10 +129,11 @@ void Events::getEvent(event_t *event, bool polled) {
 			_currentEvent->type = evtype_Quit;
 	}
 
-	if (_currentEvent->type == evtype_None && _timeouts) {
+	if (_currentEvent->type == evtype_None && isTimerExpired()) {
 		store(evtype_Timer, nullptr, 0, 0);
 		dispatchEvent(*_currentEvent, polled);
-		_timeouts = false;
+
+		_timerTimeExpiry = g_system->getMillis() + _timerMilli;
 	}
 
 	_currentEvent = nullptr;
@@ -338,4 +339,13 @@ void Events::setCursor(CursorId cursorId) {
 	}
 }
 
+void Events::setTimerInterval(uint milli) {
+	_timerMilli = milli;
+	_timerTimeExpiry = g_system->getMillis() + milli;
+}
+
+bool Events::isTimerExpired() const {
+	return _timerMilli && g_system->getMillis() >= _timerTimeExpiry;
+}
+
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index 5da4e53..f91c91b 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -164,12 +164,13 @@ private:
 	EventQueue _eventsPolled;		///< User generated events
 	EventQueue _eventsLogged;		///< Custom events generated by game code
 	Event *_currentEvent;			///< Event pointer passed during event retrieval
-	bool _timeouts;					///< Timer timeouts flag
 	uint32 _priorFrameTime;			///< Time of prior game frame
 	uint32 _frameCounter;			///< Frame counter
 	bool _redraw;					///< Screen needed redrawing
 	CursorId _cursorId;				///< Current cursor Id
 	Surface _cursors[4];			///< Cursor pixel data
+	uint _timerMilli;				///< Time in milliseconds between timer events
+	uint _timerTimeExpiry;			///< When to trigger next timer event
 private:
 	/**
 	 * Initialize the cursor graphics
@@ -262,6 +263,17 @@ public:
 	 * Sets the current cursor
 	 */
 	void setCursor(CursorId cursorId);
+
+	/**
+	 * Set a timer interval
+	 * @param	milli		Time in millieseconds for intervals, or 0 for off
+	 */
+	void setTimerInterval(uint milli);
+
+	/**
+	 * Returns true if it's time for a timer event
+	 */
+	bool isTimerExpired() const;
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 43b6d0a..fe82edc 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -709,7 +709,7 @@ void Glk::glk_select_poll(event_t *event) {
 }
 
 void Glk::glk_request_timer_events(glui32 millisecs) {
-	// TODO
+	_events->setTimerInterval(millisecs);
 }
 
 void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {


Commit: 4bd3a4a9c692fcbc13a13eb86ec44c51a715d325
    https://github.com/scummvm/scummvm/commit/4bd3a4a9c692fcbc13a13eb86ec44c51a715d325
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Cleanup of glk_gestalt_ext switch

Changed paths:
    engines/gargoyle/glk.cpp


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index fe82edc..d815242 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -116,9 +116,6 @@ glui32 Glk::glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen) {
 			return true;
 		return false;
 
-	case gestalt_Timer:
-		return true;
-
 	case gestalt_Graphics:
 	case gestalt_GraphicsTransparency:
 		return g_conf->_graphics;
@@ -136,32 +133,21 @@ glui32 Glk::glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen) {
 	case gestalt_SoundNotify:
 		return g_conf->_sound;
 
-	case gestalt_Sound2:
-		return false;
+	case gestalt_LineTerminatorKey:
+		return Window::checkTerminator(val);
 
+	case gestalt_Timer:
 	case gestalt_Unicode:
-		return true;
 	case gestalt_UnicodeNorm:
-		return true;
-
 	case gestalt_Hyperlinks:
-		return true;
 	case gestalt_HyperlinkInput:
-		return true;
-
 	case gestalt_LineInputEcho:
-		return true;
 	case gestalt_LineTerminators:
-		return true;
-	case gestalt_LineTerminatorKey:
-		return Window::checkTerminator(val);
-
 	case gestalt_DateTime:
-		return true;
-
 	case gestalt_GarglkText:
 		return true;
 
+	case gestalt_Sound2:
 	default:
 		return false;
 	}


Commit: c524c5859e7b92b189a6ee0475f0856a71441c3a
    https://github.com/scummvm/scummvm/commit/c524c5859e7b92b189a6ee0475f0856a71441c3a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add garglk_unput_string methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index d815242..8beca88 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -1115,39 +1115,36 @@ glsi32 Glk::glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor)
 
 /* XXX non-official Glk functions that may or may not exist */
 
-char *garglk_fileref_get_name(frefid_t fref) {
-	// TODO
-	return nullptr;
+const char *Glk::garglk_fileref_get_name(frefid_t fref) const {
+	return fref->_filename.c_str();
 }
 
 void Glk::garglk_set_program_name(const char *name) {
-	// TODO
+	// Program name isn't displayed
 }
 
 void Glk::garglk_set_program_info(const char *info) {
-	// TODO
+	// Program info isn't displayed
 }
 
 void Glk::garglk_set_story_name(const char *name) {
-	// TODO
+	// Story name isn't displayed
 }
 
 void Glk::garglk_set_story_title(const char *title) {
-	// TODO
+	// Story title isn't displayed
 }
 
 void Glk::garglk_set_config(const char *name) {
-	// TODO
+	// No implementation
 }
 
-/* garglk_unput_string - removes the specified string from the end of the output buffer, if
-* indeed it is there. */
-void Glk::garglk_unput_string(char *str) {
-	// TODO
+void Glk::garglk_unput_string(const char *str) {
+	_streams->getCurrent()->unputBuffer(str, strlen(str));
 }
 
-void Glk::garglk_unput_string_uni(glui32 *str) {
-	// TODO
+void Glk::garglk_unput_string_uni(const glui32 *str) {
+	_streams->getCurrent()->unputBufferUni(str, strlen_uni(str));
 }
 
 void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 7312a35..eb3e68d 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -260,7 +260,7 @@ public:
 	/* XXX non-official Glk functions that may or may not exist */
 	#define GARGLK 1
 
-	char* garglk_fileref_get_name(frefid_t fref);
+	const char *garglk_fileref_get_name(frefid_t fref) const;
 
 	void garglk_set_program_name(const char *name);
 	void garglk_set_program_info(const char *info);
@@ -268,10 +268,17 @@ public:
 	void garglk_set_story_title(const char *title);
 	void garglk_set_config(const char *name);
 
-	/* garglk_unput_string - removes the specified string from the end of the output buffer, if
-	* indeed it is there. */
-	void garglk_unput_string(char *str);
-	void garglk_unput_string_uni(glui32 *str);
+	/**
+	 * Removes the specified string from the end of the output buffer, if
+	 * indeed it is there.
+	 */
+	void garglk_unput_string(const char *str);
+
+	/**
+	 * Removes the specified string from the end of the output buffer, if
+	 * indeed it is there.
+	 */
+	void garglk_unput_string_uni(const glui32 *str);
 
 	void garglk_set_zcolors(glui32 fg, glui32 bg);
 	void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 24917c5..0f95984 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -147,6 +147,59 @@ void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
 		_window->_echoStream->putBufferUni(buf, len);
 }
 
+void WindowStream::unputBuffer(const char *buf, size_t len) {
+	glui32 lx;
+	const char *cx;
+
+	if (!_writable)
+		return;
+
+	if (_window->_lineRequest || _window->_lineRequestUni) {
+		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+			_window->cancelLineEvent(nullptr);
+			g_vm->_events->_forceClick = false;
+		} else {
+			warning("unput_buffer: window has pending line request");
+			return;
+		}
+	}
+
+	for (lx = 0, cx = buf + len - 1; lx<len; lx++, cx--) {
+		if (!_window->unputCharUni(*cx))
+			break;
+		_writeCount--;
+	}
+	if (_window->_echoStream)
+		_window->_echoStream->unputBuffer(buf, len);
+}
+
+void WindowStream::unputBufferUni(const glui32 *buf, size_t len) {
+	glui32 lx;
+	const glui32 *cx;
+
+	if (!_writable)
+		return;
+
+	if (_window->_lineRequest || _window->_lineRequestUni) {
+		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
+			_window->cancelLineEvent(nullptr);
+			g_vm->_events->_forceClick = false;
+		} else {
+			warning("unput_buffer: window has pending line request");
+			return;
+		}
+	}
+
+	for (lx = 0, cx = buf + len - 1; lx<len; lx++, cx--) {
+		if (!_window->unputCharUni(*cx))
+			break;
+		_writeCount--;
+	}
+
+	if (_window->_echoStream)
+		_window->_echoStream->unputBufferUni(buf, len);
+}
+
 void WindowStream::setStyle(glui32 val) {
 	if (!_writable)
 		return;
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 16f63db..02e2f3b 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -192,6 +192,16 @@ public:
 	virtual void putBufferUni(const uint32 *buf, size_t len) = 0;
 
 	/**
+	 * Remove a string from the end of the stream, if indeed it is at the end
+	 */
+	virtual void unputBuffer(const char *buf, size_t len) {}
+
+	/**
+	 * Remove a string from the end of the stream, if indeed it is at the end
+	 */
+	virtual void unputBufferUni(const glui32 *buf, size_t len) {}
+
+	/**
 	 * Send a line to the stream with a trailing newline
 	 */
 	void echoLine(char *buf, glui32 len) {
@@ -288,6 +298,16 @@ public:
 	 */
 	virtual void putBufferUni(const uint32 *buf, size_t len) override;
 
+	/**
+	 * Remove a string from the end of the stream, if indeed it is at the end
+	 */
+	virtual void unputBuffer(const char *buf, size_t len) override;
+
+	/**
+	 * Remove a string from the end of the stream, if indeed it is at the end
+	 */
+	virtual void unputBufferUni(const glui32 *buf, size_t len) override;
+
 	virtual void setStyle(glui32 val) override;
 
 	/**


Commit: df8dec156efa70bb5bda1f6ef6d255c22688d952
    https://github.com/scummvm/scummvm/commit/df8dec156efa70bb5bda1f6ef6d255c22688d952
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add garglk_set_zcolors methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk_types.h
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 8beca88..6022a86 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -1148,11 +1148,15 @@ void Glk::garglk_unput_string_uni(const glui32 *str) {
 }
 
 void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
-	// TODO
+	_streams->getCurrent()->setZColors(fg, bg);
 }
 
 void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
-	// TODO
+	if (str) {
+		str->setZColors(fg, bg);
+	} else {
+		warning("set_style_stream: Invalid ref");
+	}
 }
 
 void Glk::garglk_set_reversevideo(glui32 reverse) {
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 6ea4408..6dd8504 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -179,6 +179,13 @@ enum giDisp {
 	gidisp_Class_Schannel = 3,
 };
 
+enum zcolor {
+	zcolor_Transparent = (uint32)-4,
+	zcolor_Cursor      = (uint32)-3,
+	zcolor_Current     = (uint32)-2,
+	zcolor_Default     = (uint32)-1
+};
+
 #ifdef GLK_MODULE_IMAGE
 
 enum ImageAlign {
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 0f95984..9d257f2 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -63,6 +63,11 @@ void Stream::close(StreamResult *result) {
 	delete this;
 }
 
+void Stream::setZColors(glui32 fg, glui32 bg) {
+	if (_writable && g_conf->_styleHint)
+		Windows::_forceRedraw = true;
+}
+
 /*--------------------------------------------------------------------------*/
 
 void WindowStream::close(StreamResult *result) {
@@ -217,6 +222,67 @@ void WindowStream::setHyperlink(glui32 linkVal) {
 		_window->_attr.hyper = linkVal;
 }
 
+void WindowStream::setZColors(glui32 fg, glui32 bg) {
+	if (!_writable || !g_conf->_styleHint)
+		return;
+
+	byte fore[3], back[3];
+	fore[0] = (fg >> 16) & 0xff;
+	fore[1] = (fg >> 8) & 0xff;
+	fore[2] = (fg) & 0xff;
+	back[0] = (bg >> 16) & 0xff;
+	back[1] = (bg >> 8) & 0xff;
+	back[2] = (bg) & 0xff;
+
+	if (fg != zcolor_Transparent && fg != zcolor_Cursor) {
+		if (fg == zcolor_Default) {
+			_window->_attr.fgset = 0;
+			_window->_attr.fgcolor = 0;
+			Windows::_overrideFgSet = false;
+			Windows::_overrideFgVal = 0;
+			
+			Common::copy(g_conf->_moreSave, g_conf->_moreSave + 3, g_conf->_moreColor);
+			Common::copy(g_conf->_caretSave, g_conf->_caretSave + 3, g_conf->_caretColor);
+			Common::copy(g_conf->_linkSave, g_conf->_linkSave + 3, g_conf->_linkColor);
+		} else if (fg != zcolor_Current) {
+			_window->_attr.fgset = 1;
+			_window->_attr.fgcolor = fg;
+			Windows::_overrideFgSet = true;
+			Windows::_overrideFgVal = fg;
+			
+			Common::copy(fore, fore + 3, g_conf->_moreColor);
+			Common::copy(fore, fore + 3, g_conf->_caretColor);
+			Common::copy(fore, fore + 3, g_conf->_linkColor);
+		}
+	}
+
+	if (bg != zcolor_Transparent && bg != zcolor_Cursor) {
+		if (bg == zcolor_Default) {
+			_window->_attr.bgset = 0;
+			_window->_attr.bgcolor = 0;
+			Windows::_overrideBgSet = false;
+			Windows::_overrideBgVal = 0;
+
+			Common::copy(g_conf->_windowSave, g_conf->_windowSave + 3, g_conf->_windowColor);
+			Common::copy(g_conf->_borderSave, g_conf->_borderSave + 3, g_conf->_borderColor);
+		} else if (bg != zcolor_Current) {
+			_window->_attr.bgset = 1;
+			_window->_attr.bgcolor = bg;
+			Windows::_overrideBgSet = true;
+			Windows::_overrideBgVal = bg;
+
+			Common::copy(back, back + 3, g_conf->_windowColor);
+			Common::copy(back, back + 3, g_conf->_borderColor);
+		}
+	}
+
+	Windows::_overrideReverse = !(fg == zcolor_Default && bg == zcolor_Default);
+	Windows::_forceRedraw = true;
+
+	if (_window->_echoStream)
+		_window->_echoStream->setZColors(fg, bg);
+}
+
 /*--------------------------------------------------------------------------*/
 
 MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 02e2f3b..ac25032 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -257,6 +257,11 @@ public:
 	 * Set a hyperlink
 	 */
 	virtual void setHyperlink(glui32 linkVal) {}
+
+	/**
+	 * Set the style colors
+	 */
+	virtual void setZColors(glui32 fg, glui32 bg);
 };
 typedef Stream *strid_t;
 
@@ -314,6 +319,11 @@ public:
 	 * Set a hyperlink
 	 */
 	virtual void setHyperlink(glui32 linkVal) override;
+
+	/**
+	 * Set the style colors
+	 */
+	virtual void setZColors(glui32 fg, glui32 bg) override;
 };
 
 /**


Commit: 7cb69167de7a024f4d79df5f967397a0737bd574
    https://github.com/scummvm/scummvm/commit/7cb69167de7a024f4d79df5f967397a0737bd574
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add the garglk_set_reversevideo methods

Changed paths:
    engines/gargoyle/glk.cpp
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 6022a86..dc91d4c 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -1160,11 +1160,15 @@ void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
 }
 
 void Glk::garglk_set_reversevideo(glui32 reverse) {
-	// TODO
+	_streams->getCurrent()->setReverseVideo(reverse != 0);
 }
 
 void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
-	// TODO
+	if (str) {
+		str->setReverseVideo(reverse != 0);
+	} else {
+		warning("set_reversevideo: Invalid ref");
+	}
 }
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 9d257f2..b301272 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -68,6 +68,11 @@ void Stream::setZColors(glui32 fg, glui32 bg) {
 		Windows::_forceRedraw = true;
 }
 
+void Stream::setReverseVideo(bool reverse) {
+	if (_writable && g_conf->_styleHint)
+		Windows::_forceRedraw = true;
+}
+
 /*--------------------------------------------------------------------------*/
 
 void WindowStream::close(StreamResult *result) {
@@ -283,6 +288,17 @@ void WindowStream::setZColors(glui32 fg, glui32 bg) {
 		_window->_echoStream->setZColors(fg, bg);
 }
 
+void WindowStream::setReverseVideo(bool reverse) {
+	if (!_writable || !g_conf->_styleHint)
+		return;
+
+	_window->_attr.reverse = reverse;
+	if (_window->_echoStream)
+		_window->_echoStream->setReverseVideo(reverse);
+
+	Windows::_forceRedraw = true;
+}
+
 /*--------------------------------------------------------------------------*/
 
 MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index ac25032..c8b6963 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -262,6 +262,11 @@ public:
 	 * Set the style colors
 	 */
 	virtual void setZColors(glui32 fg, glui32 bg);
+
+	/**
+	 * Set the reverse video style
+	 */
+	virtual void setReverseVideo(bool reverse);
 };
 typedef Stream *strid_t;
 
@@ -324,6 +329,11 @@ public:
 	 * Set the style colors
 	 */
 	virtual void setZColors(glui32 fg, glui32 bg) override;
+
+	/**
+	 * Set the reverse video style
+	 */
+	virtual void setReverseVideo(bool reverse) override;
 };
 
 /**


Commit: 5ddf55da6cb9ba283229d2a06014ab5b88f6bfb4
    https://github.com/scummvm/scummvm/commit/5ddf55da6cb9ba283229d2a06014ab5b88f6bfb4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Convert comments to single line veresions

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


diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index dc91d4c..25ce3cc 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -1090,7 +1090,7 @@ void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *d
 }
 
 void Glk::glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time) {
-	// TODO: timezones aren't currently supported
+	// WORKAROUND: timezones aren't currently supported
 	*time = TimeAndDate(*date);
 }
 
@@ -1099,7 +1099,7 @@ void Glk::glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time) {
 }
 
 glsi32 Glk::glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor) {
-	// TODO: timezones aren't currently supported
+	// WORKAROUND: timezones aren't currently supported
 	assert(factor);
 	TimeSeconds ts = TimeAndDate(*date);
 	return ts / factor;
@@ -1113,7 +1113,7 @@ glsi32 Glk::glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor)
 
 /*--------------------------------------------------------------------------*/
 
-/* XXX non-official Glk functions that may or may not exist */
+/* XXX non-official Glk functions */
 
 const char *Glk::garglk_fileref_get_name(frefid_t fref) const {
 	return fref->_filename.c_str();
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index b0c3444..8728cdb 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -101,7 +101,7 @@ Distributed under the GNU software license\n\n");
 			break;
 		}
 
-		/* Brian Howarth games seem to use -1 for forever */
+		// Brian Howarth games seem to use -1 for forever
 		if (Items[LIGHT_SOURCE].Location/*==-1*/ != DESTROYED && GameHeader.LightTime != -1) {
 			GameHeader.LightTime--;
 			if (GameHeader.LightTime < 1) {
@@ -208,7 +208,7 @@ int Scott::countCarried(void) {
 const char *Scott::mapSynonym(const char *word) {
 	int n = 1;
 	const char *tp;
-	static char lastword[16];	/* Last non synonym */
+	static char lastword[16];	// Last non synonym
 	while (n <= GameHeader.NumWords) {
 		tp = Nouns[n];
 		if (*tp == '*')
@@ -264,20 +264,16 @@ char *Scott::readString(Common::SeekableReadStream *f) {
 			}
 		}
 		if (c == '`')
-			c = '"'; /* pdd */
+			c = '"'; // pdd
 
-					 /* Ensure a valid Glk newline is sent. */
+		// Ensure a valid Glk newline is sent.
 		if (c == '\n')
 			tmp[ct++] = 10;
-		/* Special case: assume CR is part of CRLF in a
-		* DOS-formatted file, and ignore it.
-		*/
+		// Special case: assume CR is part of CRLF in a DOS-formatted file, and ignore it.
 		else if (c == 13)
 			;
-		/* Pass only ASCII to Glk; the other reasonable option
-		* would be to pass Latin-1, but it's probably safe to
-		* assume that Scott Adams games are ASCII only.
-		*/
+		// Pass only ASCII to Glk; the other reasonable option would be to pass Latin-1,
+		// but it's probably safe to assume that Scott Adams games are ASCII only.
 		else if ((c >= 32 && c <= 126))
 			tmp[ct++] = c;
 		else
@@ -297,8 +293,8 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	Action *ap;
 	Room *rp;
 	Item *ip;
-	/* Load the header */
-
+	
+	// Load the header
 	readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
 
 	GameHeader.NumItems = ni;
@@ -320,8 +316,7 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	Messages = (const char **)memAlloc(sizeof(char *)*(mn + 1));
 	GameHeader.TreasureRoom = trm;
 
-	/* Load the actions */
-
+	// Load the actions
 	ct = 0;
 	ap = Actions;
 	if (loud)
@@ -377,7 +372,7 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	while (ct < ni + 1) {
 		ip->Text = readString(f);
 		ip->AutoGet = strchr(ip->Text, '/');
-		/* Some games use // to mean no auto get/drop word! */
+		// Some games use // to mean no auto get/drop word!
 		if (ip->AutoGet && strcmp(ip->AutoGet, "//") && strcmp(ip->AutoGet, "/*")) {
 			char *t;
 			*ip->AutoGet++ = 0;
@@ -393,7 +388,7 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 		ct++;
 	}
 	ct = 0;
-	/* Discard Comment Strings */
+	// Discard Comment Strings
 	while (ct<na + 1) {
 		free(readString(f));
 		ct++;
@@ -654,12 +649,12 @@ int Scott::getInput(int *vb, int *no) {
 			case 'w':strcpy(verb, "WEST"); break;
 			case 'u':strcpy(verb, "UP"); break;
 			case 'd':strcpy(verb, "DOWN"); break;
-				/* Brian Howarth interpreter also supports this */
+			// Brian Howarth interpreter also supports this
 			case 'i':strcpy(verb, "INVENTORY"); break;
 			}
 		}
 		nc = whichWord(verb, Nouns);
-		/* The Scott Adams system has a hack to avoid typing 'go' */
+		// The Scott Adams system has a hack to avoid typing 'go'
 		if (nc >= 1 && nc <= 6) {
 			vc = 1;
 		} else {
@@ -673,7 +668,7 @@ int Scott::getInput(int *vb, int *no) {
 		}
 	} while (vc == -1);
 
-	strcpy(NounText, noun);	/* Needed by GET/DROP hack */
+	strcpy(NounText, noun);	// Needed by GET/DROP hack
 	return 0;
 }
 
@@ -765,14 +760,16 @@ int Scott::performLine(int ct) {
 			if (Items[dv].Location == Items[dv].InitialLoc)
 				return 0;
 			break;
-		case 19:/* Only seen in Brian Howarth games so far */
+		case 19:
+			// Only seen in Brian Howarth games so far
 			if (CurrentCounter != dv)
 				return 0;
 			break;
 		}
 		cc++;
 	}
-	/* Actions */
+
+	// Actions
 	act[0] = Actions[ct].action[0];
 	act[2] = Actions[ct].action[1];
 	act[1] = act[0] % 150;
@@ -789,15 +786,13 @@ int Scott::performLine(int ct) {
 		} else if (act[cc] > 101) {
 			output(Messages[act[cc] - 50]);
 			output("\n");
-		}
-		else {
+		} else {
 			switch (act[cc]) {
-			case 0:/* NOP */
+			case 0:// NOP
 				break;
 			case 52:
-				if (countCarried() == GameHeader.MaxCarry)
-				{
-					if (Options&YOUARE)
+				if (countCarried() == GameHeader.MaxCarry) {
+					if (Options & YOUARE)
 						output("You are carrying too much. ");
 					else
 						output("I've too much to carry! ");
@@ -835,27 +830,25 @@ int Scott::performLine(int ct) {
 				else
 					output("I am dead.\n");
 				BitFlags &= ~(1 << DARKBIT);
-				MyLoc = GameHeader.NumRooms;/* It seems to be what the code says! */
+				MyLoc = GameHeader.NumRooms;// It seems to be what the code says!
 				break;
-			case 62:
-			{
-				/* Bug fix for some systems - before it could get parameters wrong */
+			case 62: {
+				// Bug fix for some systems - before it could get parameters wrong */
 				int i = param[pptr++];
 				Items[i].Location = param[pptr++];
 				break;
 			}
 			case 63:
-				doneit:				output("The game is now over.\n");
-									glk_exit();
-									break;
+doneit:
+				output("The game is now over.\n");
+				glk_exit();
+				break;
 			case 64:
 				break;
-			case 65:
-			{
+			case 65: {
 				int i = 0;
 				int n = 0;
-				while (i <= GameHeader.NumItems)
-				{
+				while (i <= GameHeader.NumItems) {
 					if (Items[i].Location == GameHeader.TreasureRoom &&
 						*Items[i].Text == '*')
 						n++;
@@ -869,27 +862,22 @@ int Scott::performLine(int ct) {
 				output(" treasures.  On a scale of 0 to 100, that rates ");
 				outputNumber((n * 100) / GameHeader.Treasures);
 				output(".\n");
-				if (n == GameHeader.Treasures)
-				{
+				if (n == GameHeader.Treasures) {
 					output("Well done.\n");
 					goto doneit;
 				}
 				break;
 			}
-			case 66:
-			{
+			case 66: {
 				int i = 0;
 				int f = 0;
 				if (Options&YOUARE)
 					output("You are carrying:\n");
 				else
 					output("I'm carrying:\n");
-				while (i <= GameHeader.NumItems)
-				{
-					if (Items[i].Location == CARRIED)
-					{
-						if (f == 1)
-						{
+				while (i <= GameHeader.NumItems) {
+					if (Items[i].Location == CARRIED) {
+						if (f == 1) {
 							if (Options & TRS80_STYLE)
 								output(". ");
 							else
@@ -917,13 +905,12 @@ int Scott::performLine(int ct) {
 				BitFlags &= ~(1 << LIGHTOUTBIT);
 				break;
 			case 70:
-				clearScreen(); /* pdd. */
+				clearScreen(); // pdd.
 				break;
 			case 71:
 				saveGame();
 				break;
-			case 72:
-			{
+			case 72: {
 				int i1 = param[pptr++];
 				int i2 = param[pptr++];
 				int t = Items[i1].Location;
@@ -937,15 +924,15 @@ int Scott::performLine(int ct) {
 			case 74:
 				Items[param[pptr++]].Location = CARRIED;
 				break;
-			case 75:
-			{
+			case 75: {
 				int i1, i2;
 				i1 = param[pptr++];
 				i2 = param[pptr++];
 				Items[i1].Location = Items[i2].Location;
 				break;
 			}
-			case 76:	/* Looking at adventure .. */
+			case 76:
+				// Looking at adventure ..
 				break;
 			case 77:
 				if (CurrentCounter >= 0)
@@ -957,19 +944,16 @@ int Scott::performLine(int ct) {
 			case 79:
 				CurrentCounter = param[pptr++];
 				break;
-			case 80:
-			{
+			case 80: {
 				int t = MyLoc;
 				MyLoc = SavedRoom;
 				SavedRoom = t;
 				break;
 			}
-			case 81:
-			{
-				/* This is somewhat guessed. Claymorgue always
-				seems to do select counter n, thing, select counter n,
-				but uses one value that always seems to exist. Trying
-				a few options I found this gave sane results on ageing */
+			case 81: {
+				// This is somewhat guessed. Claymorgue always seems to do
+				// select counter n, thing, select counter n, but uses one value that always
+				// seems to exist. Trying a few options I found this gave sane results on ageing
 				int t = param[pptr++];
 				int c1 = CurrentCounter;
 				CurrentCounter = Counters[t];
@@ -983,8 +967,8 @@ int Scott::performLine(int ct) {
 				CurrentCounter -= param[pptr++];
 				if (CurrentCounter < -1)
 					CurrentCounter = -1;
-				/* Note: This seems to be needed. I don't yet
-				know if there is a maximum value to limit too */
+				// Note: This seems to be needed. I don't yet know if there
+				// is a maximum value to limit too
 				break;
 			case 84:
 				output(NounText);
@@ -996,10 +980,8 @@ int Scott::performLine(int ct) {
 			case 86:
 				output("\n");
 				break;
-			case 87:
-			{
-				/* Changed this to swap location<->roomflag[x]
-				not roomflag 0 and x */
+			case 87: {
+				// Changed this to swap location<->roomflag[x] not roomflag 0 and x
 				int p = param[pptr++];
 				int sr = MyLoc;
 				MyLoc = RoomSaved[p];
@@ -1011,9 +993,9 @@ int Scott::performLine(int ct) {
 				break;
 			case 89:
 				pptr++;
-				/* SAGA draw picture n */
-				/* Spectrum Seas of Blood - start combat ? */
-				/* Poking this into older spectrum games causes a crash */
+				// SAGA draw picture n
+				// Spectrum Seas of Blood - start combat ?
+				// Poking this into older spectrum games causes a crash
 				break;
 			default:
 				error("Unknown action %d [Param begins %d %d]\n",
@@ -1029,7 +1011,7 @@ int Scott::performLine(int ct) {
 }
 
 int Scott::performActions(int vb, int no) {
-	static int disable_sysfunc = 0;	/* Recursion lock */
+	static int disable_sysfunc = 0;	// Recursion lock
 	int d = BitFlags&(1 << DARKBIT);
 
 	int ct = 0;
@@ -1069,11 +1051,11 @@ int Scott::performActions(int vb, int no) {
 	while (ct <= GameHeader.NumActions) {
 		int vv, nv;
 		vv = Actions[ct].Vocab;
-		/* Think this is now right. If a line we run has an action73
-		run all following lines with vocab of 0,0 */
+		// Think this is now right. If a line we run has an action73
+		// run all following lines with vocab of 0,0
 		if (vb != 0 && (doagain&&vv != 0))
 			break;
-		/* Oops.. added this minor cockup fix 1.11 */
+		// Oops.. added this minor cockup fix 1.11
 		if (vb != 0 && !doagain && fl == 0)
 			break;
 		nv = vv % 150;
@@ -1085,7 +1067,7 @@ int Scott::performActions(int vb, int no) {
 				if (fl == -1)
 					fl = -2;
 				if ((f2 = performLine(ct)) > 0) {
-					/* ahah finally figured it out ! */
+					// ahah finally figured it out !
 					fl = 0;
 					if (f2 == 2)
 						doagain = 1;
@@ -1096,13 +1078,10 @@ int Scott::performActions(int vb, int no) {
 		}
 		ct++;
 
-		/* Previously this did not check ct against
-		* GameHeader.NumActions and would read past the end of
-		* Actions.  I don't know what should happen on the last
-		* action, but doing nothing is better than reading one
-		* past the end.
-		* --Chris
-		*/
+		// Previously this did not check ct against GameHeader.NumActions and would read
+		// past the end of Actions.  I don't know what should happen on the last action,
+		// but doing nothing is better than reading one past the end.
+		// --Chris
 		if (ct <= GameHeader.NumActions && Actions[ct].Vocab != 0)
 			doagain = 0;
 	}
@@ -1112,7 +1091,7 @@ int Scott::performActions(int vb, int no) {
 			Items[LIGHT_SOURCE].Location == CARRIED)
 			d = 0;
 		if (vb == 10 || vb == 18) {
-			/* Yes they really _are_ hardcoded values */
+			// Yes they really _are_ hardcoded values
 			if (vb == 10) {
 				if (xstrcasecmp(NounText, "ALL") == 0) {
 					int i = 0;
@@ -1125,8 +1104,8 @@ int Scott::performActions(int vb, int no) {
 					while (i <= GameHeader.NumItems) {
 						if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
 							no = whichWord(Items[i].AutoGet, Nouns);
-							disable_sysfunc = 1;	/* Don't recurse into auto get ! */
-							performActions(vb, no);	/* Recursively check each items table code */
+							disable_sysfunc = 1;	// Don't recurse into auto get !
+							performActions(vb, no);	// Recursively check each items table code
 							disable_sysfunc = 0;
 							if (countCarried() == GameHeader.MaxCarry) {
 								if (Options&YOUARE)
@@ -1146,13 +1125,11 @@ int Scott::performActions(int vb, int no) {
 						output("Nothing taken.");
 					return 0;
 				}
-				if (no == -1)
-				{
+				if (no == -1) {
 					output("What ? ");
 					return 0;
 				}
-				if (countCarried() == GameHeader.MaxCarry)
-				{
+				if (countCarried() == GameHeader.MaxCarry) {
 					if (Options&YOUARE)
 						output("You are carrying too much. ");
 					else
@@ -1160,8 +1137,7 @@ int Scott::performActions(int vb, int no) {
 					return 0;
 				}
 				item = matchUpItem(NounText, MyLoc);
-				if (item == -1)
-				{
+				if (item == -1) {
 					if (Options&YOUARE)
 						output("It is beyond your power to do that. ");
 					else


Commit: a8e656a5de01e18697fa4f0086215d11d9143197
    https://github.com/scummvm/scummvm/commit/a8e656a5de01e18697fa4f0086215d11d9143197
Author: dreammaster (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: astyle formatting

Changed paths:
    engines/gargoyle/conf.cpp
    engines/gargoyle/detection.cpp
    engines/gargoyle/detection_tables.h
    engines/gargoyle/events.cpp
    engines/gargoyle/events.h
    engines/gargoyle/fonts.h
    engines/gargoyle/gargoyle.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/scott/scott.cpp
    engines/gargoyle/scott/scott.h
    engines/gargoyle/screen.h
    engines/gargoyle/selection.cpp
    engines/gargoyle/selection.h
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h
    engines/gargoyle/unicode.cpp
    engines/gargoyle/unicode.h
    engines/gargoyle/unicode_gen.cpp
    engines/gargoyle/unicode_gen.h
    engines/gargoyle/window_graphics.cpp
    engines/gargoyle/window_graphics.h
    engines/gargoyle/window_pair.cpp
    engines/gargoyle/window_text_buffer.cpp
    engines/gargoyle/window_text_buffer.h
    engines/gargoyle/window_text_grid.cpp
    engines/gargoyle/window_text_grid.h
    engines/gargoyle/windows.cpp
    engines/gargoyle/windows.h


diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
index 7add133..9a0d648 100644
--- a/engines/gargoyle/conf.cpp
+++ b/engines/gargoyle/conf.cpp
@@ -35,31 +35,31 @@ const byte SCROLL_BG[3] = { 0xb0, 0xb0, 0xb0 };
 const byte SCROLL_FG[3] = { 0x80, 0x80, 0x80 };
 
 WindowStyle T_STYLES[style_NUMSTYLES] = {
-	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Normal
-	{ PROPI,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Emphasized
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Preformatted
-	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Header
-	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Subheader
-	{ PROPZ,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Alert
-	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< Note
-	{ PROPR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< BlockQuote
-	{ PROPB,{ 0xff,0xff,0xff },{ 0x00,0x60,0x00 }, 0 }, ///< Input
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User1
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x00,0x00,0x00 }, 0 }, ///< User2
+	{ PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Normal
+	{ PROPI, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Emphasized
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Preformatted
+	{ PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Header
+	{ PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Subheader
+	{ PROPZ, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Alert
+	{ PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Note
+	{ PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< BlockQuote
+	{ PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x60, 0x00 }, 0 }, ///< Input
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< User1
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< User2
 };
 
 WindowStyle G_STYLES[style_NUMSTYLES] = {
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Normal
-	{ MONOI,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Emphasized
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Preformatted
-	{ MONOB,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Header
-	{ MONOB,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Subheader
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Alert
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Note
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< BlockQuote
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< Input
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< User1
-	{ MONOR,{ 0xff,0xff,0xff },{ 0x60,0x60,0x60 }, 0 }, ///< User2
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Normal
+	{ MONOI, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Emphasized
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Preformatted
+	{ MONOB, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Header
+	{ MONOB, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Subheader
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Alert
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Note
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< BlockQuote
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Input
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< User1
+	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< User2
 };
 
 Conf *g_conf;
@@ -122,7 +122,7 @@ Conf::Conf() {
 	get("tmarginx", _tMarginX, 7);
 	get("tmarginy", _tMarginY, 7);
 	get("gamma", _gamma, 1.0);
-	
+
 	get("caretcolor", _caretColor);
 	get("caretcolor", _caretSave);
 	get("linkcolor", _linkColor, BLUE);
@@ -235,9 +235,15 @@ void Conf::parseColor(const Common::String &str, byte *color) {
 	char r[3], g[3], b[3];
 
 	if (str.size() == 6) {
-		r[0] = str[0]; r[1] = str[1]; r[2] = 0;
-		g[0] = str[2]; g[1] = str[3]; g[2] = 0;
-		b[0] = str[4]; b[1] = str[5]; b[2] = 0;
+		r[0] = str[0];
+		r[1] = str[1];
+		r[2] = 0;
+		g[0] = str[2];
+		g[1] = str[3];
+		g[2] = 0;
+		b[0] = str[4];
+		b[1] = str[5];
+		b[2] = 0;
 
 		color[0] = strtol(r, nullptr, 16);
 		color[1] = strtol(g, nullptr, 16);
diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index af7cfc6..4363c46 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -125,20 +125,20 @@ public:
 
 bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
-		(f == kSupportsListSaves) ||
-		(f == kSupportsLoadingDuringStartup) ||
-		(f == kSupportsDeleteSave) ||
-		(f == kSavesSupportMetaInfo) ||
-		(f == kSavesSupportCreationDate) ||
-		(f == kSavesSupportPlayTime) ||
-		(f == kSimpleSavesNames);
+	    (f == kSupportsListSaves) ||
+	    (f == kSupportsLoadingDuringStartup) ||
+	    (f == kSupportsDeleteSave) ||
+	    (f == kSavesSupportMetaInfo) ||
+	    (f == kSavesSupportCreationDate) ||
+	    (f == kSavesSupportPlayTime) ||
+	    (f == kSimpleSavesNames);
 }
 
 bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
 	return
-		(f == kSupportsRTL) ||
-		(f == kSupportsLoadingDuringRuntime) ||
-		(f == kSupportsSavingDuringRuntime);
+	    (f == kSupportsRTL) ||
+	    (f == kSupportsLoadingDuringRuntime) ||
+	    (f == kSupportsSavingDuringRuntime);
 }
 
 bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
@@ -255,7 +255,7 @@ ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, con
 }
 
 #if PLUGIN_ENABLED_DYNAMIC(GARGOYLE)
-	REGISTER_PLUGIN_DYNAMIC(Gargoyle, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
+REGISTER_PLUGIN_DYNAMIC(Gargoyle, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
 #else
-	REGISTER_PLUGIN_STATIC(GARGOYLE, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
+REGISTER_PLUGIN_STATIC(GARGOYLE, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
 #endif
diff --git a/engines/gargoyle/detection_tables.h b/engines/gargoyle/detection_tables.h
index 780cfe2..777d4db 100644
--- a/engines/gargoyle/detection_tables.h
+++ b/engines/gargoyle/detection_tables.h
@@ -21,7 +21,7 @@
  */
 
 namespace Gargoyle {
-	
+
 static const GargoyleGameDescription gameDescriptions[] = {
 	{
 		{
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
index 520b6f2..6c0cd71 100644
--- a/engines/gargoyle/events.cpp
+++ b/engines/gargoyle/events.cpp
@@ -54,7 +54,7 @@ const byte ARROW[] = {
 };
 
 Events::Events() : _forceClick(false), _currentEvent(nullptr), _cursorId(CURSOR_NONE),
-		_timerMilli(0), _timerTimeExpiry(0), _priorFrameTime(0), _frameCounter(0) {
+	_timerMilli(0), _timerTimeExpiry(0), _priorFrameTime(0), _frameCounter(0) {
 	initializeCursors();
 }
 
@@ -239,34 +239,85 @@ void Events::handleKeyDown(const Common::KeyState &ks) {
 		return;
 
 	switch (ks.keycode) {
-	case Common::KEYCODE_RETURN: windows.inputHandleKey(keycode_Return); break;
-	case Common::KEYCODE_BACKSPACE: windows.inputHandleKey(keycode_Delete); break;
-	case Common::KEYCODE_DELETE: windows.inputHandleKey(keycode_Erase); break;
-	case Common::KEYCODE_TAB: windows.inputHandleKey(keycode_Tab); break;
-	case Common::KEYCODE_PAGEUP: windows.inputHandleKey(keycode_PageUp); break;
-	case Common::KEYCODE_PAGEDOWN: windows.inputHandleKey(keycode_PageDown); break;
-	case Common::KEYCODE_HOME: windows.inputHandleKey(keycode_Home); break;
-	case Common::KEYCODE_END: windows.inputHandleKey(keycode_End); break;
-	case Common::KEYCODE_LEFT: windows.inputHandleKey(keycode_Left); break;
-	case Common::KEYCODE_RIGHT: windows.inputHandleKey(keycode_Right); break;
-	case Common::KEYCODE_UP: windows.inputHandleKey(keycode_Up); break;
-	case Common::KEYCODE_DOWN: windows.inputHandleKey(keycode_Down); break;
-	case Common::KEYCODE_ESCAPE: windows.inputHandleKey(keycode_Escape); break;
-	case Common::KEYCODE_F1: windows.inputHandleKey(keycode_Func1); break;
-	case Common::KEYCODE_F2: windows.inputHandleKey(keycode_Func2); break;
-	case Common::KEYCODE_F3: windows.inputHandleKey(keycode_Func3); break;
-	case Common::KEYCODE_F4: windows.inputHandleKey(keycode_Func4); break;
-	case Common::KEYCODE_F5: windows.inputHandleKey(keycode_Func5); break;
-	case Common::KEYCODE_F6: windows.inputHandleKey(keycode_Func6); break;
-	case Common::KEYCODE_F7: windows.inputHandleKey(keycode_Func7); break;
-	case Common::KEYCODE_F8: windows.inputHandleKey(keycode_Func8); break;
-	case Common::KEYCODE_F9: windows.inputHandleKey(keycode_Func9); break;
-	case Common::KEYCODE_F10: windows.inputHandleKey(keycode_Func10); break;
-	case Common::KEYCODE_F11: windows.inputHandleKey(keycode_Func11); break;
-	case Common::KEYCODE_F12: windows.inputHandleKey(keycode_Func12); break;
+	case Common::KEYCODE_RETURN:
+		windows.inputHandleKey(keycode_Return);
+		break;
+	case Common::KEYCODE_BACKSPACE:
+		windows.inputHandleKey(keycode_Delete);
+		break;
+	case Common::KEYCODE_DELETE:
+		windows.inputHandleKey(keycode_Erase);
+		break;
+	case Common::KEYCODE_TAB:
+		windows.inputHandleKey(keycode_Tab);
+		break;
+	case Common::KEYCODE_PAGEUP:
+		windows.inputHandleKey(keycode_PageUp);
+		break;
+	case Common::KEYCODE_PAGEDOWN:
+		windows.inputHandleKey(keycode_PageDown);
+		break;
+	case Common::KEYCODE_HOME:
+		windows.inputHandleKey(keycode_Home);
+		break;
+	case Common::KEYCODE_END:
+		windows.inputHandleKey(keycode_End);
+		break;
+	case Common::KEYCODE_LEFT:
+		windows.inputHandleKey(keycode_Left);
+		break;
+	case Common::KEYCODE_RIGHT:
+		windows.inputHandleKey(keycode_Right);
+		break;
+	case Common::KEYCODE_UP:
+		windows.inputHandleKey(keycode_Up);
+		break;
+	case Common::KEYCODE_DOWN:
+		windows.inputHandleKey(keycode_Down);
+		break;
+	case Common::KEYCODE_ESCAPE:
+		windows.inputHandleKey(keycode_Escape);
+		break;
+	case Common::KEYCODE_F1:
+		windows.inputHandleKey(keycode_Func1);
+		break;
+	case Common::KEYCODE_F2:
+		windows.inputHandleKey(keycode_Func2);
+		break;
+	case Common::KEYCODE_F3:
+		windows.inputHandleKey(keycode_Func3);
+		break;
+	case Common::KEYCODE_F4:
+		windows.inputHandleKey(keycode_Func4);
+		break;
+	case Common::KEYCODE_F5:
+		windows.inputHandleKey(keycode_Func5);
+		break;
+	case Common::KEYCODE_F6:
+		windows.inputHandleKey(keycode_Func6);
+		break;
+	case Common::KEYCODE_F7:
+		windows.inputHandleKey(keycode_Func7);
+		break;
+	case Common::KEYCODE_F8:
+		windows.inputHandleKey(keycode_Func8);
+		break;
+	case Common::KEYCODE_F9:
+		windows.inputHandleKey(keycode_Func9);
+		break;
+	case Common::KEYCODE_F10:
+		windows.inputHandleKey(keycode_Func10);
+		break;
+	case Common::KEYCODE_F11:
+		windows.inputHandleKey(keycode_Func11);
+		break;
+	case Common::KEYCODE_F12:
+		windows.inputHandleKey(keycode_Func12);
+		break;
 	default:
-		windows.inputHandleKey(ks.ascii); break;
-	break;
+		windows.inputHandleKey(ks.ascii);
+		break;
+		break;
 	}
 }
 
@@ -317,8 +368,8 @@ void Events::waitForPress() {
 		g_system->delayMillis(10);
 		checkForNextFrameCounter();
 	} while (!g_vm->shouldQuit() && e.type != Common::EVENT_KEYDOWN &&
-		e.type != Common::EVENT_LBUTTONDOWN && e.type != Common::EVENT_RBUTTONDOWN &&
-		e.type != Common::EVENT_MBUTTONDOWN);
+	         e.type != Common::EVENT_LBUTTONDOWN && e.type != Common::EVENT_RBUTTONDOWN &&
+	         e.type != Common::EVENT_MBUTTONDOWN);
 }
 
 void Events::setCursor(CursorId cursorId) {
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index f91c91b..b69cd04 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -50,7 +50,7 @@ enum EvType {
 	evtype_VolumeNotify = 9,
 
 	// ScummVM custom events
-	evtype_Quit			= 99
+	evtype_Quit         = 99
 };
 
 /**
@@ -115,7 +115,9 @@ struct Event {
 	/**
 	 * Constructor
 	 */
-	Event() { clear(); }
+	Event() {
+		clear();
+	}
 
 	/**
 	 * Constructor
@@ -139,7 +141,9 @@ struct Event {
 	/**
 	 * Boolean cast to allow checking whether event is filled out
 	 */
-	operator bool() const { return type != evtype_None; }
+	operator bool() const {
+		return type != evtype_None;
+	}
 };
 typedef Event event_t;
 
@@ -161,16 +165,16 @@ class Events {
 		Common::Point _hotspot;
 	};
 private:
-	EventQueue _eventsPolled;		///< User generated events
-	EventQueue _eventsLogged;		///< Custom events generated by game code
-	Event *_currentEvent;			///< Event pointer passed during event retrieval
-	uint32 _priorFrameTime;			///< Time of prior game frame
-	uint32 _frameCounter;			///< Frame counter
-	bool _redraw;					///< Screen needed redrawing
-	CursorId _cursorId;				///< Current cursor Id
-	Surface _cursors[4];			///< Cursor pixel data
-	uint _timerMilli;				///< Time in milliseconds between timer events
-	uint _timerTimeExpiry;			///< When to trigger next timer event
+	EventQueue _eventsPolled;       ///< User generated events
+	EventQueue _eventsLogged;       ///< Custom events generated by game code
+	Event *_currentEvent;           ///< Event pointer passed during event retrieval
+	uint32 _priorFrameTime;         ///< Time of prior game frame
+	uint32 _frameCounter;           ///< Frame counter
+	bool _redraw;                   ///< Screen needed redrawing
+	CursorId _cursorId;             ///< Current cursor Id
+	Surface _cursors[4];            ///< Cursor pixel data
+	uint _timerMilli;               ///< Time in milliseconds between timer events
+	uint _timerTimeExpiry;          ///< When to trigger next timer event
 private:
 	/**
 	 * Initialize the cursor graphics
@@ -247,17 +251,23 @@ public:
 	/**
 	 * Get the total number of frames played
 	 */
-	uint32 getTotalPlayTicks() const { return _frameCounter; }
+	uint32 getTotalPlayTicks() const {
+		return _frameCounter;
+	}
 
 	/**
 	 * Set the total number of frames played
 	 */
-	void Events::setTotalPlayTicks(uint frames) { _frameCounter = frames; }
+	void Events::setTotalPlayTicks(uint frames) {
+		_frameCounter = frames;
+	}
 
 	/**
 	 * Flags the screen for redrawing
 	 */
-	void redraw() { _redraw = true; }
+	void redraw() {
+		_redraw = true;
+	}
 
 	/**
 	 * Sets the current cursor
@@ -266,7 +276,7 @@ public:
 
 	/**
 	 * Set a timer interval
-	 * @param	milli		Time in millieseconds for intervals, or 0 for off
+	 * @param   milli       Time in millieseconds for intervals, or 0 for off
 	 */
 	void setTimerInterval(uint milli);
 
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
index 1437bd8..737618e 100644
--- a/engines/gargoyle/fonts.h
+++ b/engines/gargoyle/fonts.h
@@ -76,39 +76,39 @@ public:
 
 	/**
 	 * Draws a string using the specified font at the given co-ordinates
-	 * @param pos		Position for the bottom-left corner the text will be drawn with
-	 * @param fontIdx	Which font to use
-	 * @param rgb		RGB tuplet specifying the text color
-	 * @param text		The text to draw
-	 * @param spw		??
+	 * @param pos       Position for the bottom-left corner the text will be drawn with
+	 * @param fontIdx   Which font to use
+	 * @param rgb       RGB tuplet specifying the text color
+	 * @param text      The text to draw
+	 * @param spw       ??
 	 */
 	int drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
 
 	/**
 	 * Draws a unicode string using the specified font at the given co-ordinates
-	 * @param pos		Position for the bottom-left corner the text will be drawn with
-	 * @param fontIdx	Which font to use
-	 * @param rgb		RGB tuplet specifying the text color
-	 * @param text		The text to draw
-	 * @param spw		??
+	 * @param pos       Position for the bottom-left corner the text will be drawn with
+	 * @param fontIdx   Which font to use
+	 * @param rgb       RGB tuplet specifying the text color
+	 * @param text      The text to draw
+	 * @param spw       ??
 	 */
 	int drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
 
 	/**
 	 * Get the width in pixels of a string
 	 * @param fontIdx   Which font to use
-	 * @param text		Text to get the width of
-	 * @param spw		Delta X
-	 * @returns			Width of string multiplied by GLI_SUBPIX
+	 * @param text      Text to get the width of
+	 * @param spw       Delta X
+	 * @returns         Width of string multiplied by GLI_SUBPIX
 	 */
 	size_t stringWidth(int fontIdx, const Common::String &text, int spw = 0);
 
 	/**
 	 * Get the width in pixels of a unicode string
 	 * @param fontIdx   Which font to use
-	 * @param text		Text to get the width of
-	 * @param spw		Delta X
-	 * @returns			Width of string multiplied by GLI_SUBPIX
+	 * @param text      Text to get the width of
+	 * @param spw       Delta X
+	 * @returns         Width of string multiplied by GLI_SUBPIX
 	 */
 	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = 0);
 };
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 9c00937..021323c 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -42,10 +42,10 @@ namespace Gargoyle {
 GargoyleEngine *g_vm;
 
 GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-		_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _clipboard(nullptr),
-		_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr),
-		_selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
-		gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
+	_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _clipboard(nullptr),
+	_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr),
+	_selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
+	gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
 	g_vm = this;
 }
 
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 793a353..e131be1 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -62,8 +62,8 @@ enum InterpreterType {
 
 enum GargoyleDebugChannels {
 	kDebugCore      = 1 << 0,
-	kDebugScripts	= 1 << 1,
-	kDebugGraphics	= 1 << 2,
+	kDebugScripts   = 1 << 1,
+	kDebugGraphics  = 1 << 2,
 	kDebugSound     = 1 << 3
 };
 
@@ -125,12 +125,16 @@ public:
 	/**
 	 * Returns true if a savegame can be loaded
 	 */
-	virtual bool canLoadGameStateCurrently() override { return true; }
+	virtual bool canLoadGameStateCurrently() override {
+		return true;
+	}
 
 	/**
 	 * Returns true if the game can be saved
 	 */
-	virtual bool canSaveGameStateCurrently() override { return true; }
+	virtual bool canSaveGameStateCurrently() override {
+		return true;
+	}
 
 	/**
 	 * Returns the bitset of game features
@@ -165,7 +169,9 @@ public:
 	/**
 	 * Return the game engine's target name
 	 */
-	const Common::String &getTargetName() const { return _targetName; }
+	const Common::String &getTargetName() const {
+		return _targetName;
+	}
 
 	/**
 	 * Display a message in a GUI dialog
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 25ce3cc..58293f7 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -34,8 +34,8 @@
 
 namespace Gargoyle {
 
-Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
-		GargoyleEngine(syst, gameDesc), _gliFirstEvent(false) {
+Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+	GargoyleEngine(syst, gameDesc), _gliFirstEvent(false) {
 	// Set uppercase/lowercase tables
 	int ix, res;
 	for (ix = 0; ix < 256; ix++) {
@@ -59,7 +59,7 @@ Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 }
 
 void Glk::glk_exit(void) {
- 	glk_put_string("[ press any key to exit ]");
+	glk_put_string("[ press any key to exit ]");
 	_events->waitForPress();
 
 	quitGame();
@@ -194,7 +194,7 @@ void Glk::glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, wi
 }
 
 void Glk::glk_window_get_arrangement(winid_t win, glui32 *method,
-		glui32 *size, winid_t *keyWin) {
+                                     glui32 *size, winid_t *keyWin) {
 	if (win) {
 		win->getArrangement(method, size, keyWin);
 	} else {
@@ -586,30 +586,30 @@ bool Glk::glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *resu
 
 	case stylehint_Weight:
 		*result =
-			(styles[style].font == PROPB || styles[style].font == PROPZ ||
-				styles[style].font == MONOB || styles[style].font == MONOZ);
+		    (styles[style].font == PROPB || styles[style].font == PROPZ ||
+		     styles[style].font == MONOB || styles[style].font == MONOZ);
 		break;
 
 	case stylehint_Oblique:
 		*result =
-			(styles[style].font == PROPI || styles[style].font == PROPZ ||
-				styles[style].font == MONOI || styles[style].font == MONOZ);
+		    (styles[style].font == PROPI || styles[style].font == PROPZ ||
+		     styles[style].font == MONOI || styles[style].font == MONOZ);
 		break;
 
 	case stylehint_Proportional:
 		*result =
-			(styles[style].font == PROPR || styles[style].font == PROPI ||
-				styles[style].font == PROPB || styles[style].font == PROPZ);
+		    (styles[style].font == PROPR || styles[style].font == PROPI ||
+		     styles[style].font == PROPB || styles[style].font == PROPZ);
 		break;
 
 	case stylehint_TextColor:
 		*result =
-			(styles[style].fg[0] << 16) | (styles[style].fg[1] << 8) | (styles[style].fg[2]);
+		    (styles[style].fg[0] << 16) | (styles[style].fg[1] << 8) | (styles[style].fg[2]);
 		break;
 
 	case stylehint_BackColor:
 		*result =
-			(styles[style].bg[0] << 16) | (styles[style].bg[1] << 8) | (styles[style].bg[2]);
+		    (styles[style].bg[0] << 16) | (styles[style].bg[1] << 8) | (styles[style].bg[2]);
 		break;
 
 	case stylehint_ReverseColor:
@@ -681,7 +681,7 @@ void Glk::glk_select(event_t *event) {
 		_windows->inputGuessFocus();
 		_gliFirstEvent = true;
 	}
-	
+
 	_events->getEvent(event, false);
 }
 
@@ -702,7 +702,7 @@ void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 i
 	if (!win) {
 		warning("request_line_event: invalid ref");
 	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
-			|| win->_lineRequestUni) {
+	           || win->_lineRequestUni) {
 		warning("request_line_event: window already has keyboard request");
 	} else {
 		win->requestLineEvent(buf, maxlen, initlen);
@@ -713,7 +713,7 @@ void Glk::glk_request_char_event(winid_t win) {
 	if (!win) {
 		warning("request_char_event: invalid ref");
 	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
-			|| win->_lineRequestUni) {
+	           || win->_lineRequestUni) {
 		warning("request_char_event: window already has keyboard request");
 	} else {
 		win->requestCharEvent();
@@ -777,7 +777,7 @@ glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchar
 }
 
 glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
-	glui32 numchars, glui32 lowerrest) {
+        glui32 numchars, glui32 lowerrest) {
 	return bufferChangeCase(buf, len, numchars, CASE_TITLE, COND_LINESTART, lowerrest);
 }
 
@@ -840,7 +840,7 @@ glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
 		return str->getLineUni(buf, len);
 	} else  {
 		warning("get_line_stream_uni: invalid ref");
-		return (glui32 )-1;
+		return (glui32) - 1;
 	}
 }
 
@@ -856,7 +856,7 @@ void Glk::glk_request_char_event_uni(winid_t win) {
 	if (!win) {
 		warning("request_char_event_uni: invalid ref");
 	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
-			|| win->_lineRequestUni) {
+	           || win->_lineRequestUni) {
 		warning("request_char_event_uni: window already has keyboard request");
 	} else {
 		win->requestCharEvent();
@@ -867,7 +867,7 @@ void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, gl
 	if (!win) {
 		warning("request_line_event_uni: invalid ref");
 	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
-			|| win->_lineRequestUni) {
+	           || win->_lineRequestUni) {
 		warning("request_line_event_uni: window already has keyboard request");
 	} else {
 		win->requestLineEventUni(buf, maxlen, initlen);
@@ -875,7 +875,7 @@ void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, gl
 }
 
 glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
-	glui32 numchars) {
+        glui32 numchars) {
 	// TODO
 	return 0;
 }
@@ -901,7 +901,7 @@ glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2)
 }
 
 glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2,
-		glui32 width, glui32 height) {
+                                  glui32 width, glui32 height) {
 	if (!win) {
 		warning("image_draw_scaled: invalid ref");
 	} else if (g_conf->_graphics) {
@@ -950,7 +950,7 @@ void Glk::glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 wid
 }
 
 void Glk::glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top,
-		glui32 width, glui32 height) {
+                               glui32 width, glui32 height) {
 	if (!win) {
 		warning("window_fill_rect: invalid ref");
 	} else {
@@ -1013,7 +1013,7 @@ schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
 }
 
 glui32 Glk::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
-	glui32 *sndarray, glui32 soundcount, glui32 notify) {
+                                    glui32 *sndarray, glui32 soundcount, glui32 notify) {
 	// TODO
 	return 0;
 }
@@ -1027,7 +1027,7 @@ void Glk::glk_schannel_unpause(schanid_t chan) {
 }
 
 void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
-	glui32 duration, glui32 notify) {
+                                      glui32 duration, glui32 notify) {
 	// TODO
 }
 
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index eb3e68d..b14546c 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -63,14 +63,14 @@ public:
 	 * Open a new window
 	 */
 	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
-		glui32 wintype, glui32 rock = 0) const;
+	                        glui32 wintype, glui32 rock = 0) const;
 
 	void glk_window_close(winid_t win, stream_result_t *result);
 	void glk_window_get_size(winid_t win, glui32 *width, glui32 *height);
 	void glk_window_set_arrangement(winid_t win, glui32 method,
-		glui32 size, winid_t keyWin);
+	                                glui32 size, winid_t keyWin);
 	void glk_window_get_arrangement(winid_t win, glui32 *method,
-		glui32 *size, winid_t *keyWin);
+	                                glui32 *size, winid_t *keyWin);
 	winid_t glk_window_iterate(winid_t win, glui32 *rock);
 	glui32 glk_window_get_rock(winid_t win);
 	glui32 glk_window_get_type(winid_t win);
@@ -108,7 +108,7 @@ public:
 	glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
 
 	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
-		glsi32 val);
+	                       glsi32 val);
 	void glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint);
 	glui32 glk_style_distinguish(winid_t win, glui32 style1, glui32 style2);
 	bool glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result);
@@ -129,7 +129,7 @@ public:
 	void glk_request_timer_events(glui32 millisecs);
 
 	void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
-		glui32 initlen);
+	                            glui32 initlen);
 	void glk_request_char_event(winid_t win);
 	void glk_request_mouse_event(winid_t win);
 
@@ -143,19 +143,19 @@ public:
 
 #ifdef GLK_MODULE_LINE_TERMINATORS
 	void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
-		glui32 count);
+	                                    glui32 count);
 #endif /* GLK_MODULE_LINE_TERMINATORS */
 
-	/** \addtogroup Unicode 
+	/** \addtogroup Unicode
 	 *  @{
 	 */
 
 	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
+	                                    glui32 numchars);
 	glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
+	                                    glui32 numchars);
 	glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
-		glui32 numchars, glui32 lowerrest);
+	                                    glui32 numchars, glui32 lowerrest);
 
 	void glk_put_char_uni(glui32 ch);
 	void glk_put_string_uni(glui32 *s);
@@ -173,16 +173,16 @@ public:
 
 	void glk_request_char_event_uni(winid_t win);
 	void glk_request_line_event_uni(winid_t win, glui32 *buf,
-		glui32 maxlen, glui32 initlen);
+	                                glui32 maxlen, glui32 initlen);
 
 	/** @}*/
 
 #ifdef GLK_MODULE_UNICODE_NORM
 
 	glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
+	                                      glui32 numchars);
 	glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
-		glui32 numchars);
+	                                      glui32 numchars);
 
 #endif /* GLK_MODULE_UNICODE_NORM */
 
@@ -190,15 +190,15 @@ public:
 
 	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
 	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
-		glsi32 val1, glsi32 val2, glui32 width, glui32 height);
+	                             glsi32 val1, glsi32 val2, glui32 width, glui32 height);
 	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
 
 	void glk_window_flow_break(winid_t win);
 
 	void glk_window_erase_rect(winid_t win,
-		glsi32 left, glsi32 top, glui32 width, glui32 height);
+	                           glsi32 left, glsi32 top, glui32 width, glui32 height);
 	void glk_window_fill_rect(winid_t win, glui32 color,
-		glsi32 left, glsi32 top, glui32 width, glui32 height);
+	                          glsi32 left, glsi32 top, glui32 width, glui32 height);
 	void glk_window_set_background_color(winid_t win, glui32 color);
 
 #endif /* GLK_MODULE_IMAGE */
@@ -212,7 +212,7 @@ public:
 
 	glui32 glk_schannel_play(schanid_t chan, glui32 snd);
 	glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
-		glui32 notify);
+	                             glui32 notify);
 	void glk_schannel_stop(schanid_t chan);
 	void glk_schannel_set_volume(schanid_t chan, glui32 vol);
 
@@ -224,11 +224,11 @@ public:
 
 	schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
 	glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
-		glui32 *sndarray, glui32 soundcount, glui32 notify);
+	                               glui32 *sndarray, glui32 soundcount, glui32 notify);
 	void glk_schannel_pause(schanid_t chan);
 	void glk_schannel_unpause(schanid_t chan);
 	void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
-		glui32 duration, glui32 notify);
+	                                 glui32 duration, glui32 notify);
 
 #endif /* GLK_MODULE_SOUND2 */
 #endif /* GLK_MODULE_SOUND */
@@ -258,7 +258,7 @@ public:
 #endif /* GLK_MODULE_DATETIME */
 
 	/* XXX non-official Glk functions that may or may not exist */
-	#define GARGLK 1
+#define GARGLK 1
 
 	const char *garglk_fileref_get_name(frefid_t fref) const;
 
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 6dd8504..7258e69 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -169,7 +169,7 @@ enum StyleHint {
  * These constants define the classes of opaque objects. It's a bit ugly to put
  * them in this header file, since more classes may be added in the future.
  * But if you find yourself stuck with an obsolete version of this file,
- * adding new class definitions will be easy enough -- they will be numbered 
+ * adding new class definitions will be easy enough -- they will be numbered
  * sequentially, and the numeric constants can be found in the Glk specification.
  */
 enum giDisp {
@@ -180,10 +180,10 @@ enum giDisp {
 };
 
 enum zcolor {
-	zcolor_Transparent = (uint32)-4,
-	zcolor_Cursor      = (uint32)-3,
-	zcolor_Current     = (uint32)-2,
-	zcolor_Default     = (uint32)-1
+	zcolor_Transparent = (uint32) - 4,
+	zcolor_Cursor      = (uint32) - 3,
+	zcolor_Current     = (uint32) - 2,
+	zcolor_Default     = (uint32) - 1
 };
 
 #ifdef GLK_MODULE_IMAGE
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 8728cdb..329cfb3 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -27,9 +27,9 @@ namespace Gargoyle {
 namespace Scott {
 
 Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
-		Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
-		Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
-		split_screen(true), Bottom(0), Top(0), BitFlags(0) {
+	Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
+	Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
+	split_screen(true), Bottom(0), Top(0), BitFlags(0) {
 	Common::fill(&NounText[0], &NounText[16], '\0');
 	Common::fill(&Counters[0], &Counters[16], 0);
 	Common::fill(&RoomSaved[0], &RoomSaved[16], 0);
@@ -107,19 +107,19 @@ Distributed under the GNU software license\n\n");
 			if (GameHeader.LightTime < 1) {
 				BitFlags |= (1 << LIGHTOUTBIT);
 				if (Items[LIGHT_SOURCE].Location == CARRIED ||
-					Items[LIGHT_SOURCE].Location == MyLoc) {
-					if (Options&SCOTTLIGHT)
+				        Items[LIGHT_SOURCE].Location == MyLoc) {
+					if (Options & SCOTTLIGHT)
 						output("Light has run out! ");
 					else
 						output("Your light has run out. ");
 				}
-				if (Options&PREHISTORIC_LAMP)
+				if (Options & PREHISTORIC_LAMP)
 					Items[LIGHT_SOURCE].Location = DESTROYED;
 			} else if (GameHeader.LightTime < 25) {
 				if (Items[LIGHT_SOURCE].Location == CARRIED ||
-					Items[LIGHT_SOURCE].Location == MyLoc) {
+				        Items[LIGHT_SOURCE].Location == MyLoc) {
 
-					if (Options&SCOTTLIGHT) {
+					if (Options & SCOTTLIGHT) {
 						output("Light runs out in ");
 						outputNumber(GameHeader.LightTime);
 						output(" turns. ");
@@ -208,7 +208,7 @@ int Scott::countCarried(void) {
 const char *Scott::mapSynonym(const char *word) {
 	int n = 1;
 	const char *tp;
-	static char lastword[16];	// Last non synonym
+	static char lastword[16];   // Last non synonym
 	while (n <= GameHeader.NumWords) {
 		tp = Nouns[n];
 		if (*tp == '*')
@@ -231,7 +231,7 @@ int Scott::matchUpItem(const char *text, int loc) {
 
 	while (ct <= GameHeader.NumItems) {
 		if (Items[ct].AutoGet && Items[ct].Location == loc &&
-			xstrncasecmp(Items[ct].AutoGet, word, GameHeader.WordLength) == 0)
+		        xstrncasecmp(Items[ct].AutoGet, word, GameHeader.WordLength) == 0)
 			return ct;
 		ct++;
 	}
@@ -293,27 +293,27 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	Action *ap;
 	Room *rp;
 	Item *ip;
-	
+
 	// Load the header
 	readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
 
 	GameHeader.NumItems = ni;
-	Items = (Item *)memAlloc(sizeof(Item)*(ni + 1));
+	Items = (Item *)memAlloc(sizeof(Item) * (ni + 1));
 	GameHeader.NumActions = na;
-	Actions = (Action *)memAlloc(sizeof(Action)*(na + 1));
+	Actions = (Action *)memAlloc(sizeof(Action) * (na + 1));
 	GameHeader.NumWords = nw;
 	GameHeader.WordLength = wl;
-	Verbs = (const char **)memAlloc(sizeof(char *)*(nw + 1));
-	Nouns = (const char **)memAlloc(sizeof(char *)*(nw + 1));
+	Verbs = (const char **)memAlloc(sizeof(char *) * (nw + 1));
+	Nouns = (const char **)memAlloc(sizeof(char *) * (nw + 1));
 	GameHeader.NumRooms = nr;
-	Rooms = (Room *)memAlloc(sizeof(Room)*(nr + 1));
+	Rooms = (Room *)memAlloc(sizeof(Room) * (nr + 1));
 	GameHeader.MaxCarry = mc;
 	GameHeader.PlayerRoom = pr;
 	GameHeader.Treasures = tr;
 	GameHeader.LightTime = lt;
 	LightRefill = lt;
 	GameHeader.NumMessages = mn;
-	Messages = (const char **)memAlloc(sizeof(char *)*(mn + 1));
+	Messages = (const char **)memAlloc(sizeof(char *) * (mn + 1));
 	GameHeader.TreasureRoom = trm;
 
 	// Load the actions
@@ -323,14 +323,14 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 		debug("Reading %d actions.", na);
 	while (ct < na + 1) {
 		readInts(f, 8,
-			&ap->Vocab,
-			&ap->Condition[0],
-			&ap->Condition[1],
-			&ap->Condition[2],
-			&ap->Condition[3],
-			&ap->Condition[4],
-			&ap->action[0],
-			&ap->action[1]);
+		         &ap->Vocab,
+		         &ap->Condition[0],
+		         &ap->Condition[1],
+		         &ap->Condition[2],
+		         &ap->Condition[3],
+		         &ap->Condition[4],
+		         &ap->action[0],
+		         &ap->action[1]);
 		ap++;
 		ct++;
 	}
@@ -338,7 +338,7 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	ct = 0;
 	if (loud)
 		debug("Reading %d word pairs.", nw);
-	while (ct<nw + 1) {
+	while (ct < nw + 1) {
 		Verbs[ct] = readString(f);
 		Nouns[ct] = readString(f);
 		ct++;
@@ -347,11 +347,11 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	rp = Rooms;
 	if (loud)
 		debug("Reading %d rooms.", nr);
-	while (ct<nr + 1) {
+	while (ct < nr + 1) {
 		readInts(f, 6,
-				&rp->Exits[0], &rp->Exits[1], &rp->Exits[2],
-				&rp->Exits[3], &rp->Exits[4], &rp->Exits[5]);
-	
+		         &rp->Exits[0], &rp->Exits[1], &rp->Exits[2],
+		         &rp->Exits[3], &rp->Exits[4], &rp->Exits[5]);
+
 		rp->Text = readString(f);
 		ct++;
 		rp++;
@@ -360,7 +360,7 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	ct = 0;
 	if (loud)
 		debug("Reading %d messages.", mn);
-	while (ct<mn + 1) {
+	while (ct < mn + 1) {
 		Messages[ct] = readString(f);
 		ct++;
 	}
@@ -389,7 +389,7 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	}
 	ct = 0;
 	// Discard Comment Strings
-	while (ct<na + 1) {
+	while (ct < na + 1) {
 		free(readString(f));
 		ct++;
 	}
@@ -420,9 +420,9 @@ void Scott::look(void) {
 	if (split_screen)
 		glk_window_clear(Top);
 
-	if ((BitFlags&(1 << DARKBIT)) && Items[LIGHT_SOURCE].Location != CARRIED
-		&& Items[LIGHT_SOURCE].Location != MyLoc) {
-		if (Options&YOUARE)
+	if ((BitFlags & (1 << DARKBIT)) && Items[LIGHT_SOURCE].Location != CARRIED
+	        && Items[LIGHT_SOURCE].Location != MyLoc) {
+		if (Options & YOUARE)
 			display(Top, "You can't see. It is too dark!\n");
 		else
 			display(Top, "I can't see. It is too dark!\n");
@@ -434,7 +434,7 @@ void Scott::look(void) {
 	if (*r->Text == '*')
 		display(Top, "%s\n", r->Text + 1);
 	else {
-		if (Options&YOUARE)
+		if (Options & YOUARE)
 			display(Top, "You are in a %s\n", r->Text);
 		else
 			display(Top, "I'm in a %s\n", r->Text);
@@ -443,9 +443,8 @@ void Scott::look(void) {
 	ct = 0;
 	f = 0;
 	display(Top, "\nObvious exits: ");
-	while (ct<6) {
-		if (r->Exits[ct] != 0)
-		{
+	while (ct < 6) {
+		if (r->Exits[ct] != 0) {
 			if (f == 0)
 				f = 1;
 			else
@@ -532,7 +531,7 @@ void Scott::lineInput(char *buf, size_t n) {
 
 void Scott::saveGame(void) {
 	frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
-		filemode_Write, 0);
+	               filemode_Write, 0);
 	if (ref == nullptr)
 		return;
 
@@ -557,8 +556,8 @@ Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
 	}
 
 	msg = Common::String::format("%lu %d %hd %d %d %hd\n",
-		BitFlags, (BitFlags&(1 << DARKBIT)) ? 1 : 0,
-		MyLoc, CurrentCounter, SavedRoom, GameHeader.LightTime);
+	                             BitFlags, (BitFlags & (1 << DARKBIT)) ? 1 : 0,
+	                             MyLoc, CurrentCounter, SavedRoom, GameHeader.LightTime);
 	glk_put_string_stream(file, msg.c_str());
 
 	for (int ct = 0; ct <= GameHeader.NumItems; ct++) {
@@ -574,7 +573,7 @@ Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
 
 void Scott::loadGame(void) {
 	frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
-		filemode_Read, 0);
+	               filemode_Read, 0);
 	if (ref == nullptr)
 		return;
 
@@ -597,15 +596,15 @@ Common::Error Scott::loadGameState(int slot) {
 	if (file == nullptr)
 		return Common::kReadingFailed;
 
-	for (ct = 0; ct<16; ct++) {
+	for (ct = 0; ct < 16; ct++) {
 		glk_get_line_stream(file, buf, sizeof buf);
 		sscanf(buf, "%d %d", &Counters[ct], &RoomSaved[ct]);
 	}
 
 	glk_get_line_stream(file, buf, sizeof buf);
 	sscanf(buf, "%ld %hd %d %d %d %d\n",
-		&BitFlags, &darkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
-		&GameHeader.LightTime);
+	       &BitFlags, &darkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
+	       &GameHeader.LightTime);
 
 	// Backward compatibility
 	if (darkFlag)
@@ -634,7 +633,7 @@ int Scott::getInput(int *vb, int *no) {
 
 			num = sscanf(buf, "%9s %9s", verb, noun);
 		} while (num == 0 || *buf == '\n');
-		
+
 		if (xstrcasecmp(verb, "restore") == 0) {
 			loadGame();
 			return -1;
@@ -643,14 +642,28 @@ int Scott::getInput(int *vb, int *no) {
 			*noun = 0;
 		if (*noun == 0 && strlen(verb) == 1) {
 			switch (Common::isUpper((unsigned char)*verb) ? tolower((unsigned char)*verb) : *verb) {
-			case 'n':strcpy(verb, "NORTH"); break;
-			case 'e':strcpy(verb, "EAST"); break;
-			case 's':strcpy(verb, "SOUTH"); break;
-			case 'w':strcpy(verb, "WEST"); break;
-			case 'u':strcpy(verb, "UP"); break;
-			case 'd':strcpy(verb, "DOWN"); break;
+			case 'n':
+				strcpy(verb, "NORTH");
+				break;
+			case 'e':
+				strcpy(verb, "EAST");
+				break;
+			case 's':
+				strcpy(verb, "SOUTH");
+				break;
+			case 'w':
+				strcpy(verb, "WEST");
+				break;
+			case 'u':
+				strcpy(verb, "UP");
+				break;
+			case 'd':
+				strcpy(verb, "DOWN");
+				break;
 			// Brian Howarth interpreter also supports this
-			case 'i':strcpy(verb, "INVENTORY"); break;
+			case 'i':
+				strcpy(verb, "INVENTORY");
+				break;
 			}
 		}
 		nc = whichWord(verb, Nouns);
@@ -668,7 +681,7 @@ int Scott::getInput(int *vb, int *no) {
 		}
 	} while (vc == -1);
 
-	strcpy(NounText, noun);	// Needed by GET/DROP hack
+	strcpy(NounText, noun); // Needed by GET/DROP hack
 	return 0;
 }
 
@@ -678,7 +691,7 @@ int Scott::performLine(int ct) {
 	int act[4];
 	int cc = 0;
 
-	while (cc<5) {
+	while (cc < 5) {
 		int cv, dv;
 		cv = Actions[ct].Condition[cc];
 		dv = cv / 20;
@@ -696,8 +709,8 @@ int Scott::performLine(int ct) {
 				return 0;
 			break;
 		case 3:
-			if (Items[dv].Location != CARRIED&&
-				Items[dv].Location != MyLoc)
+			if (Items[dv].Location != CARRIED &&
+			        Items[dv].Location != MyLoc)
 				return 0;
 			break;
 		case 4:
@@ -717,11 +730,11 @@ int Scott::performLine(int ct) {
 				return 0;
 			break;
 		case 8:
-			if ((BitFlags&(1 << dv)) == 0)
+			if ((BitFlags & (1 << dv)) == 0)
 				return 0;
 			break;
 		case 9:
-			if (BitFlags&(1 << dv))
+			if (BitFlags & (1 << dv))
 				return 0;
 			break;
 		case 10:
@@ -745,7 +758,7 @@ int Scott::performLine(int ct) {
 				return 0;
 			break;
 		case 15:
-			if (CurrentCounter>dv)
+			if (CurrentCounter > dv)
 				return 0;
 			break;
 		case 16:
@@ -778,8 +791,7 @@ int Scott::performLine(int ct) {
 	act[2] /= 150;
 	cc = 0;
 	pptr = 0;
-	while (cc<4)
-	{
+	while (cc < 4) {
 		if (act[cc] >= 1 && act[cc] < 52) {
 			output(Messages[act[cc]]);
 			output("\n");
@@ -825,7 +837,7 @@ int Scott::performLine(int ct) {
 				BitFlags &= ~(1 << param[pptr++]);
 				break;
 			case 61:
-				if (Options&YOUARE)
+				if (Options & YOUARE)
 					output("You are dead.\n");
 				else
 					output("I am dead.\n");
@@ -850,11 +862,11 @@ doneit:
 				int n = 0;
 				while (i <= GameHeader.NumItems) {
 					if (Items[i].Location == GameHeader.TreasureRoom &&
-						*Items[i].Text == '*')
+					        *Items[i].Text == '*')
 						n++;
 					i++;
 				}
-				if (Options&YOUARE)
+				if (Options & YOUARE)
 					output("You have stored ");
 				else
 					output("I've stored ");
@@ -871,7 +883,7 @@ doneit:
 			case 66: {
 				int i = 0;
 				int f = 0;
-				if (Options&YOUARE)
+				if (Options & YOUARE)
 					output("You are carrying:\n");
 				else
 					output("I'm carrying:\n");
@@ -999,7 +1011,7 @@ doneit:
 				break;
 			default:
 				error("Unknown action %d [Param begins %d %d]\n",
-					act[cc], param[pptr], param[pptr + 1]);
+				      act[cc], param[pptr], param[pptr + 1]);
 				break;
 			}
 		}
@@ -1011,8 +1023,8 @@ doneit:
 }
 
 int Scott::performActions(int vb, int no) {
-	static int disable_sysfunc = 0;	// Recursion lock
-	int d = BitFlags&(1 << DARKBIT);
+	static int disable_sysfunc = 0; // Recursion lock
+	int d = BitFlags & (1 << DARKBIT);
 
 	int ct = 0;
 	int fl;
@@ -1024,7 +1036,7 @@ int Scott::performActions(int vb, int no) {
 	if (vb == 1 && no >= 1 && no <= 6) {
 		int nl;
 		if (Items[LIGHT_SOURCE].Location == MyLoc ||
-			Items[LIGHT_SOURCE].Location == CARRIED)
+		        Items[LIGHT_SOURCE].Location == CARRIED)
 			d = 0;
 		if (d)
 			output("Dangerous to move in the dark! ");
@@ -1034,13 +1046,13 @@ int Scott::performActions(int vb, int no) {
 			return 0;
 		}
 		if (d) {
-			if (Options&YOUARE)
+			if (Options & YOUARE)
 				output("You fell down and broke your neck. ");
 			else
 				output("I fell down and broke my neck. ");
 			glk_exit();
 		}
-		if (Options&YOUARE)
+		if (Options & YOUARE)
 			output("You can't go in that direction. ");
 		else
 			output("I can't go in that direction. ");
@@ -1053,16 +1065,16 @@ int Scott::performActions(int vb, int no) {
 		vv = Actions[ct].Vocab;
 		// Think this is now right. If a line we run has an action73
 		// run all following lines with vocab of 0,0
-		if (vb != 0 && (doagain&&vv != 0))
+		if (vb != 0 && (doagain && vv != 0))
 			break;
 		// Oops.. added this minor cockup fix 1.11
 		if (vb != 0 && !doagain && fl == 0)
 			break;
 		nv = vv % 150;
 		vv /= 150;
-		if ((vv == vb) || (doagain&&Actions[ct].Vocab == 0)) {
+		if ((vv == vb) || (doagain && Actions[ct].Vocab == 0)) {
 			if ((vv == 0 && randomPercent(nv)) || doagain ||
-					(vv != 0 && (nv == no || nv == 0))) {
+			        (vv != 0 && (nv == no || nv == 0))) {
 				int f2;
 				if (fl == -1)
 					fl = -2;
@@ -1088,7 +1100,7 @@ int Scott::performActions(int vb, int no) {
 	if (fl != 0 && disable_sysfunc == 0) {
 		int item;
 		if (Items[LIGHT_SOURCE].Location == MyLoc ||
-			Items[LIGHT_SOURCE].Location == CARRIED)
+		        Items[LIGHT_SOURCE].Location == CARRIED)
 			d = 0;
 		if (vb == 10 || vb == 18) {
 			// Yes they really _are_ hardcoded values
@@ -1104,11 +1116,11 @@ int Scott::performActions(int vb, int no) {
 					while (i <= GameHeader.NumItems) {
 						if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
 							no = whichWord(Items[i].AutoGet, Nouns);
-							disable_sysfunc = 1;	// Don't recurse into auto get !
-							performActions(vb, no);	// Recursively check each items table code
+							disable_sysfunc = 1;    // Don't recurse into auto get !
+							performActions(vb, no); // Recursively check each items table code
 							disable_sysfunc = 0;
 							if (countCarried() == GameHeader.MaxCarry) {
-								if (Options&YOUARE)
+								if (Options & YOUARE)
 									output("You are carrying too much. ");
 								else
 									output("I've too much to carry. ");
@@ -1130,7 +1142,7 @@ int Scott::performActions(int vb, int no) {
 					return 0;
 				}
 				if (countCarried() == GameHeader.MaxCarry) {
-					if (Options&YOUARE)
+					if (Options & YOUARE)
 						output("You are carrying too much. ");
 					else
 						output("I've too much to carry. ");
@@ -1138,7 +1150,7 @@ int Scott::performActions(int vb, int no) {
 				}
 				item = matchUpItem(NounText, MyLoc);
 				if (item == -1) {
-					if (Options&YOUARE)
+					if (Options & YOUARE)
 						output("It is beyond your power to do that. ");
 					else
 						output("It's beyond my power to do that. ");
@@ -1175,7 +1187,7 @@ int Scott::performActions(int vb, int no) {
 				}
 				item = matchUpItem(NounText, CARRIED);
 				if (item == -1) {
-					if (Options&YOUARE)
+					if (Options & YOUARE)
 						output("It's beyond your power to do that.\n");
 					else
 						output("It's beyond my power to do that.\n");
@@ -1193,8 +1205,8 @@ int Scott::performActions(int vb, int no) {
 
 int Scott::xstrcasecmp(const char *s1, const char *s2) {
 	const unsigned char
-		*us1 = (const unsigned char *)s1,
-		*us2 = (const unsigned char *)s2;
+	*us1 = (const unsigned char *)s1,
+	 *us2 = (const unsigned char *)s2;
 
 	while (tolower(*us1) == tolower(*us2++))
 		if (*us1++ == '\0')
@@ -1205,8 +1217,8 @@ int Scott::xstrcasecmp(const char *s1, const char *s2) {
 int Scott::xstrncasecmp(const char *s1, const char *s2, size_t n) {
 	if (n != 0) {
 		const unsigned char
-			*us1 = (const unsigned char *)s1,
-			*us2 = (const unsigned char *)s2;
+		*us1 = (const unsigned char *)s1,
+		 *us2 = (const unsigned char *)s2;
 
 		do {
 			if (tolower(*us1) != tolower(*us2++))
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
index 81847cc..0ff8d33 100644
--- a/engines/gargoyle/scott/scott.h
+++ b/engines/gargoyle/scott/scott.h
@@ -24,7 +24,7 @@
 #define GARGOYLE_SCOTT
 
 /*
- *	Controlling block
+ *  Controlling block
  */
 
 #include "common/scummsys.h"
@@ -33,28 +33,28 @@
 namespace Gargoyle {
 namespace Scott {
 
-#define LIGHT_SOURCE	9	// Always 9 how odd
-#define CARRIED		255		// Carried
-#define DESTROYED	0		// Destroyed
-#define DARKBIT		15
-#define LIGHTOUTBIT	16		// Light gone out
+#define LIGHT_SOURCE    9   // Always 9 how odd
+#define CARRIED     255     // Carried
+#define DESTROYED   0       // Destroyed
+#define DARKBIT     15
+#define LIGHTOUTBIT 16      // Light gone out
 
 enum GameOption {
-	YOUARE      = 1,		///< You are not I am
-	SCOTTLIGHT  = 2,		///< Authentic Scott Adams light messages
-	DEBUGGING   = 4,		///< Info from database load
-	TRS80_STYLE = 8,		///< Display in style used on TRS-80
-	PREHISTORIC_LAMP = 16	///< Destroy the lamp (very old databases)
+	YOUARE      = 1,        ///< You are not I am
+	SCOTTLIGHT  = 2,        ///< Authentic Scott Adams light messages
+	DEBUGGING   = 4,        ///< Info from database load
+	TRS80_STYLE = 8,        ///< Display in style used on TRS-80
+	PREHISTORIC_LAMP = 16   ///< Destroy the lamp (very old databases)
 };
 
-#define TRS80_LINE	"\n<------------------------------------------------------------>\n"
-#define MyLoc	(GameHeader.PlayerRoom)
+#define TRS80_LINE  "\n<------------------------------------------------------------>\n"
+#define MyLoc   (GameHeader.PlayerRoom)
 
 struct Header {
- 	int Unknown;
+	int Unknown;
 	int NumItems;
 	int NumActions;
-	int NumWords;			///< Smaller of verb/noun is padded to same size
+	int NumWords;           ///< Smaller of verb/noun is padded to same size
 	int NumRooms;
 	int MaxCarry;
 	int PlayerRoom;
@@ -90,7 +90,7 @@ struct Room {
 };
 
 struct Item {
-	char *Text;		// PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
+	char *Text;     // PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
 	byte Location;
 	byte InitialLoc;
 	char *AutoGet;
@@ -125,7 +125,7 @@ private:
 	int SavedRoom;
 	int RoomSaved[16];  ///< Range unknown
 	int Options;        ///< Option flags set
-	int Width;		    ///< Terminal width
+	int Width;          ///< Terminal width
 	int TopHeight;      ///< Height of top window
 
 	bool split_screen;
diff --git a/engines/gargoyle/screen.h b/engines/gargoyle/screen.h
index d7631ef..aa43b1b 100644
--- a/engines/gargoyle/screen.h
+++ b/engines/gargoyle/screen.h
@@ -54,8 +54,8 @@ public:
 
 	/**
 	 * Draws the text input caret at the given position
-	 * @remarks		The position specifies the caret's bottom-left corner,
-	 *		and the X position is in multiples of GLI_SUBPIX
+	 * @remarks     The position specifies the caret's bottom-left corner,
+	 *      and the X position is in multiples of GLI_SUBPIX
 	 */
 	void drawCaret(const Point &pos);
 };
diff --git a/engines/gargoyle/selection.cpp b/engines/gargoyle/selection.cpp
index 8ad3b76..27cfbe7 100644
--- a/engines/gargoyle/selection.cpp
+++ b/engines/gargoyle/selection.cpp
@@ -112,9 +112,9 @@ void WindowMask::putHyperlink(glui32 linkval, uint x0, uint y0, uint x1, uint y1
 	}
 
 	if (tx0 >= _hor
-		|| tx1 >= _hor
-		|| ty0 >= _ver || ty1 >= _ver
-		|| !_links[tx0] || !_links[tx1]) {
+	        || tx1 >= _hor
+	        || ty0 >= _ver || ty1 >= _ver
+	        || !_links[tx0] || !_links[tx1]) {
 		warning("putHyperlink: invalid range given");
 		return;
 	}
@@ -190,7 +190,7 @@ void Selection::clearSelection() {
 
 bool Selection::checkSelection(const Rect &r) const {
 	Rect select(MIN(_select.left, _select.right), MAX(_select.left, _select.right),
-		MIN(_select.top, _select.bottom), MAX(_select.top, _select.bottom));
+	            MIN(_select.top, _select.bottom), MAX(_select.top, _select.bottom));
 	if (select.isEmpty())
 		return false;
 
@@ -218,7 +218,7 @@ bool Selection::getSelection(const Rect &r, int *rx0, int *rx1) const {
 	row_selected = false;
 
 	if ((cy0 >= upper && cy0 <= lower)
-		|| (cy1 >= upper && cy1 <= lower))
+	        || (cy1 >= upper && cy1 <= lower))
 		row_selected = true;
 
 	if (row >= cy0 && row <= cy1)
diff --git a/engines/gargoyle/selection.h b/engines/gargoyle/selection.h
index 2d3a208..a82af6d 100644
--- a/engines/gargoyle/selection.h
+++ b/engines/gargoyle/selection.h
@@ -92,13 +92,13 @@ class Selection : public Clipboard, public WindowMask {
 public:
 	/**
 	 * Start selecting an area of the screen
-	 * @param pos		Position to start selection area at
+	 * @param pos       Position to start selection area at
 	 */
 	void startSelection(const Point &pos);
 
 	/**
 	 * Move the end point of the selection area
-	 * @param pos		Position to end selection area at
+	 * @param pos       Position to end selection area at
 	 */
 	void moveSelection(const Point &pos);
 
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index b301272..75fb205 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -33,8 +33,8 @@
 namespace Gargoyle {
 
 Stream::Stream(Streams *streams, bool readable, bool writable, uint32 rock, bool unicode) :
-		_streams(streams), _readable(readable), _writable(writable), _readCount(0),
-		_writeCount(0), _prev(nullptr), _next(nullptr), _rock(0) {
+	_streams(streams), _readable(readable), _writable(writable), _readCount(0),
+	_writeCount(0), _prev(nullptr), _next(nullptr), _rock(0) {
 }
 
 Stream::~Stream() {
@@ -174,7 +174,7 @@ void WindowStream::unputBuffer(const char *buf, size_t len) {
 		}
 	}
 
-	for (lx = 0, cx = buf + len - 1; lx<len; lx++, cx--) {
+	for (lx = 0, cx = buf + len - 1; lx < len; lx++, cx--) {
 		if (!_window->unputCharUni(*cx))
 			break;
 		_writeCount--;
@@ -200,7 +200,7 @@ void WindowStream::unputBufferUni(const glui32 *buf, size_t len) {
 		}
 	}
 
-	for (lx = 0, cx = buf + len - 1; lx<len; lx++, cx--) {
+	for (lx = 0, cx = buf + len - 1; lx < len; lx++, cx--) {
 		if (!_window->unputCharUni(*cx))
 			break;
 		_writeCount--;
@@ -245,7 +245,7 @@ void WindowStream::setZColors(glui32 fg, glui32 bg) {
 			_window->_attr.fgcolor = 0;
 			Windows::_overrideFgSet = false;
 			Windows::_overrideFgVal = 0;
-			
+
 			Common::copy(g_conf->_moreSave, g_conf->_moreSave + 3, g_conf->_moreColor);
 			Common::copy(g_conf->_caretSave, g_conf->_caretSave + 3, g_conf->_caretColor);
 			Common::copy(g_conf->_linkSave, g_conf->_linkSave + 3, g_conf->_linkColor);
@@ -254,7 +254,7 @@ void WindowStream::setZColors(glui32 fg, glui32 bg) {
 			_window->_attr.fgcolor = fg;
 			Windows::_overrideFgSet = true;
 			Windows::_overrideFgVal = fg;
-			
+
 			Common::copy(fore, fore + 3, g_conf->_moreColor);
 			Common::copy(fore, fore + 3, g_conf->_caretColor);
 			Common::copy(fore, fore + 3, g_conf->_linkColor);
@@ -302,8 +302,8 @@ void WindowStream::setReverseVideo(bool reverse) {
 /*--------------------------------------------------------------------------*/
 
 MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint32 rock, bool unicode) :
-		Stream(streams, mode != filemode_Write, mode != filemode_Read, rock, unicode),
-		_buf(buf), _bufLen(buflen), _bufPtr(buf) {
+	Stream(streams, mode != filemode_Write, mode != filemode_Read, rock, unicode),
+	_buf(buf), _bufLen(buflen), _bufPtr(buf) {
 	assert(_buf && _bufLen);
 	assert(mode == filemode_Read || mode == filemode_Write || mode == filemode_ReadWrite);
 
@@ -363,8 +363,7 @@ void MemoryStream::putBuffer(const char *buf, size_t len) {
 	} else {
 		if (!_unicode) {
 			unsigned char *bp = (unsigned char *)_bufPtr;
-			if (bp + len > (unsigned char *)_bufEnd)
-			{
+			if (bp + len > (unsigned char *)_bufEnd) {
 				lx = (bp + len) - (unsigned char *)_bufEnd;
 				if (lx < len)
 					len -= lx;
@@ -374,7 +373,7 @@ void MemoryStream::putBuffer(const char *buf, size_t len) {
 			if (len) {
 				memmove(bp, buf, len);
 				bp += len;
-				if (bp >(unsigned char *)_bufEof)
+				if (bp > (unsigned char *)_bufEof)
 					_bufEof = bp;
 			}
 			_bufPtr = bp;
@@ -392,7 +391,7 @@ void MemoryStream::putBuffer(const char *buf, size_t len) {
 				for (i = 0; i < len; i++)
 					bp[i] = buf[i];
 				bp += len;
-				if (bp >(glui32 *)_bufEof)
+				if (bp > (glui32 *)_bufEof)
 					_bufEof = bp;
 			}
 			_bufPtr = bp;
@@ -444,7 +443,7 @@ void MemoryStream::putBufferUni(const uint32 *buf, size_t len) {
 			if (len) {
 				memmove(bp, buf, len * 4);
 				bp += len;
-				if (bp >(glui32 *)_bufEof)
+				if (bp > (glui32 *)_bufEof)
 					_bufEof = bp;
 			}
 			_bufPtr = bp;
@@ -460,8 +459,7 @@ glui32 MemoryStream::getPosition() const {
 }
 
 void MemoryStream::setPosition(glui32 pos, glui32 seekMode) {
-	if (!_unicode)
-	{
+	if (!_unicode) {
 		if (seekMode == seekmode_Current)
 			pos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + pos;
 		else if (seekMode == seekmode_End)
@@ -556,7 +554,7 @@ glui32 MemoryStream::getBuffer(char *buf, glui32 len) {
 			if (len) {
 				memcpy(buf, bp, len);
 				bp += len;
-				if (bp >(unsigned char *)_bufEof)
+				if (bp > (unsigned char *)_bufEof)
 					_bufEof = bp;
 			}
 
@@ -601,8 +599,7 @@ glui32 MemoryStream::getBufferUni(glui32 *buf, glui32 len) {
 	} else {
 		if (!_unicode) {
 			unsigned char *bp = (unsigned char *)_bufPtr;
-			if (bp + len > (unsigned char *)_bufEnd)
-			{
+			if (bp + len > (unsigned char *)_bufEnd) {
 				glui32 lx;
 				lx = (bp + len) - (unsigned char *)_bufEnd;
 				if (lx < len)
@@ -615,7 +612,7 @@ glui32 MemoryStream::getBufferUni(glui32 *buf, glui32 len) {
 				for (i = 0; i < len; i++)
 					buf[i] = bp[i];
 				bp += len;
-				if (bp >(unsigned char *)_bufEof)
+				if (bp > (unsigned char *)_bufEof)
 					_bufEof = bp;
 			}
 			_readCount += len;
@@ -633,7 +630,7 @@ glui32 MemoryStream::getBufferUni(glui32 *buf, glui32 len) {
 			if (len) {
 				memcpy(buf, bp, len * 4);
 				bp += len;
-				if (bp >(glui32 *)_bufEof)
+				if (bp > (glui32 *)_bufEof)
 					_bufEof = bp;
 			}
 			_readCount += len;
@@ -761,8 +758,8 @@ glui32 MemoryStream::getLineUni(glui32 *ubuf, glui32 len) {
 /*--------------------------------------------------------------------------*/
 
 FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 rock, bool unicode) :
-		Stream(streams, fmode == filemode_Read, fmode != filemode_Read, rock, unicode),  _lastOp(0),
-		_textFile(fref->_textMode), _inFile(nullptr), _outFile(nullptr), _inStream(nullptr) {
+	Stream(streams, fmode == filemode_Read, fmode != filemode_Read, rock, unicode),  _lastOp(0),
+	_textFile(fref->_textMode), _inFile(nullptr), _outFile(nullptr), _inStream(nullptr) {
 	Common::String fname = fref->_slotNumber == -1 ? fref->_filename : fref->getSaveName();
 
 	if (fmode == filemode_Write || fmode == filemode_ReadWrite || fmode == filemode_WriteAppend) {
@@ -789,7 +786,7 @@ FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 roc
 			if (!readSavegameHeader(_inStream, header))
 				error("Invalid savegame");
 			if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage()
-				|| header._md5 != g_vm->getGameMD5())
+			        || header._md5 != g_vm->getGameMD5())
 				error("Savegame is for a different game");
 
 			g_vm->_events->setTotalPlayTicks(header._totalFrames);
@@ -873,7 +870,7 @@ void FileStream::putBufferUni(const uint32 *buf, size_t len) {
 
 
 	ensureOp(filemode_Write);
-	for (size_t lx = 0; lx<len; lx++) {
+	for (size_t lx = 0; lx < len; lx++) {
 		glui32 ch = buf[lx];
 		if (!_unicode) {
 			if (ch >= 0x100)
@@ -1005,7 +1002,7 @@ void FileStream::setPosition(glui32 pos, glui32 seekMode) {
 	_lastOp = 0;
 	if (_unicode)
 		pos *= 4;
-	
+
 	if (_inStream) {
 		_inStream->seek(pos, SEEK_SET);
 	} else {
@@ -1100,7 +1097,7 @@ glui32 FileStream::getBuffer(char *buf, glui32 len) {
 		return res;
 	} else if (_textFile) {
 		glui32 lx;
-		for (lx = 0; lx<len; lx++) {
+		for (lx = 0; lx < len; lx++) {
 			glui32 ch;
 			ch = getCharUtf8();
 			if (ch == -1)
@@ -1148,7 +1145,7 @@ glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 	ensureOp(filemode_Read);
 	if (!_unicode) {
 		glui32 lx;
-		for (lx = 0; lx<len; lx++) {
+		for (lx = 0; lx < len; lx++) {
 			int res;
 			glui32 ch;
 			res = _inStream->readByte();
@@ -1161,7 +1158,7 @@ glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 		return lx;
 	} else if (_textFile) {
 		glui32 lx;
-		for (lx = 0; lx<len; lx++) {
+		for (lx = 0; lx < len; lx++) {
 			glui32 ch;
 			ch = getCharUtf8();
 			if (ch == -1)
@@ -1172,8 +1169,7 @@ glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 		return lx;
 	} else {
 		glui32 lx;
-		for (lx = 0; lx<len; lx++)
-		{
+		for (lx = 0; lx < len; lx++) {
 			int res;
 			glui32 ch;
 			res = _inStream->readByte();
@@ -1477,8 +1473,7 @@ frefid_t Streams::createByPrompt(glui32 usage, FileMode fmode, glui32 rock) {
 				Common::String desc = dialog->getResultString();
 				return createRef(slot, desc, usage, rock);
 			}
-		}
-		else if (fmode == filemode_Read) {
+		} else if (fmode == filemode_Read) {
 			// Load a savegame slot
 			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
 
@@ -1486,8 +1481,7 @@ frefid_t Streams::createByPrompt(glui32 usage, FileMode fmode, glui32 rock) {
 			if (slot >= 0) {
 				return createRef(slot, "", usage, rock);
 			}
-		}
-		else {
+		} else {
 			error("Unsupport file mode");
 		}
 		break;
@@ -1527,7 +1521,7 @@ frefid_t Streams::createRef(const Common::String &filename, glui32 usage, glui32
 
 frefid_t Streams::createTemp(glui32 usage, glui32 rock) {
 	return createRef(Common::String::format("%s.tmp", g_vm->getTargetName().c_str()),
-		usage, rock);
+	                 usage, rock);
 }
 
 frefid_t Streams::createFromRef(frefid_t fref, glui32 usage, glui32 rock) {
@@ -1579,8 +1573,7 @@ bool FileReference::exists() const {
 		if (Common::File::exists(_filename))
 			return true;
 		filename = _filename;
-	}
-	else {
+	} else {
 		filename = getSaveName();
 	}
 
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index c8b6963..8a497e0 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -159,7 +159,9 @@ public:
 	/**
 	 * Get the rock value for the stream
 	 */
-	uint32 getRock() const { return _rock; }
+	uint32 getRock() const {
+		return _rock;
+	}
 
 	/**
 	 * Fill out the total amount read and/or written
@@ -217,7 +219,9 @@ public:
 		putCharUni('\n');
 	}
 
-	virtual glui32 getPosition() const { return 0; }
+	virtual glui32 getPosition() const {
+		return 0;
+	}
 
 	virtual void setPosition(glui32 pos, glui32 seekMode) {}
 
@@ -226,32 +230,44 @@ public:
 	/**
 	 * Get a character from the stream
 	 */
-	virtual glsi32 getChar() { return -1; }
+	virtual glsi32 getChar() {
+		return -1;
+	}
 
 	/**
 	 * Get a unicode character from the stream
 	 */
-	virtual glsi32 getCharUni() { return -1; }
+	virtual glsi32 getCharUni() {
+		return -1;
+	}
 
 	/**
 	 * Get a buffer
 	 */
-	virtual glui32 getBuffer(char *buf, glui32 len) { return 0; }
+	virtual glui32 getBuffer(char *buf, glui32 len) {
+		return 0;
+	}
 
 	/**
 	 * Get a unicode buffer
 	 */
-	virtual glui32 getBufferUni(glui32 *buf, glui32 len) { return 0; }
+	virtual glui32 getBufferUni(glui32 *buf, glui32 len) {
+		return 0;
+	}
 
 	/**
 	 * Get a line
 	 */
-	virtual glui32 getLine(char *buf, glui32 len) { return 0; }
+	virtual glui32 getLine(char *buf, glui32 len) {
+		return 0;
+	}
 
 	/**
 	 * Get a unicode line
 	 */
-	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) { return 0; }
+	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) {
+		return 0;
+	}
 
 	/**
 	 * Set a hyperlink
@@ -341,11 +357,11 @@ public:
  */
 class MemoryStream : public Stream {
 private:
-	void *_buf;		///< unsigned char* for latin1, glui32* for unicode
+	void *_buf;     ///< unsigned char* for latin1, glui32* for unicode
 	void *_bufPtr;
 	void *_bufEnd;
 	void *_bufEof;
-	size_t _bufLen;	///< # of bytes for latin1, # of 4-byte words for unicode
+	size_t _bufLen; ///< # of bytes for latin1, # of 4-byte words for unicode
 public:
 	/**
 	 * Constructor
@@ -416,7 +432,7 @@ private:
 	Common::OutSaveFile *_outFile;
 	Common::InSaveFile *_inFile;
 	Common::SeekableReadStream *_inStream;
-	uint32 _lastOp;					///< 0, filemode_Write, or filemode_Read
+	uint32 _lastOp;                 ///< 0, filemode_Write, or filemode_Read
 	bool _textFile;
 private:
 	/**
@@ -577,7 +593,9 @@ public:
 	/**
 	 * Gets the current output stream
 	 */
-	Stream *getCurrent() const { return _currentStream; }
+	Stream *getCurrent() const {
+		return _currentStream;
+	}
 
 	/**
 	 * Prompt for a savegame to load or save, and populate a file reference from the result
diff --git a/engines/gargoyle/unicode.cpp b/engines/gargoyle/unicode.cpp
index c783915..f32093b 100644
--- a/engines/gargoyle/unicode.cpp
+++ b/engines/gargoyle/unicode.cpp
@@ -34,7 +34,7 @@ size_t strlen_uni(const uint32 *s) {
 }
 
 glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCase destcase,
-		BufferChangeCond cond, int changerest) {
+                        BufferChangeCond cond, int changerest) {
 	glui32 ix, jx;
 	glui32 *outbuf;
 	glui32 *newoutbuf;
@@ -92,8 +92,7 @@ glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCa
 				res = block[ch & 0xFF][target];
 		}
 
-		if (res != 0xFFFFFFFF || res == ch)
-		{
+		if (res != 0xFFFFFFFF || res == ch) {
 			/* simple case */
 			if (outcount < len)
 				outbuf[outcount] = res;
@@ -130,7 +129,7 @@ glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCa
 			outbuf = newoutbuf;
 		}
 
-		for (jx = 0; jx<speccount; jx++) {
+		for (jx = 0; jx < speccount; jx++) {
 			if (outcount < len)
 				outbuf[outcount] = ptr[jx];
 			outcount++;
diff --git a/engines/gargoyle/unicode.h b/engines/gargoyle/unicode.h
index 2392c1a..e9fa928 100644
--- a/engines/gargoyle/unicode.h
+++ b/engines/gargoyle/unicode.h
@@ -44,7 +44,7 @@ size_t strlen_uni(const uint32 *s);
  *converted string should have contained.
  */
 extern glui32 bufferChangeCase(glui32 *buf, glui32 len,
-	glui32 numchars, BufferChangeCase destcase, BufferChangeCond cond, int changerest);
+                               glui32 numchars, BufferChangeCase destcase, BufferChangeCond cond, int changerest);
 
 } // End of namespace Gargoyle
 
diff --git a/engines/gargoyle/unicode_gen.cpp b/engines/gargoyle/unicode_gen.cpp
index c9ad570..56e44c3 100644
--- a/engines/gargoyle/unicode_gen.cpp
+++ b/engines/gargoyle/unicode_gen.cpp
@@ -3861,3325 +3861,3325 @@ gli_case_special_t unigen_special_0xfb16 = { 857, 860, 862 };
 gli_case_special_t unigen_special_0xfb17 = { 865, 868, 870 };
 
 #define RETURN_COMBINING_CLASS(ch)  \
-switch ((glui32)(ch) >> 8) {  \
-case 3:  \
-switch (ch) {  \
-    case 820:  \
-    case 821:  \
-    case 822:  \
-    case 823:  \
-    case 824:  \
-    return 1;  \
-    case 801:  \
-    case 802:  \
-    case 807:  \
-    case 808:  \
-    return 202;  \
-    case 795:  \
-    return 216;  \
-    case 790:  \
-    case 791:  \
-    case 792:  \
-    case 793:  \
-    case 796:  \
-    case 797:  \
-    case 798:  \
-    case 799:  \
-    case 800:  \
-    case 803:  \
-    case 804:  \
-    case 805:  \
-    case 806:  \
-    case 809:  \
-    case 810:  \
-    case 811:  \
-    case 812:  \
-    case 813:  \
-    case 814:  \
-    case 815:  \
-    case 816:  \
-    case 817:  \
-    case 818:  \
-    case 819:  \
-    case 825:  \
-    case 826:  \
-    case 827:  \
-    case 828:  \
-    case 839:  \
-    case 840:  \
-    case 841:  \
-    case 845:  \
-    case 846:  \
-    case 851:  \
-    case 852:  \
-    case 853:  \
-    case 854:  \
-    return 220;  \
-    case 768:  \
-    case 769:  \
-    case 770:  \
-    case 771:  \
-    case 772:  \
-    case 773:  \
-    case 774:  \
-    case 775:  \
-    case 776:  \
-    case 777:  \
-    case 778:  \
-    case 779:  \
-    case 780:  \
-    case 781:  \
-    case 782:  \
-    case 783:  \
-    case 784:  \
-    case 785:  \
-    case 786:  \
-    case 787:  \
-    case 788:  \
-    case 829:  \
-    case 830:  \
-    case 831:  \
-    case 832:  \
-    case 833:  \
-    case 834:  \
-    case 835:  \
-    case 836:  \
-    case 838:  \
-    case 842:  \
-    case 843:  \
-    case 844:  \
-    case 848:  \
-    case 849:  \
-    case 850:  \
-    case 855:  \
-    case 867:  \
-    case 868:  \
-    case 869:  \
-    case 870:  \
-    case 871:  \
-    case 872:  \
-    case 873:  \
-    case 874:  \
-    case 875:  \
-    case 876:  \
-    case 877:  \
-    case 878:  \
-    case 879:  \
-    return 230;  \
-    case 789:  \
-    case 794:  \
-    return 232;  \
-    case 863:  \
-    case 866:  \
-    return 233;  \
-    case 861:  \
-    case 862:  \
-    case 864:  \
-    case 865:  \
-    return 234;  \
-    case 837:  \
-    return 240;  \
-}  \
-return 0;  \
-case 4:  \
-switch (ch) {  \
-    case 1155:  \
-    case 1156:  \
-    case 1157:  \
-    case 1158:  \
-    return 230;  \
-}  \
-return 0;  \
-case 5:  \
-switch (ch) {  \
-    case 1456:  \
-    return 10;  \
-    case 1457:  \
-    return 11;  \
-    case 1458:  \
-    return 12;  \
-    case 1459:  \
-    return 13;  \
-    case 1460:  \
-    return 14;  \
-    case 1461:  \
-    return 15;  \
-    case 1462:  \
-    return 16;  \
-    case 1463:  \
-    return 17;  \
-    case 1464:  \
-    return 18;  \
-    case 1465:  \
-    return 19;  \
-    case 1467:  \
-    return 20;  \
-    case 1468:  \
-    return 21;  \
-    case 1469:  \
-    return 22;  \
-    case 1471:  \
-    return 23;  \
-    case 1473:  \
-    return 24;  \
-    case 1474:  \
-    return 25;  \
-    case 1425:  \
-    case 1430:  \
-    case 1435:  \
-    case 1443:  \
-    case 1444:  \
-    case 1445:  \
-    case 1446:  \
-    case 1447:  \
-    case 1450:  \
-    return 220;  \
-    case 1434:  \
-    case 1453:  \
-    return 222;  \
-    case 1454:  \
-    return 228;  \
-    case 1426:  \
-    case 1427:  \
-    case 1428:  \
-    case 1429:  \
-    case 1431:  \
-    case 1432:  \
-    case 1433:  \
-    case 1436:  \
-    case 1437:  \
-    case 1438:  \
-    case 1439:  \
-    case 1440:  \
-    case 1441:  \
-    case 1448:  \
-    case 1449:  \
-    case 1451:  \
-    case 1452:  \
-    case 1455:  \
-    case 1476:  \
-    return 230;  \
-}  \
-return 0;  \
-case 6:  \
-switch (ch) {  \
-    case 1611:  \
-    return 27;  \
-    case 1612:  \
-    return 28;  \
-    case 1613:  \
-    return 29;  \
-    case 1614:  \
-    return 30;  \
-    case 1615:  \
-    return 31;  \
-    case 1616:  \
-    return 32;  \
-    case 1617:  \
-    return 33;  \
-    case 1618:  \
-    return 34;  \
-    case 1648:  \
-    return 35;  \
-    case 1621:  \
-    case 1622:  \
-    case 1763:  \
-    case 1770:  \
-    case 1773:  \
-    return 220;  \
-    case 1552:  \
-    case 1553:  \
-    case 1554:  \
-    case 1555:  \
-    case 1556:  \
-    case 1557:  \
-    case 1619:  \
-    case 1620:  \
-    case 1623:  \
-    case 1624:  \
-    case 1750:  \
-    case 1751:  \
-    case 1752:  \
-    case 1753:  \
-    case 1754:  \
-    case 1755:  \
-    case 1756:  \
-    case 1759:  \
-    case 1760:  \
-    case 1761:  \
-    case 1762:  \
-    case 1764:  \
-    case 1767:  \
-    case 1768:  \
-    case 1771:  \
-    case 1772:  \
-    return 230;  \
-}  \
-return 0;  \
-case 7:  \
-switch (ch) {  \
-    case 1809:  \
-    return 36;  \
-    case 1841:  \
-    case 1844:  \
-    case 1847:  \
-    case 1848:  \
-    case 1849:  \
-    case 1851:  \
-    case 1852:  \
-    case 1854:  \
-    case 1858:  \
-    case 1860:  \
-    case 1862:  \
-    case 1864:  \
-    return 220;  \
-    case 1840:  \
-    case 1842:  \
-    case 1843:  \
-    case 1845:  \
-    case 1846:  \
-    case 1850:  \
-    case 1853:  \
-    case 1855:  \
-    case 1856:  \
-    case 1857:  \
-    case 1859:  \
-    case 1861:  \
-    case 1863:  \
-    case 1865:  \
-    case 1866:  \
-    return 230;  \
-}  \
-return 0;  \
-case 9:  \
-switch (ch) {  \
-    case 2364:  \
-    case 2492:  \
-    return 7;  \
-    case 2381:  \
-    case 2509:  \
-    return 9;  \
-    case 2386:  \
-    return 220;  \
-    case 2385:  \
-    case 2387:  \
-    case 2388:  \
-    return 230;  \
-}  \
-return 0;  \
-case 10:  \
-switch (ch) {  \
-    case 2620:  \
-    case 2748:  \
-    return 7;  \
-    case 2637:  \
-    case 2765:  \
-    return 9;  \
-}  \
-return 0;  \
-case 11:  \
-switch (ch) {  \
-    case 2876:  \
-    return 7;  \
-    case 2893:  \
-    case 3021:  \
-    return 9;  \
-}  \
-return 0;  \
-case 12:  \
-switch (ch) {  \
-    case 3260:  \
-    return 7;  \
-    case 3149:  \
-    case 3277:  \
-    return 9;  \
-    case 3157:  \
-    return 84;  \
-    case 3158:  \
-    return 91;  \
-}  \
-return 0;  \
-case 13:  \
-switch (ch) {  \
-    case 3405:  \
-    case 3530:  \
-    return 9;  \
-}  \
-return 0;  \
-case 14:  \
-switch (ch) {  \
-    case 3642:  \
-    return 9;  \
-    case 3640:  \
-    case 3641:  \
-    return 103;  \
-    case 3656:  \
-    case 3657:  \
-    case 3658:  \
-    case 3659:  \
-    return 107;  \
-    case 3768:  \
-    case 3769:  \
-    return 118;  \
-    case 3784:  \
-    case 3785:  \
-    case 3786:  \
-    case 3787:  \
-    return 122;  \
-}  \
-return 0;  \
-case 15:  \
-switch (ch) {  \
-    case 3972:  \
-    return 9;  \
-    case 3953:  \
-    return 129;  \
-    case 3954:  \
-    case 3962:  \
-    case 3963:  \
-    case 3964:  \
-    case 3965:  \
-    case 3968:  \
-    return 130;  \
-    case 3956:  \
-    return 132;  \
-    case 3897:  \
-    return 216;  \
-    case 3864:  \
-    case 3865:  \
-    case 3893:  \
-    case 3895:  \
-    case 4038:  \
-    return 220;  \
-    case 3970:  \
-    case 3971:  \
-    case 3974:  \
-    case 3975:  \
-    return 230;  \
-}  \
-return 0;  \
-case 16:  \
-switch (ch) {  \
-    case 4151:  \
-    return 7;  \
-    case 4153:  \
-    return 9;  \
-}  \
-return 0;  \
-case 23:  \
-switch (ch) {  \
-    case 5908:  \
-    case 5940:  \
-    case 6098:  \
-    return 9;  \
-    case 6109:  \
-    return 230;  \
-}  \
-return 0;  \
-case 24:  \
-switch (ch) {  \
-    case 6313:  \
-    return 228;  \
-}  \
-return 0;  \
-case 25:  \
-switch (ch) {  \
-    case 6459:  \
-    return 220;  \
-    case 6457:  \
-    return 222;  \
-    case 6458:  \
-    return 230;  \
-}  \
-return 0;  \
-case 32:  \
-switch (ch) {  \
-    case 8402:  \
-    case 8403:  \
-    case 8408:  \
-    case 8409:  \
-    case 8410:  \
-    case 8421:  \
-    case 8422:  \
-    case 8426:  \
-    return 1;  \
-    case 8424:  \
-    return 220;  \
-    case 8400:  \
-    case 8401:  \
-    case 8404:  \
-    case 8405:  \
-    case 8406:  \
-    case 8407:  \
-    case 8411:  \
-    case 8412:  \
-    case 8417:  \
-    case 8423:  \
-    case 8425:  \
-    return 230;  \
-}  \
-return 0;  \
-case 48:  \
-switch (ch) {  \
-    case 12441:  \
-    case 12442:  \
-    return 8;  \
-    case 12330:  \
-    return 218;  \
-    case 12333:  \
-    return 222;  \
-    case 12334:  \
-    case 12335:  \
-    return 224;  \
-    case 12331:  \
-    return 228;  \
-    case 12332:  \
-    return 232;  \
-}  \
-return 0;  \
-case 251:  \
-switch (ch) {  \
-    case 64286:  \
-    return 26;  \
-}  \
-return 0;  \
-case 254:  \
-switch (ch) {  \
-    case 65056:  \
-    case 65057:  \
-    case 65058:  \
-    case 65059:  \
-    return 230;  \
-}  \
-return 0;  \
-case 465:  \
-switch (ch) {  \
-    case 119143:  \
-    case 119144:  \
-    case 119145:  \
-    return 1;  \
-    case 119141:  \
-    case 119142:  \
-    case 119150:  \
-    case 119151:  \
-    case 119152:  \
-    case 119153:  \
-    case 119154:  \
-    return 216;  \
-    case 119163:  \
-    case 119164:  \
-    case 119165:  \
-    case 119166:  \
-    case 119167:  \
-    case 119168:  \
-    case 119169:  \
-    case 119170:  \
-    case 119178:  \
-    case 119179:  \
-    return 220;  \
-    case 119149:  \
-    return 226;  \
-    case 119173:  \
-    case 119174:  \
-    case 119175:  \
-    case 119176:  \
-    case 119177:  \
-    case 119210:  \
-    case 119211:  \
-    case 119212:  \
-    case 119213:  \
-    return 230;  \
-}  \
-return 0;  \
-}  \
-return 0;
+	switch ((glui32)(ch) >> 8) {  \
+	case 3:  \
+		switch (ch) {  \
+		case 820:  \
+		case 821:  \
+		case 822:  \
+		case 823:  \
+		case 824:  \
+			return 1;  \
+		case 801:  \
+		case 802:  \
+		case 807:  \
+		case 808:  \
+			return 202;  \
+		case 795:  \
+			return 216;  \
+		case 790:  \
+		case 791:  \
+		case 792:  \
+		case 793:  \
+		case 796:  \
+		case 797:  \
+		case 798:  \
+		case 799:  \
+		case 800:  \
+		case 803:  \
+		case 804:  \
+		case 805:  \
+		case 806:  \
+		case 809:  \
+		case 810:  \
+		case 811:  \
+		case 812:  \
+		case 813:  \
+		case 814:  \
+		case 815:  \
+		case 816:  \
+		case 817:  \
+		case 818:  \
+		case 819:  \
+		case 825:  \
+		case 826:  \
+		case 827:  \
+		case 828:  \
+		case 839:  \
+		case 840:  \
+		case 841:  \
+		case 845:  \
+		case 846:  \
+		case 851:  \
+		case 852:  \
+		case 853:  \
+		case 854:  \
+			return 220;  \
+		case 768:  \
+		case 769:  \
+		case 770:  \
+		case 771:  \
+		case 772:  \
+		case 773:  \
+		case 774:  \
+		case 775:  \
+		case 776:  \
+		case 777:  \
+		case 778:  \
+		case 779:  \
+		case 780:  \
+		case 781:  \
+		case 782:  \
+		case 783:  \
+		case 784:  \
+		case 785:  \
+		case 786:  \
+		case 787:  \
+		case 788:  \
+		case 829:  \
+		case 830:  \
+		case 831:  \
+		case 832:  \
+		case 833:  \
+		case 834:  \
+		case 835:  \
+		case 836:  \
+		case 838:  \
+		case 842:  \
+		case 843:  \
+		case 844:  \
+		case 848:  \
+		case 849:  \
+		case 850:  \
+		case 855:  \
+		case 867:  \
+		case 868:  \
+		case 869:  \
+		case 870:  \
+		case 871:  \
+		case 872:  \
+		case 873:  \
+		case 874:  \
+		case 875:  \
+		case 876:  \
+		case 877:  \
+		case 878:  \
+		case 879:  \
+			return 230;  \
+		case 789:  \
+		case 794:  \
+			return 232;  \
+		case 863:  \
+		case 866:  \
+			return 233;  \
+		case 861:  \
+		case 862:  \
+		case 864:  \
+		case 865:  \
+			return 234;  \
+		case 837:  \
+			return 240;  \
+		}  \
+		return 0;  \
+	case 4:  \
+		switch (ch) {  \
+		case 1155:  \
+		case 1156:  \
+		case 1157:  \
+		case 1158:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 5:  \
+		switch (ch) {  \
+		case 1456:  \
+			return 10;  \
+		case 1457:  \
+			return 11;  \
+		case 1458:  \
+			return 12;  \
+		case 1459:  \
+			return 13;  \
+		case 1460:  \
+			return 14;  \
+		case 1461:  \
+			return 15;  \
+		case 1462:  \
+			return 16;  \
+		case 1463:  \
+			return 17;  \
+		case 1464:  \
+			return 18;  \
+		case 1465:  \
+			return 19;  \
+		case 1467:  \
+			return 20;  \
+		case 1468:  \
+			return 21;  \
+		case 1469:  \
+			return 22;  \
+		case 1471:  \
+			return 23;  \
+		case 1473:  \
+			return 24;  \
+		case 1474:  \
+			return 25;  \
+		case 1425:  \
+		case 1430:  \
+		case 1435:  \
+		case 1443:  \
+		case 1444:  \
+		case 1445:  \
+		case 1446:  \
+		case 1447:  \
+		case 1450:  \
+			return 220;  \
+		case 1434:  \
+		case 1453:  \
+			return 222;  \
+		case 1454:  \
+			return 228;  \
+		case 1426:  \
+		case 1427:  \
+		case 1428:  \
+		case 1429:  \
+		case 1431:  \
+		case 1432:  \
+		case 1433:  \
+		case 1436:  \
+		case 1437:  \
+		case 1438:  \
+		case 1439:  \
+		case 1440:  \
+		case 1441:  \
+		case 1448:  \
+		case 1449:  \
+		case 1451:  \
+		case 1452:  \
+		case 1455:  \
+		case 1476:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 6:  \
+		switch (ch) {  \
+		case 1611:  \
+			return 27;  \
+		case 1612:  \
+			return 28;  \
+		case 1613:  \
+			return 29;  \
+		case 1614:  \
+			return 30;  \
+		case 1615:  \
+			return 31;  \
+		case 1616:  \
+			return 32;  \
+		case 1617:  \
+			return 33;  \
+		case 1618:  \
+			return 34;  \
+		case 1648:  \
+			return 35;  \
+		case 1621:  \
+		case 1622:  \
+		case 1763:  \
+		case 1770:  \
+		case 1773:  \
+			return 220;  \
+		case 1552:  \
+		case 1553:  \
+		case 1554:  \
+		case 1555:  \
+		case 1556:  \
+		case 1557:  \
+		case 1619:  \
+		case 1620:  \
+		case 1623:  \
+		case 1624:  \
+		case 1750:  \
+		case 1751:  \
+		case 1752:  \
+		case 1753:  \
+		case 1754:  \
+		case 1755:  \
+		case 1756:  \
+		case 1759:  \
+		case 1760:  \
+		case 1761:  \
+		case 1762:  \
+		case 1764:  \
+		case 1767:  \
+		case 1768:  \
+		case 1771:  \
+		case 1772:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 7:  \
+		switch (ch) {  \
+		case 1809:  \
+			return 36;  \
+		case 1841:  \
+		case 1844:  \
+		case 1847:  \
+		case 1848:  \
+		case 1849:  \
+		case 1851:  \
+		case 1852:  \
+		case 1854:  \
+		case 1858:  \
+		case 1860:  \
+		case 1862:  \
+		case 1864:  \
+			return 220;  \
+		case 1840:  \
+		case 1842:  \
+		case 1843:  \
+		case 1845:  \
+		case 1846:  \
+		case 1850:  \
+		case 1853:  \
+		case 1855:  \
+		case 1856:  \
+		case 1857:  \
+		case 1859:  \
+		case 1861:  \
+		case 1863:  \
+		case 1865:  \
+		case 1866:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 9:  \
+		switch (ch) {  \
+		case 2364:  \
+		case 2492:  \
+			return 7;  \
+		case 2381:  \
+		case 2509:  \
+			return 9;  \
+		case 2386:  \
+			return 220;  \
+		case 2385:  \
+		case 2387:  \
+		case 2388:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 10:  \
+		switch (ch) {  \
+		case 2620:  \
+		case 2748:  \
+			return 7;  \
+		case 2637:  \
+		case 2765:  \
+			return 9;  \
+		}  \
+		return 0;  \
+	case 11:  \
+		switch (ch) {  \
+		case 2876:  \
+			return 7;  \
+		case 2893:  \
+		case 3021:  \
+			return 9;  \
+		}  \
+		return 0;  \
+	case 12:  \
+		switch (ch) {  \
+		case 3260:  \
+			return 7;  \
+		case 3149:  \
+		case 3277:  \
+			return 9;  \
+		case 3157:  \
+			return 84;  \
+		case 3158:  \
+			return 91;  \
+		}  \
+		return 0;  \
+	case 13:  \
+		switch (ch) {  \
+		case 3405:  \
+		case 3530:  \
+			return 9;  \
+		}  \
+		return 0;  \
+	case 14:  \
+		switch (ch) {  \
+		case 3642:  \
+			return 9;  \
+		case 3640:  \
+		case 3641:  \
+			return 103;  \
+		case 3656:  \
+		case 3657:  \
+		case 3658:  \
+		case 3659:  \
+			return 107;  \
+		case 3768:  \
+		case 3769:  \
+			return 118;  \
+		case 3784:  \
+		case 3785:  \
+		case 3786:  \
+		case 3787:  \
+			return 122;  \
+		}  \
+		return 0;  \
+	case 15:  \
+		switch (ch) {  \
+		case 3972:  \
+			return 9;  \
+		case 3953:  \
+			return 129;  \
+		case 3954:  \
+		case 3962:  \
+		case 3963:  \
+		case 3964:  \
+		case 3965:  \
+		case 3968:  \
+			return 130;  \
+		case 3956:  \
+			return 132;  \
+		case 3897:  \
+			return 216;  \
+		case 3864:  \
+		case 3865:  \
+		case 3893:  \
+		case 3895:  \
+		case 4038:  \
+			return 220;  \
+		case 3970:  \
+		case 3971:  \
+		case 3974:  \
+		case 3975:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 16:  \
+		switch (ch) {  \
+		case 4151:  \
+			return 7;  \
+		case 4153:  \
+			return 9;  \
+		}  \
+		return 0;  \
+	case 23:  \
+		switch (ch) {  \
+		case 5908:  \
+		case 5940:  \
+		case 6098:  \
+			return 9;  \
+		case 6109:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 24:  \
+		switch (ch) {  \
+		case 6313:  \
+			return 228;  \
+		}  \
+		return 0;  \
+	case 25:  \
+		switch (ch) {  \
+		case 6459:  \
+			return 220;  \
+		case 6457:  \
+			return 222;  \
+		case 6458:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 32:  \
+		switch (ch) {  \
+		case 8402:  \
+		case 8403:  \
+		case 8408:  \
+		case 8409:  \
+		case 8410:  \
+		case 8421:  \
+		case 8422:  \
+		case 8426:  \
+			return 1;  \
+		case 8424:  \
+			return 220;  \
+		case 8400:  \
+		case 8401:  \
+		case 8404:  \
+		case 8405:  \
+		case 8406:  \
+		case 8407:  \
+		case 8411:  \
+		case 8412:  \
+		case 8417:  \
+		case 8423:  \
+		case 8425:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 48:  \
+		switch (ch) {  \
+		case 12441:  \
+		case 12442:  \
+			return 8;  \
+		case 12330:  \
+			return 218;  \
+		case 12333:  \
+			return 222;  \
+		case 12334:  \
+		case 12335:  \
+			return 224;  \
+		case 12331:  \
+			return 228;  \
+		case 12332:  \
+			return 232;  \
+		}  \
+		return 0;  \
+	case 251:  \
+		switch (ch) {  \
+		case 64286:  \
+			return 26;  \
+		}  \
+		return 0;  \
+	case 254:  \
+		switch (ch) {  \
+		case 65056:  \
+		case 65057:  \
+		case 65058:  \
+		case 65059:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	case 465:  \
+		switch (ch) {  \
+		case 119143:  \
+		case 119144:  \
+		case 119145:  \
+			return 1;  \
+		case 119141:  \
+		case 119142:  \
+		case 119150:  \
+		case 119151:  \
+		case 119152:  \
+		case 119153:  \
+		case 119154:  \
+			return 216;  \
+		case 119163:  \
+		case 119164:  \
+		case 119165:  \
+		case 119166:  \
+		case 119167:  \
+		case 119168:  \
+		case 119169:  \
+		case 119170:  \
+		case 119178:  \
+		case 119179:  \
+			return 220;  \
+		case 119149:  \
+			return 226;  \
+		case 119173:  \
+		case 119174:  \
+		case 119175:  \
+		case 119176:  \
+		case 119177:  \
+		case 119210:  \
+		case 119211:  \
+		case 119212:  \
+		case 119213:  \
+			return 230;  \
+		}  \
+		return 0;  \
+	}  \
+	return 0;
 
 #define RETURN_COMPOSITION(ch1, ch2)  \
-switch ((glui32)(ch1) >> 8) {  \
-case 0:  \
-switch (ch1) {  \
-    case 60:  \
-    switch (ch2) {  \
-        case 824: return 8814;  \
-    }  \
-    return 0;  \
-    case 61:  \
-    switch (ch2) {  \
-        case 824: return 8800;  \
-    }  \
-    return 0;  \
-    case 62:  \
-    switch (ch2) {  \
-        case 824: return 8815;  \
-    }  \
-    return 0;  \
-    case 65:  \
-    switch (ch2) {  \
-        case 768: return 192;  \
-        case 769: return 193;  \
-        case 770: return 194;  \
-        case 771: return 195;  \
-        case 772: return 256;  \
-        case 774: return 258;  \
-        case 775: return 550;  \
-        case 776: return 196;  \
-        case 777: return 7842;  \
-        case 778: return 197;  \
-        case 780: return 461;  \
-        case 783: return 512;  \
-        case 785: return 514;  \
-        case 803: return 7840;  \
-        case 805: return 7680;  \
-        case 808: return 260;  \
-    }  \
-    return 0;  \
-    case 66:  \
-    switch (ch2) {  \
-        case 775: return 7682;  \
-        case 803: return 7684;  \
-        case 817: return 7686;  \
-    }  \
-    return 0;  \
-    case 67:  \
-    switch (ch2) {  \
-        case 769: return 262;  \
-        case 770: return 264;  \
-        case 775: return 266;  \
-        case 780: return 268;  \
-        case 807: return 199;  \
-    }  \
-    return 0;  \
-    case 68:  \
-    switch (ch2) {  \
-        case 775: return 7690;  \
-        case 780: return 270;  \
-        case 803: return 7692;  \
-        case 807: return 7696;  \
-        case 813: return 7698;  \
-        case 817: return 7694;  \
-    }  \
-    return 0;  \
-    case 69:  \
-    switch (ch2) {  \
-        case 768: return 200;  \
-        case 769: return 201;  \
-        case 770: return 202;  \
-        case 771: return 7868;  \
-        case 772: return 274;  \
-        case 774: return 276;  \
-        case 775: return 278;  \
-        case 776: return 203;  \
-        case 777: return 7866;  \
-        case 780: return 282;  \
-        case 783: return 516;  \
-        case 785: return 518;  \
-        case 803: return 7864;  \
-        case 807: return 552;  \
-        case 808: return 280;  \
-        case 813: return 7704;  \
-        case 816: return 7706;  \
-    }  \
-    return 0;  \
-    case 70:  \
-    switch (ch2) {  \
-        case 775: return 7710;  \
-    }  \
-    return 0;  \
-    case 71:  \
-    switch (ch2) {  \
-        case 769: return 500;  \
-        case 770: return 284;  \
-        case 772: return 7712;  \
-        case 774: return 286;  \
-        case 775: return 288;  \
-        case 780: return 486;  \
-        case 807: return 290;  \
-    }  \
-    return 0;  \
-    case 72:  \
-    switch (ch2) {  \
-        case 770: return 292;  \
-        case 775: return 7714;  \
-        case 776: return 7718;  \
-        case 780: return 542;  \
-        case 803: return 7716;  \
-        case 807: return 7720;  \
-        case 814: return 7722;  \
-    }  \
-    return 0;  \
-    case 73:  \
-    switch (ch2) {  \
-        case 768: return 204;  \
-        case 769: return 205;  \
-        case 770: return 206;  \
-        case 771: return 296;  \
-        case 772: return 298;  \
-        case 774: return 300;  \
-        case 775: return 304;  \
-        case 776: return 207;  \
-        case 777: return 7880;  \
-        case 780: return 463;  \
-        case 783: return 520;  \
-        case 785: return 522;  \
-        case 803: return 7882;  \
-        case 808: return 302;  \
-        case 816: return 7724;  \
-    }  \
-    return 0;  \
-    case 74:  \
-    switch (ch2) {  \
-        case 770: return 308;  \
-    }  \
-    return 0;  \
-    case 75:  \
-    switch (ch2) {  \
-        case 769: return 7728;  \
-        case 780: return 488;  \
-        case 803: return 7730;  \
-        case 807: return 310;  \
-        case 817: return 7732;  \
-    }  \
-    return 0;  \
-    case 76:  \
-    switch (ch2) {  \
-        case 769: return 313;  \
-        case 780: return 317;  \
-        case 803: return 7734;  \
-        case 807: return 315;  \
-        case 813: return 7740;  \
-        case 817: return 7738;  \
-    }  \
-    return 0;  \
-    case 77:  \
-    switch (ch2) {  \
-        case 769: return 7742;  \
-        case 775: return 7744;  \
-        case 803: return 7746;  \
-    }  \
-    return 0;  \
-    case 78:  \
-    switch (ch2) {  \
-        case 768: return 504;  \
-        case 769: return 323;  \
-        case 771: return 209;  \
-        case 775: return 7748;  \
-        case 780: return 327;  \
-        case 803: return 7750;  \
-        case 807: return 325;  \
-        case 813: return 7754;  \
-        case 817: return 7752;  \
-    }  \
-    return 0;  \
-    case 79:  \
-    switch (ch2) {  \
-        case 768: return 210;  \
-        case 769: return 211;  \
-        case 770: return 212;  \
-        case 771: return 213;  \
-        case 772: return 332;  \
-        case 774: return 334;  \
-        case 775: return 558;  \
-        case 776: return 214;  \
-        case 777: return 7886;  \
-        case 779: return 336;  \
-        case 780: return 465;  \
-        case 783: return 524;  \
-        case 785: return 526;  \
-        case 795: return 416;  \
-        case 803: return 7884;  \
-        case 808: return 490;  \
-    }  \
-    return 0;  \
-    case 80:  \
-    switch (ch2) {  \
-        case 769: return 7764;  \
-        case 775: return 7766;  \
-    }  \
-    return 0;  \
-    case 82:  \
-    switch (ch2) {  \
-        case 769: return 340;  \
-        case 775: return 7768;  \
-        case 780: return 344;  \
-        case 783: return 528;  \
-        case 785: return 530;  \
-        case 803: return 7770;  \
-        case 807: return 342;  \
-        case 817: return 7774;  \
-    }  \
-    return 0;  \
-    case 83:  \
-    switch (ch2) {  \
-        case 769: return 346;  \
-        case 770: return 348;  \
-        case 775: return 7776;  \
-        case 780: return 352;  \
-        case 803: return 7778;  \
-        case 806: return 536;  \
-        case 807: return 350;  \
-    }  \
-    return 0;  \
-    case 84:  \
-    switch (ch2) {  \
-        case 775: return 7786;  \
-        case 780: return 356;  \
-        case 803: return 7788;  \
-        case 806: return 538;  \
-        case 807: return 354;  \
-        case 813: return 7792;  \
-        case 817: return 7790;  \
-    }  \
-    return 0;  \
-    case 85:  \
-    switch (ch2) {  \
-        case 768: return 217;  \
-        case 769: return 218;  \
-        case 770: return 219;  \
-        case 771: return 360;  \
-        case 772: return 362;  \
-        case 774: return 364;  \
-        case 776: return 220;  \
-        case 777: return 7910;  \
-        case 778: return 366;  \
-        case 779: return 368;  \
-        case 780: return 467;  \
-        case 783: return 532;  \
-        case 785: return 534;  \
-        case 795: return 431;  \
-        case 803: return 7908;  \
-        case 804: return 7794;  \
-        case 808: return 370;  \
-        case 813: return 7798;  \
-        case 816: return 7796;  \
-    }  \
-    return 0;  \
-    case 86:  \
-    switch (ch2) {  \
-        case 771: return 7804;  \
-        case 803: return 7806;  \
-    }  \
-    return 0;  \
-    case 87:  \
-    switch (ch2) {  \
-        case 768: return 7808;  \
-        case 769: return 7810;  \
-        case 770: return 372;  \
-        case 775: return 7814;  \
-        case 776: return 7812;  \
-        case 803: return 7816;  \
-    }  \
-    return 0;  \
-    case 88:  \
-    switch (ch2) {  \
-        case 775: return 7818;  \
-        case 776: return 7820;  \
-    }  \
-    return 0;  \
-    case 89:  \
-    switch (ch2) {  \
-        case 768: return 7922;  \
-        case 769: return 221;  \
-        case 770: return 374;  \
-        case 771: return 7928;  \
-        case 772: return 562;  \
-        case 775: return 7822;  \
-        case 776: return 376;  \
-        case 777: return 7926;  \
-        case 803: return 7924;  \
-    }  \
-    return 0;  \
-    case 90:  \
-    switch (ch2) {  \
-        case 769: return 377;  \
-        case 770: return 7824;  \
-        case 775: return 379;  \
-        case 780: return 381;  \
-        case 803: return 7826;  \
-        case 817: return 7828;  \
-    }  \
-    return 0;  \
-    case 97:  \
-    switch (ch2) {  \
-        case 768: return 224;  \
-        case 769: return 225;  \
-        case 770: return 226;  \
-        case 771: return 227;  \
-        case 772: return 257;  \
-        case 774: return 259;  \
-        case 775: return 551;  \
-        case 776: return 228;  \
-        case 777: return 7843;  \
-        case 778: return 229;  \
-        case 780: return 462;  \
-        case 783: return 513;  \
-        case 785: return 515;  \
-        case 803: return 7841;  \
-        case 805: return 7681;  \
-        case 808: return 261;  \
-    }  \
-    return 0;  \
-    case 98:  \
-    switch (ch2) {  \
-        case 775: return 7683;  \
-        case 803: return 7685;  \
-        case 817: return 7687;  \
-    }  \
-    return 0;  \
-    case 99:  \
-    switch (ch2) {  \
-        case 769: return 263;  \
-        case 770: return 265;  \
-        case 775: return 267;  \
-        case 780: return 269;  \
-        case 807: return 231;  \
-    }  \
-    return 0;  \
-    case 100:  \
-    switch (ch2) {  \
-        case 775: return 7691;  \
-        case 780: return 271;  \
-        case 803: return 7693;  \
-        case 807: return 7697;  \
-        case 813: return 7699;  \
-        case 817: return 7695;  \
-    }  \
-    return 0;  \
-    case 101:  \
-    switch (ch2) {  \
-        case 768: return 232;  \
-        case 769: return 233;  \
-        case 770: return 234;  \
-        case 771: return 7869;  \
-        case 772: return 275;  \
-        case 774: return 277;  \
-        case 775: return 279;  \
-        case 776: return 235;  \
-        case 777: return 7867;  \
-        case 780: return 283;  \
-        case 783: return 517;  \
-        case 785: return 519;  \
-        case 803: return 7865;  \
-        case 807: return 553;  \
-        case 808: return 281;  \
-        case 813: return 7705;  \
-        case 816: return 7707;  \
-    }  \
-    return 0;  \
-    case 102:  \
-    switch (ch2) {  \
-        case 775: return 7711;  \
-    }  \
-    return 0;  \
-    case 103:  \
-    switch (ch2) {  \
-        case 769: return 501;  \
-        case 770: return 285;  \
-        case 772: return 7713;  \
-        case 774: return 287;  \
-        case 775: return 289;  \
-        case 780: return 487;  \
-        case 807: return 291;  \
-    }  \
-    return 0;  \
-    case 104:  \
-    switch (ch2) {  \
-        case 770: return 293;  \
-        case 775: return 7715;  \
-        case 776: return 7719;  \
-        case 780: return 543;  \
-        case 803: return 7717;  \
-        case 807: return 7721;  \
-        case 814: return 7723;  \
-        case 817: return 7830;  \
-    }  \
-    return 0;  \
-    case 105:  \
-    switch (ch2) {  \
-        case 768: return 236;  \
-        case 769: return 237;  \
-        case 770: return 238;  \
-        case 771: return 297;  \
-        case 772: return 299;  \
-        case 774: return 301;  \
-        case 776: return 239;  \
-        case 777: return 7881;  \
-        case 780: return 464;  \
-        case 783: return 521;  \
-        case 785: return 523;  \
-        case 803: return 7883;  \
-        case 808: return 303;  \
-        case 816: return 7725;  \
-    }  \
-    return 0;  \
-    case 106:  \
-    switch (ch2) {  \
-        case 770: return 309;  \
-        case 780: return 496;  \
-    }  \
-    return 0;  \
-    case 107:  \
-    switch (ch2) {  \
-        case 769: return 7729;  \
-        case 780: return 489;  \
-        case 803: return 7731;  \
-        case 807: return 311;  \
-        case 817: return 7733;  \
-    }  \
-    return 0;  \
-    case 108:  \
-    switch (ch2) {  \
-        case 769: return 314;  \
-        case 780: return 318;  \
-        case 803: return 7735;  \
-        case 807: return 316;  \
-        case 813: return 7741;  \
-        case 817: return 7739;  \
-    }  \
-    return 0;  \
-    case 109:  \
-    switch (ch2) {  \
-        case 769: return 7743;  \
-        case 775: return 7745;  \
-        case 803: return 7747;  \
-    }  \
-    return 0;  \
-    case 110:  \
-    switch (ch2) {  \
-        case 768: return 505;  \
-        case 769: return 324;  \
-        case 771: return 241;  \
-        case 775: return 7749;  \
-        case 780: return 328;  \
-        case 803: return 7751;  \
-        case 807: return 326;  \
-        case 813: return 7755;  \
-        case 817: return 7753;  \
-    }  \
-    return 0;  \
-    case 111:  \
-    switch (ch2) {  \
-        case 768: return 242;  \
-        case 769: return 243;  \
-        case 770: return 244;  \
-        case 771: return 245;  \
-        case 772: return 333;  \
-        case 774: return 335;  \
-        case 775: return 559;  \
-        case 776: return 246;  \
-        case 777: return 7887;  \
-        case 779: return 337;  \
-        case 780: return 466;  \
-        case 783: return 525;  \
-        case 785: return 527;  \
-        case 795: return 417;  \
-        case 803: return 7885;  \
-        case 808: return 491;  \
-    }  \
-    return 0;  \
-    case 112:  \
-    switch (ch2) {  \
-        case 769: return 7765;  \
-        case 775: return 7767;  \
-    }  \
-    return 0;  \
-    case 114:  \
-    switch (ch2) {  \
-        case 769: return 341;  \
-        case 775: return 7769;  \
-        case 780: return 345;  \
-        case 783: return 529;  \
-        case 785: return 531;  \
-        case 803: return 7771;  \
-        case 807: return 343;  \
-        case 817: return 7775;  \
-    }  \
-    return 0;  \
-    case 115:  \
-    switch (ch2) {  \
-        case 769: return 347;  \
-        case 770: return 349;  \
-        case 775: return 7777;  \
-        case 780: return 353;  \
-        case 803: return 7779;  \
-        case 806: return 537;  \
-        case 807: return 351;  \
-    }  \
-    return 0;  \
-    case 116:  \
-    switch (ch2) {  \
-        case 775: return 7787;  \
-        case 776: return 7831;  \
-        case 780: return 357;  \
-        case 803: return 7789;  \
-        case 806: return 539;  \
-        case 807: return 355;  \
-        case 813: return 7793;  \
-        case 817: return 7791;  \
-    }  \
-    return 0;  \
-    case 117:  \
-    switch (ch2) {  \
-        case 768: return 249;  \
-        case 769: return 250;  \
-        case 770: return 251;  \
-        case 771: return 361;  \
-        case 772: return 363;  \
-        case 774: return 365;  \
-        case 776: return 252;  \
-        case 777: return 7911;  \
-        case 778: return 367;  \
-        case 779: return 369;  \
-        case 780: return 468;  \
-        case 783: return 533;  \
-        case 785: return 535;  \
-        case 795: return 432;  \
-        case 803: return 7909;  \
-        case 804: return 7795;  \
-        case 808: return 371;  \
-        case 813: return 7799;  \
-        case 816: return 7797;  \
-    }  \
-    return 0;  \
-    case 118:  \
-    switch (ch2) {  \
-        case 771: return 7805;  \
-        case 803: return 7807;  \
-    }  \
-    return 0;  \
-    case 119:  \
-    switch (ch2) {  \
-        case 768: return 7809;  \
-        case 769: return 7811;  \
-        case 770: return 373;  \
-        case 775: return 7815;  \
-        case 776: return 7813;  \
-        case 778: return 7832;  \
-        case 803: return 7817;  \
-    }  \
-    return 0;  \
-    case 120:  \
-    switch (ch2) {  \
-        case 775: return 7819;  \
-        case 776: return 7821;  \
-    }  \
-    return 0;  \
-    case 121:  \
-    switch (ch2) {  \
-        case 768: return 7923;  \
-        case 769: return 253;  \
-        case 770: return 375;  \
-        case 771: return 7929;  \
-        case 772: return 563;  \
-        case 775: return 7823;  \
-        case 776: return 255;  \
-        case 777: return 7927;  \
-        case 778: return 7833;  \
-        case 803: return 7925;  \
-    }  \
-    return 0;  \
-    case 122:  \
-    switch (ch2) {  \
-        case 769: return 378;  \
-        case 770: return 7825;  \
-        case 775: return 380;  \
-        case 780: return 382;  \
-        case 803: return 7827;  \
-        case 817: return 7829;  \
-    }  \
-    return 0;  \
-    case 168:  \
-    switch (ch2) {  \
-        case 768: return 8173;  \
-        case 769: return 901;  \
-        case 834: return 8129;  \
-    }  \
-    return 0;  \
-    case 194:  \
-    switch (ch2) {  \
-        case 768: return 7846;  \
-        case 769: return 7844;  \
-        case 771: return 7850;  \
-        case 777: return 7848;  \
-    }  \
-    return 0;  \
-    case 196:  \
-    switch (ch2) {  \
-        case 772: return 478;  \
-    }  \
-    return 0;  \
-    case 197:  \
-    switch (ch2) {  \
-        case 769: return 506;  \
-    }  \
-    return 0;  \
-    case 198:  \
-    switch (ch2) {  \
-        case 769: return 508;  \
-        case 772: return 482;  \
-    }  \
-    return 0;  \
-    case 199:  \
-    switch (ch2) {  \
-        case 769: return 7688;  \
-    }  \
-    return 0;  \
-    case 202:  \
-    switch (ch2) {  \
-        case 768: return 7872;  \
-        case 769: return 7870;  \
-        case 771: return 7876;  \
-        case 777: return 7874;  \
-    }  \
-    return 0;  \
-    case 207:  \
-    switch (ch2) {  \
-        case 769: return 7726;  \
-    }  \
-    return 0;  \
-    case 212:  \
-    switch (ch2) {  \
-        case 768: return 7890;  \
-        case 769: return 7888;  \
-        case 771: return 7894;  \
-        case 777: return 7892;  \
-    }  \
-    return 0;  \
-    case 213:  \
-    switch (ch2) {  \
-        case 769: return 7756;  \
-        case 772: return 556;  \
-        case 776: return 7758;  \
-    }  \
-    return 0;  \
-    case 214:  \
-    switch (ch2) {  \
-        case 772: return 554;  \
-    }  \
-    return 0;  \
-    case 216:  \
-    switch (ch2) {  \
-        case 769: return 510;  \
-    }  \
-    return 0;  \
-    case 220:  \
-    switch (ch2) {  \
-        case 768: return 475;  \
-        case 769: return 471;  \
-        case 772: return 469;  \
-        case 780: return 473;  \
-    }  \
-    return 0;  \
-    case 226:  \
-    switch (ch2) {  \
-        case 768: return 7847;  \
-        case 769: return 7845;  \
-        case 771: return 7851;  \
-        case 777: return 7849;  \
-    }  \
-    return 0;  \
-    case 228:  \
-    switch (ch2) {  \
-        case 772: return 479;  \
-    }  \
-    return 0;  \
-    case 229:  \
-    switch (ch2) {  \
-        case 769: return 507;  \
-    }  \
-    return 0;  \
-    case 230:  \
-    switch (ch2) {  \
-        case 769: return 509;  \
-        case 772: return 483;  \
-    }  \
-    return 0;  \
-    case 231:  \
-    switch (ch2) {  \
-        case 769: return 7689;  \
-    }  \
-    return 0;  \
-    case 234:  \
-    switch (ch2) {  \
-        case 768: return 7873;  \
-        case 769: return 7871;  \
-        case 771: return 7877;  \
-        case 777: return 7875;  \
-    }  \
-    return 0;  \
-    case 239:  \
-    switch (ch2) {  \
-        case 769: return 7727;  \
-    }  \
-    return 0;  \
-    case 244:  \
-    switch (ch2) {  \
-        case 768: return 7891;  \
-        case 769: return 7889;  \
-        case 771: return 7895;  \
-        case 777: return 7893;  \
-    }  \
-    return 0;  \
-    case 245:  \
-    switch (ch2) {  \
-        case 769: return 7757;  \
-        case 772: return 557;  \
-        case 776: return 7759;  \
-    }  \
-    return 0;  \
-    case 246:  \
-    switch (ch2) {  \
-        case 772: return 555;  \
-    }  \
-    return 0;  \
-    case 248:  \
-    switch (ch2) {  \
-        case 769: return 511;  \
-    }  \
-    return 0;  \
-    case 252:  \
-    switch (ch2) {  \
-        case 768: return 476;  \
-        case 769: return 472;  \
-        case 772: return 470;  \
-        case 780: return 474;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 1:  \
-switch (ch1) {  \
-    case 258:  \
-    switch (ch2) {  \
-        case 768: return 7856;  \
-        case 769: return 7854;  \
-        case 771: return 7860;  \
-        case 777: return 7858;  \
-    }  \
-    return 0;  \
-    case 259:  \
-    switch (ch2) {  \
-        case 768: return 7857;  \
-        case 769: return 7855;  \
-        case 771: return 7861;  \
-        case 777: return 7859;  \
-    }  \
-    return 0;  \
-    case 274:  \
-    switch (ch2) {  \
-        case 768: return 7700;  \
-        case 769: return 7702;  \
-    }  \
-    return 0;  \
-    case 275:  \
-    switch (ch2) {  \
-        case 768: return 7701;  \
-        case 769: return 7703;  \
-    }  \
-    return 0;  \
-    case 332:  \
-    switch (ch2) {  \
-        case 768: return 7760;  \
-        case 769: return 7762;  \
-    }  \
-    return 0;  \
-    case 333:  \
-    switch (ch2) {  \
-        case 768: return 7761;  \
-        case 769: return 7763;  \
-    }  \
-    return 0;  \
-    case 346:  \
-    switch (ch2) {  \
-        case 775: return 7780;  \
-    }  \
-    return 0;  \
-    case 347:  \
-    switch (ch2) {  \
-        case 775: return 7781;  \
-    }  \
-    return 0;  \
-    case 352:  \
-    switch (ch2) {  \
-        case 775: return 7782;  \
-    }  \
-    return 0;  \
-    case 353:  \
-    switch (ch2) {  \
-        case 775: return 7783;  \
-    }  \
-    return 0;  \
-    case 360:  \
-    switch (ch2) {  \
-        case 769: return 7800;  \
-    }  \
-    return 0;  \
-    case 361:  \
-    switch (ch2) {  \
-        case 769: return 7801;  \
-    }  \
-    return 0;  \
-    case 362:  \
-    switch (ch2) {  \
-        case 776: return 7802;  \
-    }  \
-    return 0;  \
-    case 363:  \
-    switch (ch2) {  \
-        case 776: return 7803;  \
-    }  \
-    return 0;  \
-    case 383:  \
-    switch (ch2) {  \
-        case 775: return 7835;  \
-    }  \
-    return 0;  \
-    case 416:  \
-    switch (ch2) {  \
-        case 768: return 7900;  \
-        case 769: return 7898;  \
-        case 771: return 7904;  \
-        case 777: return 7902;  \
-        case 803: return 7906;  \
-    }  \
-    return 0;  \
-    case 417:  \
-    switch (ch2) {  \
-        case 768: return 7901;  \
-        case 769: return 7899;  \
-        case 771: return 7905;  \
-        case 777: return 7903;  \
-        case 803: return 7907;  \
-    }  \
-    return 0;  \
-    case 431:  \
-    switch (ch2) {  \
-        case 768: return 7914;  \
-        case 769: return 7912;  \
-        case 771: return 7918;  \
-        case 777: return 7916;  \
-        case 803: return 7920;  \
-    }  \
-    return 0;  \
-    case 432:  \
-    switch (ch2) {  \
-        case 768: return 7915;  \
-        case 769: return 7913;  \
-        case 771: return 7919;  \
-        case 777: return 7917;  \
-        case 803: return 7921;  \
-    }  \
-    return 0;  \
-    case 439:  \
-    switch (ch2) {  \
-        case 780: return 494;  \
-    }  \
-    return 0;  \
-    case 490:  \
-    switch (ch2) {  \
-        case 772: return 492;  \
-    }  \
-    return 0;  \
-    case 491:  \
-    switch (ch2) {  \
-        case 772: return 493;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 2:  \
-switch (ch1) {  \
-    case 550:  \
-    switch (ch2) {  \
-        case 772: return 480;  \
-    }  \
-    return 0;  \
-    case 551:  \
-    switch (ch2) {  \
-        case 772: return 481;  \
-    }  \
-    return 0;  \
-    case 552:  \
-    switch (ch2) {  \
-        case 774: return 7708;  \
-    }  \
-    return 0;  \
-    case 553:  \
-    switch (ch2) {  \
-        case 774: return 7709;  \
-    }  \
-    return 0;  \
-    case 558:  \
-    switch (ch2) {  \
-        case 772: return 560;  \
-    }  \
-    return 0;  \
-    case 559:  \
-    switch (ch2) {  \
-        case 772: return 561;  \
-    }  \
-    return 0;  \
-    case 658:  \
-    switch (ch2) {  \
-        case 780: return 495;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 3:  \
-switch (ch1) {  \
-    case 776:  \
-    switch (ch2) {  \
-        case 769: return 836;  \
-    }  \
-    return 0;  \
-    case 913:  \
-    switch (ch2) {  \
-        case 768: return 8122;  \
-        case 769: return 902;  \
-        case 772: return 8121;  \
-        case 774: return 8120;  \
-        case 787: return 7944;  \
-        case 788: return 7945;  \
-        case 837: return 8124;  \
-    }  \
-    return 0;  \
-    case 917:  \
-    switch (ch2) {  \
-        case 768: return 8136;  \
-        case 769: return 904;  \
-        case 787: return 7960;  \
-        case 788: return 7961;  \
-    }  \
-    return 0;  \
-    case 919:  \
-    switch (ch2) {  \
-        case 768: return 8138;  \
-        case 769: return 905;  \
-        case 787: return 7976;  \
-        case 788: return 7977;  \
-        case 837: return 8140;  \
-    }  \
-    return 0;  \
-    case 921:  \
-    switch (ch2) {  \
-        case 768: return 8154;  \
-        case 769: return 906;  \
-        case 772: return 8153;  \
-        case 774: return 8152;  \
-        case 776: return 938;  \
-        case 787: return 7992;  \
-        case 788: return 7993;  \
-    }  \
-    return 0;  \
-    case 927:  \
-    switch (ch2) {  \
-        case 768: return 8184;  \
-        case 769: return 908;  \
-        case 787: return 8008;  \
-        case 788: return 8009;  \
-    }  \
-    return 0;  \
-    case 929:  \
-    switch (ch2) {  \
-        case 788: return 8172;  \
-    }  \
-    return 0;  \
-    case 933:  \
-    switch (ch2) {  \
-        case 768: return 8170;  \
-        case 769: return 910;  \
-        case 772: return 8169;  \
-        case 774: return 8168;  \
-        case 776: return 939;  \
-        case 788: return 8025;  \
-    }  \
-    return 0;  \
-    case 937:  \
-    switch (ch2) {  \
-        case 768: return 8186;  \
-        case 769: return 911;  \
-        case 787: return 8040;  \
-        case 788: return 8041;  \
-        case 837: return 8188;  \
-    }  \
-    return 0;  \
-    case 940:  \
-    switch (ch2) {  \
-        case 837: return 8116;  \
-    }  \
-    return 0;  \
-    case 942:  \
-    switch (ch2) {  \
-        case 837: return 8132;  \
-    }  \
-    return 0;  \
-    case 945:  \
-    switch (ch2) {  \
-        case 768: return 8048;  \
-        case 769: return 940;  \
-        case 772: return 8113;  \
-        case 774: return 8112;  \
-        case 787: return 7936;  \
-        case 788: return 7937;  \
-        case 834: return 8118;  \
-        case 837: return 8115;  \
-    }  \
-    return 0;  \
-    case 949:  \
-    switch (ch2) {  \
-        case 768: return 8050;  \
-        case 769: return 941;  \
-        case 787: return 7952;  \
-        case 788: return 7953;  \
-    }  \
-    return 0;  \
-    case 951:  \
-    switch (ch2) {  \
-        case 768: return 8052;  \
-        case 769: return 942;  \
-        case 787: return 7968;  \
-        case 788: return 7969;  \
-        case 834: return 8134;  \
-        case 837: return 8131;  \
-    }  \
-    return 0;  \
-    case 953:  \
-    switch (ch2) {  \
-        case 768: return 8054;  \
-        case 769: return 943;  \
-        case 772: return 8145;  \
-        case 774: return 8144;  \
-        case 776: return 970;  \
-        case 787: return 7984;  \
-        case 788: return 7985;  \
-        case 834: return 8150;  \
-    }  \
-    return 0;  \
-    case 959:  \
-    switch (ch2) {  \
-        case 768: return 8056;  \
-        case 769: return 972;  \
-        case 787: return 8000;  \
-        case 788: return 8001;  \
-    }  \
-    return 0;  \
-    case 961:  \
-    switch (ch2) {  \
-        case 787: return 8164;  \
-        case 788: return 8165;  \
-    }  \
-    return 0;  \
-    case 965:  \
-    switch (ch2) {  \
-        case 768: return 8058;  \
-        case 769: return 973;  \
-        case 772: return 8161;  \
-        case 774: return 8160;  \
-        case 776: return 971;  \
-        case 787: return 8016;  \
-        case 788: return 8017;  \
-        case 834: return 8166;  \
-    }  \
-    return 0;  \
-    case 969:  \
-    switch (ch2) {  \
-        case 768: return 8060;  \
-        case 769: return 974;  \
-        case 787: return 8032;  \
-        case 788: return 8033;  \
-        case 834: return 8182;  \
-        case 837: return 8179;  \
-    }  \
-    return 0;  \
-    case 970:  \
-    switch (ch2) {  \
-        case 768: return 8146;  \
-        case 769: return 912;  \
-        case 834: return 8151;  \
-    }  \
-    return 0;  \
-    case 971:  \
-    switch (ch2) {  \
-        case 768: return 8162;  \
-        case 769: return 944;  \
-        case 834: return 8167;  \
-    }  \
-    return 0;  \
-    case 974:  \
-    switch (ch2) {  \
-        case 837: return 8180;  \
-    }  \
-    return 0;  \
-    case 978:  \
-    switch (ch2) {  \
-        case 769: return 979;  \
-        case 776: return 980;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 4:  \
-switch (ch1) {  \
-    case 1030:  \
-    switch (ch2) {  \
-        case 776: return 1031;  \
-    }  \
-    return 0;  \
-    case 1040:  \
-    switch (ch2) {  \
-        case 774: return 1232;  \
-        case 776: return 1234;  \
-    }  \
-    return 0;  \
-    case 1043:  \
-    switch (ch2) {  \
-        case 769: return 1027;  \
-    }  \
-    return 0;  \
-    case 1045:  \
-    switch (ch2) {  \
-        case 768: return 1024;  \
-        case 774: return 1238;  \
-        case 776: return 1025;  \
-    }  \
-    return 0;  \
-    case 1046:  \
-    switch (ch2) {  \
-        case 774: return 1217;  \
-        case 776: return 1244;  \
-    }  \
-    return 0;  \
-    case 1047:  \
-    switch (ch2) {  \
-        case 776: return 1246;  \
-    }  \
-    return 0;  \
-    case 1048:  \
-    switch (ch2) {  \
-        case 768: return 1037;  \
-        case 772: return 1250;  \
-        case 774: return 1049;  \
-        case 776: return 1252;  \
-    }  \
-    return 0;  \
-    case 1050:  \
-    switch (ch2) {  \
-        case 769: return 1036;  \
-    }  \
-    return 0;  \
-    case 1054:  \
-    switch (ch2) {  \
-        case 776: return 1254;  \
-    }  \
-    return 0;  \
-    case 1059:  \
-    switch (ch2) {  \
-        case 772: return 1262;  \
-        case 774: return 1038;  \
-        case 776: return 1264;  \
-        case 779: return 1266;  \
-    }  \
-    return 0;  \
-    case 1063:  \
-    switch (ch2) {  \
-        case 776: return 1268;  \
-    }  \
-    return 0;  \
-    case 1067:  \
-    switch (ch2) {  \
-        case 776: return 1272;  \
-    }  \
-    return 0;  \
-    case 1069:  \
-    switch (ch2) {  \
-        case 776: return 1260;  \
-    }  \
-    return 0;  \
-    case 1072:  \
-    switch (ch2) {  \
-        case 774: return 1233;  \
-        case 776: return 1235;  \
-    }  \
-    return 0;  \
-    case 1075:  \
-    switch (ch2) {  \
-        case 769: return 1107;  \
-    }  \
-    return 0;  \
-    case 1077:  \
-    switch (ch2) {  \
-        case 768: return 1104;  \
-        case 774: return 1239;  \
-        case 776: return 1105;  \
-    }  \
-    return 0;  \
-    case 1078:  \
-    switch (ch2) {  \
-        case 774: return 1218;  \
-        case 776: return 1245;  \
-    }  \
-    return 0;  \
-    case 1079:  \
-    switch (ch2) {  \
-        case 776: return 1247;  \
-    }  \
-    return 0;  \
-    case 1080:  \
-    switch (ch2) {  \
-        case 768: return 1117;  \
-        case 772: return 1251;  \
-        case 774: return 1081;  \
-        case 776: return 1253;  \
-    }  \
-    return 0;  \
-    case 1082:  \
-    switch (ch2) {  \
-        case 769: return 1116;  \
-    }  \
-    return 0;  \
-    case 1086:  \
-    switch (ch2) {  \
-        case 776: return 1255;  \
-    }  \
-    return 0;  \
-    case 1091:  \
-    switch (ch2) {  \
-        case 772: return 1263;  \
-        case 774: return 1118;  \
-        case 776: return 1265;  \
-        case 779: return 1267;  \
-    }  \
-    return 0;  \
-    case 1095:  \
-    switch (ch2) {  \
-        case 776: return 1269;  \
-    }  \
-    return 0;  \
-    case 1099:  \
-    switch (ch2) {  \
-        case 776: return 1273;  \
-    }  \
-    return 0;  \
-    case 1101:  \
-    switch (ch2) {  \
-        case 776: return 1261;  \
-    }  \
-    return 0;  \
-    case 1110:  \
-    switch (ch2) {  \
-        case 776: return 1111;  \
-    }  \
-    return 0;  \
-    case 1140:  \
-    switch (ch2) {  \
-        case 783: return 1142;  \
-    }  \
-    return 0;  \
-    case 1141:  \
-    switch (ch2) {  \
-        case 783: return 1143;  \
-    }  \
-    return 0;  \
-    case 1240:  \
-    switch (ch2) {  \
-        case 776: return 1242;  \
-    }  \
-    return 0;  \
-    case 1241:  \
-    switch (ch2) {  \
-        case 776: return 1243;  \
-    }  \
-    return 0;  \
-    case 1256:  \
-    switch (ch2) {  \
-        case 776: return 1258;  \
-    }  \
-    return 0;  \
-    case 1257:  \
-    switch (ch2) {  \
-        case 776: return 1259;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 5:  \
-switch (ch1) {  \
-    case 1488:  \
-    switch (ch2) {  \
-        case 1463: return 64302;  \
-        case 1464: return 64303;  \
-        case 1468: return 64304;  \
-    }  \
-    return 0;  \
-    case 1489:  \
-    switch (ch2) {  \
-        case 1468: return 64305;  \
-        case 1471: return 64332;  \
-    }  \
-    return 0;  \
-    case 1490:  \
-    switch (ch2) {  \
-        case 1468: return 64306;  \
-    }  \
-    return 0;  \
-    case 1491:  \
-    switch (ch2) {  \
-        case 1468: return 64307;  \
-    }  \
-    return 0;  \
-    case 1492:  \
-    switch (ch2) {  \
-        case 1468: return 64308;  \
-    }  \
-    return 0;  \
-    case 1493:  \
-    switch (ch2) {  \
-        case 1465: return 64331;  \
-        case 1468: return 64309;  \
-    }  \
-    return 0;  \
-    case 1494:  \
-    switch (ch2) {  \
-        case 1468: return 64310;  \
-    }  \
-    return 0;  \
-    case 1496:  \
-    switch (ch2) {  \
-        case 1468: return 64312;  \
-    }  \
-    return 0;  \
-    case 1497:  \
-    switch (ch2) {  \
-        case 1460: return 64285;  \
-        case 1468: return 64313;  \
-    }  \
-    return 0;  \
-    case 1498:  \
-    switch (ch2) {  \
-        case 1468: return 64314;  \
-    }  \
-    return 0;  \
-    case 1499:  \
-    switch (ch2) {  \
-        case 1468: return 64315;  \
-        case 1471: return 64333;  \
-    }  \
-    return 0;  \
-    case 1500:  \
-    switch (ch2) {  \
-        case 1468: return 64316;  \
-    }  \
-    return 0;  \
-    case 1502:  \
-    switch (ch2) {  \
-        case 1468: return 64318;  \
-    }  \
-    return 0;  \
-    case 1504:  \
-    switch (ch2) {  \
-        case 1468: return 64320;  \
-    }  \
-    return 0;  \
-    case 1505:  \
-    switch (ch2) {  \
-        case 1468: return 64321;  \
-    }  \
-    return 0;  \
-    case 1507:  \
-    switch (ch2) {  \
-        case 1468: return 64323;  \
-    }  \
-    return 0;  \
-    case 1508:  \
-    switch (ch2) {  \
-        case 1468: return 64324;  \
-        case 1471: return 64334;  \
-    }  \
-    return 0;  \
-    case 1510:  \
-    switch (ch2) {  \
-        case 1468: return 64326;  \
-    }  \
-    return 0;  \
-    case 1511:  \
-    switch (ch2) {  \
-        case 1468: return 64327;  \
-    }  \
-    return 0;  \
-    case 1512:  \
-    switch (ch2) {  \
-        case 1468: return 64328;  \
-    }  \
-    return 0;  \
-    case 1513:  \
-    switch (ch2) {  \
-        case 1468: return 64329;  \
-        case 1473: return 64298;  \
-        case 1474: return 64299;  \
-    }  \
-    return 0;  \
-    case 1514:  \
-    switch (ch2) {  \
-        case 1468: return 64330;  \
-    }  \
-    return 0;  \
-    case 1522:  \
-    switch (ch2) {  \
-        case 1463: return 64287;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 6:  \
-switch (ch1) {  \
-    case 1575:  \
-    switch (ch2) {  \
-        case 1619: return 1570;  \
-        case 1620: return 1571;  \
-        case 1621: return 1573;  \
-    }  \
-    return 0;  \
-    case 1608:  \
-    switch (ch2) {  \
-        case 1620: return 1572;  \
-    }  \
-    return 0;  \
-    case 1610:  \
-    switch (ch2) {  \
-        case 1620: return 1574;  \
-    }  \
-    return 0;  \
-    case 1729:  \
-    switch (ch2) {  \
-        case 1620: return 1730;  \
-    }  \
-    return 0;  \
-    case 1746:  \
-    switch (ch2) {  \
-        case 1620: return 1747;  \
-    }  \
-    return 0;  \
-    case 1749:  \
-    switch (ch2) {  \
-        case 1620: return 1728;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 9:  \
-switch (ch1) {  \
-    case 2325:  \
-    switch (ch2) {  \
-        case 2364: return 2392;  \
-    }  \
-    return 0;  \
-    case 2326:  \
-    switch (ch2) {  \
-        case 2364: return 2393;  \
-    }  \
-    return 0;  \
-    case 2327:  \
-    switch (ch2) {  \
-        case 2364: return 2394;  \
-    }  \
-    return 0;  \
-    case 2332:  \
-    switch (ch2) {  \
-        case 2364: return 2395;  \
-    }  \
-    return 0;  \
-    case 2337:  \
-    switch (ch2) {  \
-        case 2364: return 2396;  \
-    }  \
-    return 0;  \
-    case 2338:  \
-    switch (ch2) {  \
-        case 2364: return 2397;  \
-    }  \
-    return 0;  \
-    case 2344:  \
-    switch (ch2) {  \
-        case 2364: return 2345;  \
-    }  \
-    return 0;  \
-    case 2347:  \
-    switch (ch2) {  \
-        case 2364: return 2398;  \
-    }  \
-    return 0;  \
-    case 2351:  \
-    switch (ch2) {  \
-        case 2364: return 2399;  \
-    }  \
-    return 0;  \
-    case 2352:  \
-    switch (ch2) {  \
-        case 2364: return 2353;  \
-    }  \
-    return 0;  \
-    case 2355:  \
-    switch (ch2) {  \
-        case 2364: return 2356;  \
-    }  \
-    return 0;  \
-    case 2465:  \
-    switch (ch2) {  \
-        case 2492: return 2524;  \
-    }  \
-    return 0;  \
-    case 2466:  \
-    switch (ch2) {  \
-        case 2492: return 2525;  \
-    }  \
-    return 0;  \
-    case 2479:  \
-    switch (ch2) {  \
-        case 2492: return 2527;  \
-    }  \
-    return 0;  \
-    case 2503:  \
-    switch (ch2) {  \
-        case 2494: return 2507;  \
-        case 2519: return 2508;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 10:  \
-switch (ch1) {  \
-    case 2582:  \
-    switch (ch2) {  \
-        case 2620: return 2649;  \
-    }  \
-    return 0;  \
-    case 2583:  \
-    switch (ch2) {  \
-        case 2620: return 2650;  \
-    }  \
-    return 0;  \
-    case 2588:  \
-    switch (ch2) {  \
-        case 2620: return 2651;  \
-    }  \
-    return 0;  \
-    case 2603:  \
-    switch (ch2) {  \
-        case 2620: return 2654;  \
-    }  \
-    return 0;  \
-    case 2610:  \
-    switch (ch2) {  \
-        case 2620: return 2611;  \
-    }  \
-    return 0;  \
-    case 2616:  \
-    switch (ch2) {  \
-        case 2620: return 2614;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 11:  \
-switch (ch1) {  \
-    case 2849:  \
-    switch (ch2) {  \
-        case 2876: return 2908;  \
-    }  \
-    return 0;  \
-    case 2850:  \
-    switch (ch2) {  \
-        case 2876: return 2909;  \
-    }  \
-    return 0;  \
-    case 2887:  \
-    switch (ch2) {  \
-        case 2878: return 2891;  \
-        case 2902: return 2888;  \
-        case 2903: return 2892;  \
-    }  \
-    return 0;  \
-    case 2962:  \
-    switch (ch2) {  \
-        case 3031: return 2964;  \
-    }  \
-    return 0;  \
-    case 3014:  \
-    switch (ch2) {  \
-        case 3006: return 3018;  \
-        case 3031: return 3020;  \
-    }  \
-    return 0;  \
-    case 3015:  \
-    switch (ch2) {  \
-        case 3006: return 3019;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 12:  \
-switch (ch1) {  \
-    case 3142:  \
-    switch (ch2) {  \
-        case 3158: return 3144;  \
-    }  \
-    return 0;  \
-    case 3263:  \
-    switch (ch2) {  \
-        case 3285: return 3264;  \
-    }  \
-    return 0;  \
-    case 3270:  \
-    switch (ch2) {  \
-        case 3266: return 3274;  \
-        case 3285: return 3271;  \
-        case 3286: return 3272;  \
-    }  \
-    return 0;  \
-    case 3274:  \
-    switch (ch2) {  \
-        case 3285: return 3275;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 13:  \
-switch (ch1) {  \
-    case 3398:  \
-    switch (ch2) {  \
-        case 3390: return 3402;  \
-        case 3415: return 3404;  \
-    }  \
-    return 0;  \
-    case 3399:  \
-    switch (ch2) {  \
-        case 3390: return 3403;  \
-    }  \
-    return 0;  \
-    case 3545:  \
-    switch (ch2) {  \
-        case 3530: return 3546;  \
-        case 3535: return 3548;  \
-        case 3551: return 3550;  \
-    }  \
-    return 0;  \
-    case 3548:  \
-    switch (ch2) {  \
-        case 3530: return 3549;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 15:  \
-switch (ch1) {  \
-    case 3904:  \
-    switch (ch2) {  \
-        case 4021: return 3945;  \
-    }  \
-    return 0;  \
-    case 3906:  \
-    switch (ch2) {  \
-        case 4023: return 3907;  \
-    }  \
-    return 0;  \
-    case 3916:  \
-    switch (ch2) {  \
-        case 4023: return 3917;  \
-    }  \
-    return 0;  \
-    case 3921:  \
-    switch (ch2) {  \
-        case 4023: return 3922;  \
-    }  \
-    return 0;  \
-    case 3926:  \
-    switch (ch2) {  \
-        case 4023: return 3927;  \
-    }  \
-    return 0;  \
-    case 3931:  \
-    switch (ch2) {  \
-        case 4023: return 3932;  \
-    }  \
-    return 0;  \
-    case 3953:  \
-    switch (ch2) {  \
-        case 3954: return 3955;  \
-        case 3956: return 3957;  \
-        case 3968: return 3969;  \
-    }  \
-    return 0;  \
-    case 3984:  \
-    switch (ch2) {  \
-        case 4021: return 4025;  \
-    }  \
-    return 0;  \
-    case 3986:  \
-    switch (ch2) {  \
-        case 4023: return 3987;  \
-    }  \
-    return 0;  \
-    case 3996:  \
-    switch (ch2) {  \
-        case 4023: return 3997;  \
-    }  \
-    return 0;  \
-    case 4001:  \
-    switch (ch2) {  \
-        case 4023: return 4002;  \
-    }  \
-    return 0;  \
-    case 4006:  \
-    switch (ch2) {  \
-        case 4023: return 4007;  \
-    }  \
-    return 0;  \
-    case 4011:  \
-    switch (ch2) {  \
-        case 4023: return 4012;  \
-    }  \
-    return 0;  \
-    case 4018:  \
-    switch (ch2) {  \
-        case 3968: return 3958;  \
-    }  \
-    return 0;  \
-    case 4019:  \
-    switch (ch2) {  \
-        case 3968: return 3960;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 16:  \
-switch (ch1) {  \
-    case 4133:  \
-    switch (ch2) {  \
-        case 4142: return 4134;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 30:  \
-switch (ch1) {  \
-    case 7734:  \
-    switch (ch2) {  \
-        case 772: return 7736;  \
-    }  \
-    return 0;  \
-    case 7735:  \
-    switch (ch2) {  \
-        case 772: return 7737;  \
-    }  \
-    return 0;  \
-    case 7770:  \
-    switch (ch2) {  \
-        case 772: return 7772;  \
-    }  \
-    return 0;  \
-    case 7771:  \
-    switch (ch2) {  \
-        case 772: return 7773;  \
-    }  \
-    return 0;  \
-    case 7778:  \
-    switch (ch2) {  \
-        case 775: return 7784;  \
-    }  \
-    return 0;  \
-    case 7779:  \
-    switch (ch2) {  \
-        case 775: return 7785;  \
-    }  \
-    return 0;  \
-    case 7840:  \
-    switch (ch2) {  \
-        case 770: return 7852;  \
-        case 774: return 7862;  \
-    }  \
-    return 0;  \
-    case 7841:  \
-    switch (ch2) {  \
-        case 770: return 7853;  \
-        case 774: return 7863;  \
-    }  \
-    return 0;  \
-    case 7864:  \
-    switch (ch2) {  \
-        case 770: return 7878;  \
-    }  \
-    return 0;  \
-    case 7865:  \
-    switch (ch2) {  \
-        case 770: return 7879;  \
-    }  \
-    return 0;  \
-    case 7884:  \
-    switch (ch2) {  \
-        case 770: return 7896;  \
-    }  \
-    return 0;  \
-    case 7885:  \
-    switch (ch2) {  \
-        case 770: return 7897;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 31:  \
-switch (ch1) {  \
-    case 7936:  \
-    switch (ch2) {  \
-        case 768: return 7938;  \
-        case 769: return 7940;  \
-        case 834: return 7942;  \
-        case 837: return 8064;  \
-    }  \
-    return 0;  \
-    case 7937:  \
-    switch (ch2) {  \
-        case 768: return 7939;  \
-        case 769: return 7941;  \
-        case 834: return 7943;  \
-        case 837: return 8065;  \
-    }  \
-    return 0;  \
-    case 7938:  \
-    switch (ch2) {  \
-        case 837: return 8066;  \
-    }  \
-    return 0;  \
-    case 7939:  \
-    switch (ch2) {  \
-        case 837: return 8067;  \
-    }  \
-    return 0;  \
-    case 7940:  \
-    switch (ch2) {  \
-        case 837: return 8068;  \
-    }  \
-    return 0;  \
-    case 7941:  \
-    switch (ch2) {  \
-        case 837: return 8069;  \
-    }  \
-    return 0;  \
-    case 7942:  \
-    switch (ch2) {  \
-        case 837: return 8070;  \
-    }  \
-    return 0;  \
-    case 7943:  \
-    switch (ch2) {  \
-        case 837: return 8071;  \
-    }  \
-    return 0;  \
-    case 7944:  \
-    switch (ch2) {  \
-        case 768: return 7946;  \
-        case 769: return 7948;  \
-        case 834: return 7950;  \
-        case 837: return 8072;  \
-    }  \
-    return 0;  \
-    case 7945:  \
-    switch (ch2) {  \
-        case 768: return 7947;  \
-        case 769: return 7949;  \
-        case 834: return 7951;  \
-        case 837: return 8073;  \
-    }  \
-    return 0;  \
-    case 7946:  \
-    switch (ch2) {  \
-        case 837: return 8074;  \
-    }  \
-    return 0;  \
-    case 7947:  \
-    switch (ch2) {  \
-        case 837: return 8075;  \
-    }  \
-    return 0;  \
-    case 7948:  \
-    switch (ch2) {  \
-        case 837: return 8076;  \
-    }  \
-    return 0;  \
-    case 7949:  \
-    switch (ch2) {  \
-        case 837: return 8077;  \
-    }  \
-    return 0;  \
-    case 7950:  \
-    switch (ch2) {  \
-        case 837: return 8078;  \
-    }  \
-    return 0;  \
-    case 7951:  \
-    switch (ch2) {  \
-        case 837: return 8079;  \
-    }  \
-    return 0;  \
-    case 7952:  \
-    switch (ch2) {  \
-        case 768: return 7954;  \
-        case 769: return 7956;  \
-    }  \
-    return 0;  \
-    case 7953:  \
-    switch (ch2) {  \
-        case 768: return 7955;  \
-        case 769: return 7957;  \
-    }  \
-    return 0;  \
-    case 7960:  \
-    switch (ch2) {  \
-        case 768: return 7962;  \
-        case 769: return 7964;  \
-    }  \
-    return 0;  \
-    case 7961:  \
-    switch (ch2) {  \
-        case 768: return 7963;  \
-        case 769: return 7965;  \
-    }  \
-    return 0;  \
-    case 7968:  \
-    switch (ch2) {  \
-        case 768: return 7970;  \
-        case 769: return 7972;  \
-        case 834: return 7974;  \
-        case 837: return 8080;  \
-    }  \
-    return 0;  \
-    case 7969:  \
-    switch (ch2) {  \
-        case 768: return 7971;  \
-        case 769: return 7973;  \
-        case 834: return 7975;  \
-        case 837: return 8081;  \
-    }  \
-    return 0;  \
-    case 7970:  \
-    switch (ch2) {  \
-        case 837: return 8082;  \
-    }  \
-    return 0;  \
-    case 7971:  \
-    switch (ch2) {  \
-        case 837: return 8083;  \
-    }  \
-    return 0;  \
-    case 7972:  \
-    switch (ch2) {  \
-        case 837: return 8084;  \
-    }  \
-    return 0;  \
-    case 7973:  \
-    switch (ch2) {  \
-        case 837: return 8085;  \
-    }  \
-    return 0;  \
-    case 7974:  \
-    switch (ch2) {  \
-        case 837: return 8086;  \
-    }  \
-    return 0;  \
-    case 7975:  \
-    switch (ch2) {  \
-        case 837: return 8087;  \
-    }  \
-    return 0;  \
-    case 7976:  \
-    switch (ch2) {  \
-        case 768: return 7978;  \
-        case 769: return 7980;  \
-        case 834: return 7982;  \
-        case 837: return 8088;  \
-    }  \
-    return 0;  \
-    case 7977:  \
-    switch (ch2) {  \
-        case 768: return 7979;  \
-        case 769: return 7981;  \
-        case 834: return 7983;  \
-        case 837: return 8089;  \
-    }  \
-    return 0;  \
-    case 7978:  \
-    switch (ch2) {  \
-        case 837: return 8090;  \
-    }  \
-    return 0;  \
-    case 7979:  \
-    switch (ch2) {  \
-        case 837: return 8091;  \
-    }  \
-    return 0;  \
-    case 7980:  \
-    switch (ch2) {  \
-        case 837: return 8092;  \
-    }  \
-    return 0;  \
-    case 7981:  \
-    switch (ch2) {  \
-        case 837: return 8093;  \
-    }  \
-    return 0;  \
-    case 7982:  \
-    switch (ch2) {  \
-        case 837: return 8094;  \
-    }  \
-    return 0;  \
-    case 7983:  \
-    switch (ch2) {  \
-        case 837: return 8095;  \
-    }  \
-    return 0;  \
-    case 7984:  \
-    switch (ch2) {  \
-        case 768: return 7986;  \
-        case 769: return 7988;  \
-        case 834: return 7990;  \
-    }  \
-    return 0;  \
-    case 7985:  \
-    switch (ch2) {  \
-        case 768: return 7987;  \
-        case 769: return 7989;  \
-        case 834: return 7991;  \
-    }  \
-    return 0;  \
-    case 7992:  \
-    switch (ch2) {  \
-        case 768: return 7994;  \
-        case 769: return 7996;  \
-        case 834: return 7998;  \
-    }  \
-    return 0;  \
-    case 7993:  \
-    switch (ch2) {  \
-        case 768: return 7995;  \
-        case 769: return 7997;  \
-        case 834: return 7999;  \
-    }  \
-    return 0;  \
-    case 8000:  \
-    switch (ch2) {  \
-        case 768: return 8002;  \
-        case 769: return 8004;  \
-    }  \
-    return 0;  \
-    case 8001:  \
-    switch (ch2) {  \
-        case 768: return 8003;  \
-        case 769: return 8005;  \
-    }  \
-    return 0;  \
-    case 8008:  \
-    switch (ch2) {  \
-        case 768: return 8010;  \
-        case 769: return 8012;  \
-    }  \
-    return 0;  \
-    case 8009:  \
-    switch (ch2) {  \
-        case 768: return 8011;  \
-        case 769: return 8013;  \
-    }  \
-    return 0;  \
-    case 8016:  \
-    switch (ch2) {  \
-        case 768: return 8018;  \
-        case 769: return 8020;  \
-        case 834: return 8022;  \
-    }  \
-    return 0;  \
-    case 8017:  \
-    switch (ch2) {  \
-        case 768: return 8019;  \
-        case 769: return 8021;  \
-        case 834: return 8023;  \
-    }  \
-    return 0;  \
-    case 8025:  \
-    switch (ch2) {  \
-        case 768: return 8027;  \
-        case 769: return 8029;  \
-        case 834: return 8031;  \
-    }  \
-    return 0;  \
-    case 8032:  \
-    switch (ch2) {  \
-        case 768: return 8034;  \
-        case 769: return 8036;  \
-        case 834: return 8038;  \
-        case 837: return 8096;  \
-    }  \
-    return 0;  \
-    case 8033:  \
-    switch (ch2) {  \
-        case 768: return 8035;  \
-        case 769: return 8037;  \
-        case 834: return 8039;  \
-        case 837: return 8097;  \
-    }  \
-    return 0;  \
-    case 8034:  \
-    switch (ch2) {  \
-        case 837: return 8098;  \
-    }  \
-    return 0;  \
-    case 8035:  \
-    switch (ch2) {  \
-        case 837: return 8099;  \
-    }  \
-    return 0;  \
-    case 8036:  \
-    switch (ch2) {  \
-        case 837: return 8100;  \
-    }  \
-    return 0;  \
-    case 8037:  \
-    switch (ch2) {  \
-        case 837: return 8101;  \
-    }  \
-    return 0;  \
-    case 8038:  \
-    switch (ch2) {  \
-        case 837: return 8102;  \
-    }  \
-    return 0;  \
-    case 8039:  \
-    switch (ch2) {  \
-        case 837: return 8103;  \
-    }  \
-    return 0;  \
-    case 8040:  \
-    switch (ch2) {  \
-        case 768: return 8042;  \
-        case 769: return 8044;  \
-        case 834: return 8046;  \
-        case 837: return 8104;  \
-    }  \
-    return 0;  \
-    case 8041:  \
-    switch (ch2) {  \
-        case 768: return 8043;  \
-        case 769: return 8045;  \
-        case 834: return 8047;  \
-        case 837: return 8105;  \
-    }  \
-    return 0;  \
-    case 8042:  \
-    switch (ch2) {  \
-        case 837: return 8106;  \
-    }  \
-    return 0;  \
-    case 8043:  \
-    switch (ch2) {  \
-        case 837: return 8107;  \
-    }  \
-    return 0;  \
-    case 8044:  \
-    switch (ch2) {  \
-        case 837: return 8108;  \
-    }  \
-    return 0;  \
-    case 8045:  \
-    switch (ch2) {  \
-        case 837: return 8109;  \
-    }  \
-    return 0;  \
-    case 8046:  \
-    switch (ch2) {  \
-        case 837: return 8110;  \
-    }  \
-    return 0;  \
-    case 8047:  \
-    switch (ch2) {  \
-        case 837: return 8111;  \
-    }  \
-    return 0;  \
-    case 8048:  \
-    switch (ch2) {  \
-        case 837: return 8114;  \
-    }  \
-    return 0;  \
-    case 8052:  \
-    switch (ch2) {  \
-        case 837: return 8130;  \
-    }  \
-    return 0;  \
-    case 8060:  \
-    switch (ch2) {  \
-        case 837: return 8178;  \
-    }  \
-    return 0;  \
-    case 8118:  \
-    switch (ch2) {  \
-        case 837: return 8119;  \
-    }  \
-    return 0;  \
-    case 8127:  \
-    switch (ch2) {  \
-        case 768: return 8141;  \
-        case 769: return 8142;  \
-        case 834: return 8143;  \
-    }  \
-    return 0;  \
-    case 8134:  \
-    switch (ch2) {  \
-        case 837: return 8135;  \
-    }  \
-    return 0;  \
-    case 8182:  \
-    switch (ch2) {  \
-        case 837: return 8183;  \
-    }  \
-    return 0;  \
-    case 8190:  \
-    switch (ch2) {  \
-        case 768: return 8157;  \
-        case 769: return 8158;  \
-        case 834: return 8159;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 33:  \
-switch (ch1) {  \
-    case 8592:  \
-    switch (ch2) {  \
-        case 824: return 8602;  \
-    }  \
-    return 0;  \
-    case 8594:  \
-    switch (ch2) {  \
-        case 824: return 8603;  \
-    }  \
-    return 0;  \
-    case 8596:  \
-    switch (ch2) {  \
-        case 824: return 8622;  \
-    }  \
-    return 0;  \
-    case 8656:  \
-    switch (ch2) {  \
-        case 824: return 8653;  \
-    }  \
-    return 0;  \
-    case 8658:  \
-    switch (ch2) {  \
-        case 824: return 8655;  \
-    }  \
-    return 0;  \
-    case 8660:  \
-    switch (ch2) {  \
-        case 824: return 8654;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 34:  \
-switch (ch1) {  \
-    case 8707:  \
-    switch (ch2) {  \
-        case 824: return 8708;  \
-    }  \
-    return 0;  \
-    case 8712:  \
-    switch (ch2) {  \
-        case 824: return 8713;  \
-    }  \
-    return 0;  \
-    case 8715:  \
-    switch (ch2) {  \
-        case 824: return 8716;  \
-    }  \
-    return 0;  \
-    case 8739:  \
-    switch (ch2) {  \
-        case 824: return 8740;  \
-    }  \
-    return 0;  \
-    case 8741:  \
-    switch (ch2) {  \
-        case 824: return 8742;  \
-    }  \
-    return 0;  \
-    case 8764:  \
-    switch (ch2) {  \
-        case 824: return 8769;  \
-    }  \
-    return 0;  \
-    case 8771:  \
-    switch (ch2) {  \
-        case 824: return 8772;  \
-    }  \
-    return 0;  \
-    case 8773:  \
-    switch (ch2) {  \
-        case 824: return 8775;  \
-    }  \
-    return 0;  \
-    case 8776:  \
-    switch (ch2) {  \
-        case 824: return 8777;  \
-    }  \
-    return 0;  \
-    case 8781:  \
-    switch (ch2) {  \
-        case 824: return 8813;  \
-    }  \
-    return 0;  \
-    case 8801:  \
-    switch (ch2) {  \
-        case 824: return 8802;  \
-    }  \
-    return 0;  \
-    case 8804:  \
-    switch (ch2) {  \
-        case 824: return 8816;  \
-    }  \
-    return 0;  \
-    case 8805:  \
-    switch (ch2) {  \
-        case 824: return 8817;  \
-    }  \
-    return 0;  \
-    case 8818:  \
-    switch (ch2) {  \
-        case 824: return 8820;  \
-    }  \
-    return 0;  \
-    case 8819:  \
-    switch (ch2) {  \
-        case 824: return 8821;  \
-    }  \
-    return 0;  \
-    case 8822:  \
-    switch (ch2) {  \
-        case 824: return 8824;  \
-    }  \
-    return 0;  \
-    case 8823:  \
-    switch (ch2) {  \
-        case 824: return 8825;  \
-    }  \
-    return 0;  \
-    case 8826:  \
-    switch (ch2) {  \
-        case 824: return 8832;  \
-    }  \
-    return 0;  \
-    case 8827:  \
-    switch (ch2) {  \
-        case 824: return 8833;  \
-    }  \
-    return 0;  \
-    case 8828:  \
-    switch (ch2) {  \
-        case 824: return 8928;  \
-    }  \
-    return 0;  \
-    case 8829:  \
-    switch (ch2) {  \
-        case 824: return 8929;  \
-    }  \
-    return 0;  \
-    case 8834:  \
-    switch (ch2) {  \
-        case 824: return 8836;  \
-    }  \
-    return 0;  \
-    case 8835:  \
-    switch (ch2) {  \
-        case 824: return 8837;  \
-    }  \
-    return 0;  \
-    case 8838:  \
-    switch (ch2) {  \
-        case 824: return 8840;  \
-    }  \
-    return 0;  \
-    case 8839:  \
-    switch (ch2) {  \
-        case 824: return 8841;  \
-    }  \
-    return 0;  \
-    case 8849:  \
-    switch (ch2) {  \
-        case 824: return 8930;  \
-    }  \
-    return 0;  \
-    case 8850:  \
-    switch (ch2) {  \
-        case 824: return 8931;  \
-    }  \
-    return 0;  \
-    case 8866:  \
-    switch (ch2) {  \
-        case 824: return 8876;  \
-    }  \
-    return 0;  \
-    case 8872:  \
-    switch (ch2) {  \
-        case 824: return 8877;  \
-    }  \
-    return 0;  \
-    case 8873:  \
-    switch (ch2) {  \
-        case 824: return 8878;  \
-    }  \
-    return 0;  \
-    case 8875:  \
-    switch (ch2) {  \
-        case 824: return 8879;  \
-    }  \
-    return 0;  \
-    case 8882:  \
-    switch (ch2) {  \
-        case 824: return 8938;  \
-    }  \
-    return 0;  \
-    case 8883:  \
-    switch (ch2) {  \
-        case 824: return 8939;  \
-    }  \
-    return 0;  \
-    case 8884:  \
-    switch (ch2) {  \
-        case 824: return 8940;  \
-    }  \
-    return 0;  \
-    case 8885:  \
-    switch (ch2) {  \
-        case 824: return 8941;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 42:  \
-switch (ch1) {  \
-    case 10973:  \
-    switch (ch2) {  \
-        case 824: return 10972;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 48:  \
-switch (ch1) {  \
-    case 12358:  \
-    switch (ch2) {  \
-        case 12441: return 12436;  \
-    }  \
-    return 0;  \
-    case 12363:  \
-    switch (ch2) {  \
-        case 12441: return 12364;  \
-    }  \
-    return 0;  \
-    case 12365:  \
-    switch (ch2) {  \
-        case 12441: return 12366;  \
-    }  \
-    return 0;  \
-    case 12367:  \
-    switch (ch2) {  \
-        case 12441: return 12368;  \
-    }  \
-    return 0;  \
-    case 12369:  \
-    switch (ch2) {  \
-        case 12441: return 12370;  \
-    }  \
-    return 0;  \
-    case 12371:  \
-    switch (ch2) {  \
-        case 12441: return 12372;  \
-    }  \
-    return 0;  \
-    case 12373:  \
-    switch (ch2) {  \
-        case 12441: return 12374;  \
-    }  \
-    return 0;  \
-    case 12375:  \
-    switch (ch2) {  \
-        case 12441: return 12376;  \
-    }  \
-    return 0;  \
-    case 12377:  \
-    switch (ch2) {  \
-        case 12441: return 12378;  \
-    }  \
-    return 0;  \
-    case 12379:  \
-    switch (ch2) {  \
-        case 12441: return 12380;  \
-    }  \
-    return 0;  \
-    case 12381:  \
-    switch (ch2) {  \
-        case 12441: return 12382;  \
-    }  \
-    return 0;  \
-    case 12383:  \
-    switch (ch2) {  \
-        case 12441: return 12384;  \
-    }  \
-    return 0;  \
-    case 12385:  \
-    switch (ch2) {  \
-        case 12441: return 12386;  \
-    }  \
-    return 0;  \
-    case 12388:  \
-    switch (ch2) {  \
-        case 12441: return 12389;  \
-    }  \
-    return 0;  \
-    case 12390:  \
-    switch (ch2) {  \
-        case 12441: return 12391;  \
-    }  \
-    return 0;  \
-    case 12392:  \
-    switch (ch2) {  \
-        case 12441: return 12393;  \
-    }  \
-    return 0;  \
-    case 12399:  \
-    switch (ch2) {  \
-        case 12441: return 12400;  \
-        case 12442: return 12401;  \
-    }  \
-    return 0;  \
-    case 12402:  \
-    switch (ch2) {  \
-        case 12441: return 12403;  \
-        case 12442: return 12404;  \
-    }  \
-    return 0;  \
-    case 12405:  \
-    switch (ch2) {  \
-        case 12441: return 12406;  \
-        case 12442: return 12407;  \
-    }  \
-    return 0;  \
-    case 12408:  \
-    switch (ch2) {  \
-        case 12441: return 12409;  \
-        case 12442: return 12410;  \
-    }  \
-    return 0;  \
-    case 12411:  \
-    switch (ch2) {  \
-        case 12441: return 12412;  \
-        case 12442: return 12413;  \
-    }  \
-    return 0;  \
-    case 12445:  \
-    switch (ch2) {  \
-        case 12441: return 12446;  \
-    }  \
-    return 0;  \
-    case 12454:  \
-    switch (ch2) {  \
-        case 12441: return 12532;  \
-    }  \
-    return 0;  \
-    case 12459:  \
-    switch (ch2) {  \
-        case 12441: return 12460;  \
-    }  \
-    return 0;  \
-    case 12461:  \
-    switch (ch2) {  \
-        case 12441: return 12462;  \
-    }  \
-    return 0;  \
-    case 12463:  \
-    switch (ch2) {  \
-        case 12441: return 12464;  \
-    }  \
-    return 0;  \
-    case 12465:  \
-    switch (ch2) {  \
-        case 12441: return 12466;  \
-    }  \
-    return 0;  \
-    case 12467:  \
-    switch (ch2) {  \
-        case 12441: return 12468;  \
-    }  \
-    return 0;  \
-    case 12469:  \
-    switch (ch2) {  \
-        case 12441: return 12470;  \
-    }  \
-    return 0;  \
-    case 12471:  \
-    switch (ch2) {  \
-        case 12441: return 12472;  \
-    }  \
-    return 0;  \
-    case 12473:  \
-    switch (ch2) {  \
-        case 12441: return 12474;  \
-    }  \
-    return 0;  \
-    case 12475:  \
-    switch (ch2) {  \
-        case 12441: return 12476;  \
-    }  \
-    return 0;  \
-    case 12477:  \
-    switch (ch2) {  \
-        case 12441: return 12478;  \
-    }  \
-    return 0;  \
-    case 12479:  \
-    switch (ch2) {  \
-        case 12441: return 12480;  \
-    }  \
-    return 0;  \
-    case 12481:  \
-    switch (ch2) {  \
-        case 12441: return 12482;  \
-    }  \
-    return 0;  \
-    case 12484:  \
-    switch (ch2) {  \
-        case 12441: return 12485;  \
-    }  \
-    return 0;  \
-    case 12486:  \
-    switch (ch2) {  \
-        case 12441: return 12487;  \
-    }  \
-    return 0;  \
-    case 12488:  \
-    switch (ch2) {  \
-        case 12441: return 12489;  \
-    }  \
-    return 0;  \
-    case 12495:  \
-    switch (ch2) {  \
-        case 12441: return 12496;  \
-        case 12442: return 12497;  \
-    }  \
-    return 0;  \
-    case 12498:  \
-    switch (ch2) {  \
-        case 12441: return 12499;  \
-        case 12442: return 12500;  \
-    }  \
-    return 0;  \
-    case 12501:  \
-    switch (ch2) {  \
-        case 12441: return 12502;  \
-        case 12442: return 12503;  \
-    }  \
-    return 0;  \
-    case 12504:  \
-    switch (ch2) {  \
-        case 12441: return 12505;  \
-        case 12442: return 12506;  \
-    }  \
-    return 0;  \
-    case 12507:  \
-    switch (ch2) {  \
-        case 12441: return 12508;  \
-        case 12442: return 12509;  \
-    }  \
-    return 0;  \
-    case 12527:  \
-    switch (ch2) {  \
-        case 12441: return 12535;  \
-    }  \
-    return 0;  \
-    case 12528:  \
-    switch (ch2) {  \
-        case 12441: return 12536;  \
-    }  \
-    return 0;  \
-    case 12529:  \
-    switch (ch2) {  \
-        case 12441: return 12537;  \
-    }  \
-    return 0;  \
-    case 12530:  \
-    switch (ch2) {  \
-        case 12441: return 12538;  \
-    }  \
-    return 0;  \
-    case 12541:  \
-    switch (ch2) {  \
-        case 12441: return 12542;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 251:  \
-switch (ch1) {  \
-    case 64329:  \
-    switch (ch2) {  \
-        case 1473: return 64300;  \
-        case 1474: return 64301;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-case 465:  \
-switch (ch1) {  \
-    case 119127:  \
-    switch (ch2) {  \
-        case 119141: return 119134;  \
-    }  \
-    return 0;  \
-    case 119128:  \
-    switch (ch2) {  \
-        case 119141: return 119135;  \
-    }  \
-    return 0;  \
-    case 119135:  \
-    switch (ch2) {  \
-        case 119150: return 119136;  \
-        case 119151: return 119137;  \
-        case 119152: return 119138;  \
-        case 119153: return 119139;  \
-        case 119154: return 119140;  \
-    }  \
-    return 0;  \
-    case 119225:  \
-    switch (ch2) {  \
-        case 119141: return 119227;  \
-    }  \
-    return 0;  \
-    case 119226:  \
-    switch (ch2) {  \
-        case 119141: return 119228;  \
-    }  \
-    return 0;  \
-    case 119227:  \
-    switch (ch2) {  \
-        case 119150: return 119229;  \
-        case 119151: return 119231;  \
-    }  \
-    return 0;  \
-    case 119228:  \
-    switch (ch2) {  \
-        case 119150: return 119230;  \
-        case 119151: return 119232;  \
-    }  \
-    return 0;  \
-}  \
-return 0;  \
-}  \
-return 0;
+	switch ((glui32)(ch1) >> 8) {  \
+	case 0:  \
+		switch (ch1) {  \
+		case 60:  \
+			switch (ch2) {  \
+			case 824: return 8814;  \
+			}  \
+			return 0;  \
+		case 61:  \
+			switch (ch2) {  \
+			case 824: return 8800;  \
+			}  \
+			return 0;  \
+		case 62:  \
+			switch (ch2) {  \
+			case 824: return 8815;  \
+			}  \
+			return 0;  \
+		case 65:  \
+			switch (ch2) {  \
+			case 768: return 192;  \
+			case 769: return 193;  \
+			case 770: return 194;  \
+			case 771: return 195;  \
+			case 772: return 256;  \
+			case 774: return 258;  \
+			case 775: return 550;  \
+			case 776: return 196;  \
+			case 777: return 7842;  \
+			case 778: return 197;  \
+			case 780: return 461;  \
+			case 783: return 512;  \
+			case 785: return 514;  \
+			case 803: return 7840;  \
+			case 805: return 7680;  \
+			case 808: return 260;  \
+			}  \
+			return 0;  \
+		case 66:  \
+			switch (ch2) {  \
+			case 775: return 7682;  \
+			case 803: return 7684;  \
+			case 817: return 7686;  \
+			}  \
+			return 0;  \
+		case 67:  \
+			switch (ch2) {  \
+			case 769: return 262;  \
+			case 770: return 264;  \
+			case 775: return 266;  \
+			case 780: return 268;  \
+			case 807: return 199;  \
+			}  \
+			return 0;  \
+		case 68:  \
+			switch (ch2) {  \
+			case 775: return 7690;  \
+			case 780: return 270;  \
+			case 803: return 7692;  \
+			case 807: return 7696;  \
+			case 813: return 7698;  \
+			case 817: return 7694;  \
+			}  \
+			return 0;  \
+		case 69:  \
+			switch (ch2) {  \
+			case 768: return 200;  \
+			case 769: return 201;  \
+			case 770: return 202;  \
+			case 771: return 7868;  \
+			case 772: return 274;  \
+			case 774: return 276;  \
+			case 775: return 278;  \
+			case 776: return 203;  \
+			case 777: return 7866;  \
+			case 780: return 282;  \
+			case 783: return 516;  \
+			case 785: return 518;  \
+			case 803: return 7864;  \
+			case 807: return 552;  \
+			case 808: return 280;  \
+			case 813: return 7704;  \
+			case 816: return 7706;  \
+			}  \
+			return 0;  \
+		case 70:  \
+			switch (ch2) {  \
+			case 775: return 7710;  \
+			}  \
+			return 0;  \
+		case 71:  \
+			switch (ch2) {  \
+			case 769: return 500;  \
+			case 770: return 284;  \
+			case 772: return 7712;  \
+			case 774: return 286;  \
+			case 775: return 288;  \
+			case 780: return 486;  \
+			case 807: return 290;  \
+			}  \
+			return 0;  \
+		case 72:  \
+			switch (ch2) {  \
+			case 770: return 292;  \
+			case 775: return 7714;  \
+			case 776: return 7718;  \
+			case 780: return 542;  \
+			case 803: return 7716;  \
+			case 807: return 7720;  \
+			case 814: return 7722;  \
+			}  \
+			return 0;  \
+		case 73:  \
+			switch (ch2) {  \
+			case 768: return 204;  \
+			case 769: return 205;  \
+			case 770: return 206;  \
+			case 771: return 296;  \
+			case 772: return 298;  \
+			case 774: return 300;  \
+			case 775: return 304;  \
+			case 776: return 207;  \
+			case 777: return 7880;  \
+			case 780: return 463;  \
+			case 783: return 520;  \
+			case 785: return 522;  \
+			case 803: return 7882;  \
+			case 808: return 302;  \
+			case 816: return 7724;  \
+			}  \
+			return 0;  \
+		case 74:  \
+			switch (ch2) {  \
+			case 770: return 308;  \
+			}  \
+			return 0;  \
+		case 75:  \
+			switch (ch2) {  \
+			case 769: return 7728;  \
+			case 780: return 488;  \
+			case 803: return 7730;  \
+			case 807: return 310;  \
+			case 817: return 7732;  \
+			}  \
+			return 0;  \
+		case 76:  \
+			switch (ch2) {  \
+			case 769: return 313;  \
+			case 780: return 317;  \
+			case 803: return 7734;  \
+			case 807: return 315;  \
+			case 813: return 7740;  \
+			case 817: return 7738;  \
+			}  \
+			return 0;  \
+		case 77:  \
+			switch (ch2) {  \
+			case 769: return 7742;  \
+			case 775: return 7744;  \
+			case 803: return 7746;  \
+			}  \
+			return 0;  \
+		case 78:  \
+			switch (ch2) {  \
+			case 768: return 504;  \
+			case 769: return 323;  \
+			case 771: return 209;  \
+			case 775: return 7748;  \
+			case 780: return 327;  \
+			case 803: return 7750;  \
+			case 807: return 325;  \
+			case 813: return 7754;  \
+			case 817: return 7752;  \
+			}  \
+			return 0;  \
+		case 79:  \
+			switch (ch2) {  \
+			case 768: return 210;  \
+			case 769: return 211;  \
+			case 770: return 212;  \
+			case 771: return 213;  \
+			case 772: return 332;  \
+			case 774: return 334;  \
+			case 775: return 558;  \
+			case 776: return 214;  \
+			case 777: return 7886;  \
+			case 779: return 336;  \
+			case 780: return 465;  \
+			case 783: return 524;  \
+			case 785: return 526;  \
+			case 795: return 416;  \
+			case 803: return 7884;  \
+			case 808: return 490;  \
+			}  \
+			return 0;  \
+		case 80:  \
+			switch (ch2) {  \
+			case 769: return 7764;  \
+			case 775: return 7766;  \
+			}  \
+			return 0;  \
+		case 82:  \
+			switch (ch2) {  \
+			case 769: return 340;  \
+			case 775: return 7768;  \
+			case 780: return 344;  \
+			case 783: return 528;  \
+			case 785: return 530;  \
+			case 803: return 7770;  \
+			case 807: return 342;  \
+			case 817: return 7774;  \
+			}  \
+			return 0;  \
+		case 83:  \
+			switch (ch2) {  \
+			case 769: return 346;  \
+			case 770: return 348;  \
+			case 775: return 7776;  \
+			case 780: return 352;  \
+			case 803: return 7778;  \
+			case 806: return 536;  \
+			case 807: return 350;  \
+			}  \
+			return 0;  \
+		case 84:  \
+			switch (ch2) {  \
+			case 775: return 7786;  \
+			case 780: return 356;  \
+			case 803: return 7788;  \
+			case 806: return 538;  \
+			case 807: return 354;  \
+			case 813: return 7792;  \
+			case 817: return 7790;  \
+			}  \
+			return 0;  \
+		case 85:  \
+			switch (ch2) {  \
+			case 768: return 217;  \
+			case 769: return 218;  \
+			case 770: return 219;  \
+			case 771: return 360;  \
+			case 772: return 362;  \
+			case 774: return 364;  \
+			case 776: return 220;  \
+			case 777: return 7910;  \
+			case 778: return 366;  \
+			case 779: return 368;  \
+			case 780: return 467;  \
+			case 783: return 532;  \
+			case 785: return 534;  \
+			case 795: return 431;  \
+			case 803: return 7908;  \
+			case 804: return 7794;  \
+			case 808: return 370;  \
+			case 813: return 7798;  \
+			case 816: return 7796;  \
+			}  \
+			return 0;  \
+		case 86:  \
+			switch (ch2) {  \
+			case 771: return 7804;  \
+			case 803: return 7806;  \
+			}  \
+			return 0;  \
+		case 87:  \
+			switch (ch2) {  \
+			case 768: return 7808;  \
+			case 769: return 7810;  \
+			case 770: return 372;  \
+			case 775: return 7814;  \
+			case 776: return 7812;  \
+			case 803: return 7816;  \
+			}  \
+			return 0;  \
+		case 88:  \
+			switch (ch2) {  \
+			case 775: return 7818;  \
+			case 776: return 7820;  \
+			}  \
+			return 0;  \
+		case 89:  \
+			switch (ch2) {  \
+			case 768: return 7922;  \
+			case 769: return 221;  \
+			case 770: return 374;  \
+			case 771: return 7928;  \
+			case 772: return 562;  \
+			case 775: return 7822;  \
+			case 776: return 376;  \
+			case 777: return 7926;  \
+			case 803: return 7924;  \
+			}  \
+			return 0;  \
+		case 90:  \
+			switch (ch2) {  \
+			case 769: return 377;  \
+			case 770: return 7824;  \
+			case 775: return 379;  \
+			case 780: return 381;  \
+			case 803: return 7826;  \
+			case 817: return 7828;  \
+			}  \
+			return 0;  \
+		case 97:  \
+			switch (ch2) {  \
+			case 768: return 224;  \
+			case 769: return 225;  \
+			case 770: return 226;  \
+			case 771: return 227;  \
+			case 772: return 257;  \
+			case 774: return 259;  \
+			case 775: return 551;  \
+			case 776: return 228;  \
+			case 777: return 7843;  \
+			case 778: return 229;  \
+			case 780: return 462;  \
+			case 783: return 513;  \
+			case 785: return 515;  \
+			case 803: return 7841;  \
+			case 805: return 7681;  \
+			case 808: return 261;  \
+			}  \
+			return 0;  \
+		case 98:  \
+			switch (ch2) {  \
+			case 775: return 7683;  \
+			case 803: return 7685;  \
+			case 817: return 7687;  \
+			}  \
+			return 0;  \
+		case 99:  \
+			switch (ch2) {  \
+			case 769: return 263;  \
+			case 770: return 265;  \
+			case 775: return 267;  \
+			case 780: return 269;  \
+			case 807: return 231;  \
+			}  \
+			return 0;  \
+		case 100:  \
+			switch (ch2) {  \
+			case 775: return 7691;  \
+			case 780: return 271;  \
+			case 803: return 7693;  \
+			case 807: return 7697;  \
+			case 813: return 7699;  \
+			case 817: return 7695;  \
+			}  \
+			return 0;  \
+		case 101:  \
+			switch (ch2) {  \
+			case 768: return 232;  \
+			case 769: return 233;  \
+			case 770: return 234;  \
+			case 771: return 7869;  \
+			case 772: return 275;  \
+			case 774: return 277;  \
+			case 775: return 279;  \
+			case 776: return 235;  \
+			case 777: return 7867;  \
+			case 780: return 283;  \
+			case 783: return 517;  \
+			case 785: return 519;  \
+			case 803: return 7865;  \
+			case 807: return 553;  \
+			case 808: return 281;  \
+			case 813: return 7705;  \
+			case 816: return 7707;  \
+			}  \
+			return 0;  \
+		case 102:  \
+			switch (ch2) {  \
+			case 775: return 7711;  \
+			}  \
+			return 0;  \
+		case 103:  \
+			switch (ch2) {  \
+			case 769: return 501;  \
+			case 770: return 285;  \
+			case 772: return 7713;  \
+			case 774: return 287;  \
+			case 775: return 289;  \
+			case 780: return 487;  \
+			case 807: return 291;  \
+			}  \
+			return 0;  \
+		case 104:  \
+			switch (ch2) {  \
+			case 770: return 293;  \
+			case 775: return 7715;  \
+			case 776: return 7719;  \
+			case 780: return 543;  \
+			case 803: return 7717;  \
+			case 807: return 7721;  \
+			case 814: return 7723;  \
+			case 817: return 7830;  \
+			}  \
+			return 0;  \
+		case 105:  \
+			switch (ch2) {  \
+			case 768: return 236;  \
+			case 769: return 237;  \
+			case 770: return 238;  \
+			case 771: return 297;  \
+			case 772: return 299;  \
+			case 774: return 301;  \
+			case 776: return 239;  \
+			case 777: return 7881;  \
+			case 780: return 464;  \
+			case 783: return 521;  \
+			case 785: return 523;  \
+			case 803: return 7883;  \
+			case 808: return 303;  \
+			case 816: return 7725;  \
+			}  \
+			return 0;  \
+		case 106:  \
+			switch (ch2) {  \
+			case 770: return 309;  \
+			case 780: return 496;  \
+			}  \
+			return 0;  \
+		case 107:  \
+			switch (ch2) {  \
+			case 769: return 7729;  \
+			case 780: return 489;  \
+			case 803: return 7731;  \
+			case 807: return 311;  \
+			case 817: return 7733;  \
+			}  \
+			return 0;  \
+		case 108:  \
+			switch (ch2) {  \
+			case 769: return 314;  \
+			case 780: return 318;  \
+			case 803: return 7735;  \
+			case 807: return 316;  \
+			case 813: return 7741;  \
+			case 817: return 7739;  \
+			}  \
+			return 0;  \
+		case 109:  \
+			switch (ch2) {  \
+			case 769: return 7743;  \
+			case 775: return 7745;  \
+			case 803: return 7747;  \
+			}  \
+			return 0;  \
+		case 110:  \
+			switch (ch2) {  \
+			case 768: return 505;  \
+			case 769: return 324;  \
+			case 771: return 241;  \
+			case 775: return 7749;  \
+			case 780: return 328;  \
+			case 803: return 7751;  \
+			case 807: return 326;  \
+			case 813: return 7755;  \
+			case 817: return 7753;  \
+			}  \
+			return 0;  \
+		case 111:  \
+			switch (ch2) {  \
+			case 768: return 242;  \
+			case 769: return 243;  \
+			case 770: return 244;  \
+			case 771: return 245;  \
+			case 772: return 333;  \
+			case 774: return 335;  \
+			case 775: return 559;  \
+			case 776: return 246;  \
+			case 777: return 7887;  \
+			case 779: return 337;  \
+			case 780: return 466;  \
+			case 783: return 525;  \
+			case 785: return 527;  \
+			case 795: return 417;  \
+			case 803: return 7885;  \
+			case 808: return 491;  \
+			}  \
+			return 0;  \
+		case 112:  \
+			switch (ch2) {  \
+			case 769: return 7765;  \
+			case 775: return 7767;  \
+			}  \
+			return 0;  \
+		case 114:  \
+			switch (ch2) {  \
+			case 769: return 341;  \
+			case 775: return 7769;  \
+			case 780: return 345;  \
+			case 783: return 529;  \
+			case 785: return 531;  \
+			case 803: return 7771;  \
+			case 807: return 343;  \
+			case 817: return 7775;  \
+			}  \
+			return 0;  \
+		case 115:  \
+			switch (ch2) {  \
+			case 769: return 347;  \
+			case 770: return 349;  \
+			case 775: return 7777;  \
+			case 780: return 353;  \
+			case 803: return 7779;  \
+			case 806: return 537;  \
+			case 807: return 351;  \
+			}  \
+			return 0;  \
+		case 116:  \
+			switch (ch2) {  \
+			case 775: return 7787;  \
+			case 776: return 7831;  \
+			case 780: return 357;  \
+			case 803: return 7789;  \
+			case 806: return 539;  \
+			case 807: return 355;  \
+			case 813: return 7793;  \
+			case 817: return 7791;  \
+			}  \
+			return 0;  \
+		case 117:  \
+			switch (ch2) {  \
+			case 768: return 249;  \
+			case 769: return 250;  \
+			case 770: return 251;  \
+			case 771: return 361;  \
+			case 772: return 363;  \
+			case 774: return 365;  \
+			case 776: return 252;  \
+			case 777: return 7911;  \
+			case 778: return 367;  \
+			case 779: return 369;  \
+			case 780: return 468;  \
+			case 783: return 533;  \
+			case 785: return 535;  \
+			case 795: return 432;  \
+			case 803: return 7909;  \
+			case 804: return 7795;  \
+			case 808: return 371;  \
+			case 813: return 7799;  \
+			case 816: return 7797;  \
+			}  \
+			return 0;  \
+		case 118:  \
+			switch (ch2) {  \
+			case 771: return 7805;  \
+			case 803: return 7807;  \
+			}  \
+			return 0;  \
+		case 119:  \
+			switch (ch2) {  \
+			case 768: return 7809;  \
+			case 769: return 7811;  \
+			case 770: return 373;  \
+			case 775: return 7815;  \
+			case 776: return 7813;  \
+			case 778: return 7832;  \
+			case 803: return 7817;  \
+			}  \
+			return 0;  \
+		case 120:  \
+			switch (ch2) {  \
+			case 775: return 7819;  \
+			case 776: return 7821;  \
+			}  \
+			return 0;  \
+		case 121:  \
+			switch (ch2) {  \
+			case 768: return 7923;  \
+			case 769: return 253;  \
+			case 770: return 375;  \
+			case 771: return 7929;  \
+			case 772: return 563;  \
+			case 775: return 7823;  \
+			case 776: return 255;  \
+			case 777: return 7927;  \
+			case 778: return 7833;  \
+			case 803: return 7925;  \
+			}  \
+			return 0;  \
+		case 122:  \
+			switch (ch2) {  \
+			case 769: return 378;  \
+			case 770: return 7825;  \
+			case 775: return 380;  \
+			case 780: return 382;  \
+			case 803: return 7827;  \
+			case 817: return 7829;  \
+			}  \
+			return 0;  \
+		case 168:  \
+			switch (ch2) {  \
+			case 768: return 8173;  \
+			case 769: return 901;  \
+			case 834: return 8129;  \
+			}  \
+			return 0;  \
+		case 194:  \
+			switch (ch2) {  \
+			case 768: return 7846;  \
+			case 769: return 7844;  \
+			case 771: return 7850;  \
+			case 777: return 7848;  \
+			}  \
+			return 0;  \
+		case 196:  \
+			switch (ch2) {  \
+			case 772: return 478;  \
+			}  \
+			return 0;  \
+		case 197:  \
+			switch (ch2) {  \
+			case 769: return 506;  \
+			}  \
+			return 0;  \
+		case 198:  \
+			switch (ch2) {  \
+			case 769: return 508;  \
+			case 772: return 482;  \
+			}  \
+			return 0;  \
+		case 199:  \
+			switch (ch2) {  \
+			case 769: return 7688;  \
+			}  \
+			return 0;  \
+		case 202:  \
+			switch (ch2) {  \
+			case 768: return 7872;  \
+			case 769: return 7870;  \
+			case 771: return 7876;  \
+			case 777: return 7874;  \
+			}  \
+			return 0;  \
+		case 207:  \
+			switch (ch2) {  \
+			case 769: return 7726;  \
+			}  \
+			return 0;  \
+		case 212:  \
+			switch (ch2) {  \
+			case 768: return 7890;  \
+			case 769: return 7888;  \
+			case 771: return 7894;  \
+			case 777: return 7892;  \
+			}  \
+			return 0;  \
+		case 213:  \
+			switch (ch2) {  \
+			case 769: return 7756;  \
+			case 772: return 556;  \
+			case 776: return 7758;  \
+			}  \
+			return 0;  \
+		case 214:  \
+			switch (ch2) {  \
+			case 772: return 554;  \
+			}  \
+			return 0;  \
+		case 216:  \
+			switch (ch2) {  \
+			case 769: return 510;  \
+			}  \
+			return 0;  \
+		case 220:  \
+			switch (ch2) {  \
+			case 768: return 475;  \
+			case 769: return 471;  \
+			case 772: return 469;  \
+			case 780: return 473;  \
+			}  \
+			return 0;  \
+		case 226:  \
+			switch (ch2) {  \
+			case 768: return 7847;  \
+			case 769: return 7845;  \
+			case 771: return 7851;  \
+			case 777: return 7849;  \
+			}  \
+			return 0;  \
+		case 228:  \
+			switch (ch2) {  \
+			case 772: return 479;  \
+			}  \
+			return 0;  \
+		case 229:  \
+			switch (ch2) {  \
+			case 769: return 507;  \
+			}  \
+			return 0;  \
+		case 230:  \
+			switch (ch2) {  \
+			case 769: return 509;  \
+			case 772: return 483;  \
+			}  \
+			return 0;  \
+		case 231:  \
+			switch (ch2) {  \
+			case 769: return 7689;  \
+			}  \
+			return 0;  \
+		case 234:  \
+			switch (ch2) {  \
+			case 768: return 7873;  \
+			case 769: return 7871;  \
+			case 771: return 7877;  \
+			case 777: return 7875;  \
+			}  \
+			return 0;  \
+		case 239:  \
+			switch (ch2) {  \
+			case 769: return 7727;  \
+			}  \
+			return 0;  \
+		case 244:  \
+			switch (ch2) {  \
+			case 768: return 7891;  \
+			case 769: return 7889;  \
+			case 771: return 7895;  \
+			case 777: return 7893;  \
+			}  \
+			return 0;  \
+		case 245:  \
+			switch (ch2) {  \
+			case 769: return 7757;  \
+			case 772: return 557;  \
+			case 776: return 7759;  \
+			}  \
+			return 0;  \
+		case 246:  \
+			switch (ch2) {  \
+			case 772: return 555;  \
+			}  \
+			return 0;  \
+		case 248:  \
+			switch (ch2) {  \
+			case 769: return 511;  \
+			}  \
+			return 0;  \
+		case 252:  \
+			switch (ch2) {  \
+			case 768: return 476;  \
+			case 769: return 472;  \
+			case 772: return 470;  \
+			case 780: return 474;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 1:  \
+		switch (ch1) {  \
+		case 258:  \
+			switch (ch2) {  \
+			case 768: return 7856;  \
+			case 769: return 7854;  \
+			case 771: return 7860;  \
+			case 777: return 7858;  \
+			}  \
+			return 0;  \
+		case 259:  \
+			switch (ch2) {  \
+			case 768: return 7857;  \
+			case 769: return 7855;  \
+			case 771: return 7861;  \
+			case 777: return 7859;  \
+			}  \
+			return 0;  \
+		case 274:  \
+			switch (ch2) {  \
+			case 768: return 7700;  \
+			case 769: return 7702;  \
+			}  \
+			return 0;  \
+		case 275:  \
+			switch (ch2) {  \
+			case 768: return 7701;  \
+			case 769: return 7703;  \
+			}  \
+			return 0;  \
+		case 332:  \
+			switch (ch2) {  \
+			case 768: return 7760;  \
+			case 769: return 7762;  \
+			}  \
+			return 0;  \
+		case 333:  \
+			switch (ch2) {  \
+			case 768: return 7761;  \
+			case 769: return 7763;  \
+			}  \
+			return 0;  \
+		case 346:  \
+			switch (ch2) {  \
+			case 775: return 7780;  \
+			}  \
+			return 0;  \
+		case 347:  \
+			switch (ch2) {  \
+			case 775: return 7781;  \
+			}  \
+			return 0;  \
+		case 352:  \
+			switch (ch2) {  \
+			case 775: return 7782;  \
+			}  \
+			return 0;  \
+		case 353:  \
+			switch (ch2) {  \
+			case 775: return 7783;  \
+			}  \
+			return 0;  \
+		case 360:  \
+			switch (ch2) {  \
+			case 769: return 7800;  \
+			}  \
+			return 0;  \
+		case 361:  \
+			switch (ch2) {  \
+			case 769: return 7801;  \
+			}  \
+			return 0;  \
+		case 362:  \
+			switch (ch2) {  \
+			case 776: return 7802;  \
+			}  \
+			return 0;  \
+		case 363:  \
+			switch (ch2) {  \
+			case 776: return 7803;  \
+			}  \
+			return 0;  \
+		case 383:  \
+			switch (ch2) {  \
+			case 775: return 7835;  \
+			}  \
+			return 0;  \
+		case 416:  \
+			switch (ch2) {  \
+			case 768: return 7900;  \
+			case 769: return 7898;  \
+			case 771: return 7904;  \
+			case 777: return 7902;  \
+			case 803: return 7906;  \
+			}  \
+			return 0;  \
+		case 417:  \
+			switch (ch2) {  \
+			case 768: return 7901;  \
+			case 769: return 7899;  \
+			case 771: return 7905;  \
+			case 777: return 7903;  \
+			case 803: return 7907;  \
+			}  \
+			return 0;  \
+		case 431:  \
+			switch (ch2) {  \
+			case 768: return 7914;  \
+			case 769: return 7912;  \
+			case 771: return 7918;  \
+			case 777: return 7916;  \
+			case 803: return 7920;  \
+			}  \
+			return 0;  \
+		case 432:  \
+			switch (ch2) {  \
+			case 768: return 7915;  \
+			case 769: return 7913;  \
+			case 771: return 7919;  \
+			case 777: return 7917;  \
+			case 803: return 7921;  \
+			}  \
+			return 0;  \
+		case 439:  \
+			switch (ch2) {  \
+			case 780: return 494;  \
+			}  \
+			return 0;  \
+		case 490:  \
+			switch (ch2) {  \
+			case 772: return 492;  \
+			}  \
+			return 0;  \
+		case 491:  \
+			switch (ch2) {  \
+			case 772: return 493;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 2:  \
+		switch (ch1) {  \
+		case 550:  \
+			switch (ch2) {  \
+			case 772: return 480;  \
+			}  \
+			return 0;  \
+		case 551:  \
+			switch (ch2) {  \
+			case 772: return 481;  \
+			}  \
+			return 0;  \
+		case 552:  \
+			switch (ch2) {  \
+			case 774: return 7708;  \
+			}  \
+			return 0;  \
+		case 553:  \
+			switch (ch2) {  \
+			case 774: return 7709;  \
+			}  \
+			return 0;  \
+		case 558:  \
+			switch (ch2) {  \
+			case 772: return 560;  \
+			}  \
+			return 0;  \
+		case 559:  \
+			switch (ch2) {  \
+			case 772: return 561;  \
+			}  \
+			return 0;  \
+		case 658:  \
+			switch (ch2) {  \
+			case 780: return 495;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 3:  \
+		switch (ch1) {  \
+		case 776:  \
+			switch (ch2) {  \
+			case 769: return 836;  \
+			}  \
+			return 0;  \
+		case 913:  \
+			switch (ch2) {  \
+			case 768: return 8122;  \
+			case 769: return 902;  \
+			case 772: return 8121;  \
+			case 774: return 8120;  \
+			case 787: return 7944;  \
+			case 788: return 7945;  \
+			case 837: return 8124;  \
+			}  \
+			return 0;  \
+		case 917:  \
+			switch (ch2) {  \
+			case 768: return 8136;  \
+			case 769: return 904;  \
+			case 787: return 7960;  \
+			case 788: return 7961;  \
+			}  \
+			return 0;  \
+		case 919:  \
+			switch (ch2) {  \
+			case 768: return 8138;  \
+			case 769: return 905;  \
+			case 787: return 7976;  \
+			case 788: return 7977;  \
+			case 837: return 8140;  \
+			}  \
+			return 0;  \
+		case 921:  \
+			switch (ch2) {  \
+			case 768: return 8154;  \
+			case 769: return 906;  \
+			case 772: return 8153;  \
+			case 774: return 8152;  \
+			case 776: return 938;  \
+			case 787: return 7992;  \
+			case 788: return 7993;  \
+			}  \
+			return 0;  \
+		case 927:  \
+			switch (ch2) {  \
+			case 768: return 8184;  \
+			case 769: return 908;  \
+			case 787: return 8008;  \
+			case 788: return 8009;  \
+			}  \
+			return 0;  \
+		case 929:  \
+			switch (ch2) {  \
+			case 788: return 8172;  \
+			}  \
+			return 0;  \
+		case 933:  \
+			switch (ch2) {  \
+			case 768: return 8170;  \
+			case 769: return 910;  \
+			case 772: return 8169;  \
+			case 774: return 8168;  \
+			case 776: return 939;  \
+			case 788: return 8025;  \
+			}  \
+			return 0;  \
+		case 937:  \
+			switch (ch2) {  \
+			case 768: return 8186;  \
+			case 769: return 911;  \
+			case 787: return 8040;  \
+			case 788: return 8041;  \
+			case 837: return 8188;  \
+			}  \
+			return 0;  \
+		case 940:  \
+			switch (ch2) {  \
+			case 837: return 8116;  \
+			}  \
+			return 0;  \
+		case 942:  \
+			switch (ch2) {  \
+			case 837: return 8132;  \
+			}  \
+			return 0;  \
+		case 945:  \
+			switch (ch2) {  \
+			case 768: return 8048;  \
+			case 769: return 940;  \
+			case 772: return 8113;  \
+			case 774: return 8112;  \
+			case 787: return 7936;  \
+			case 788: return 7937;  \
+			case 834: return 8118;  \
+			case 837: return 8115;  \
+			}  \
+			return 0;  \
+		case 949:  \
+			switch (ch2) {  \
+			case 768: return 8050;  \
+			case 769: return 941;  \
+			case 787: return 7952;  \
+			case 788: return 7953;  \
+			}  \
+			return 0;  \
+		case 951:  \
+			switch (ch2) {  \
+			case 768: return 8052;  \
+			case 769: return 942;  \
+			case 787: return 7968;  \
+			case 788: return 7969;  \
+			case 834: return 8134;  \
+			case 837: return 8131;  \
+			}  \
+			return 0;  \
+		case 953:  \
+			switch (ch2) {  \
+			case 768: return 8054;  \
+			case 769: return 943;  \
+			case 772: return 8145;  \
+			case 774: return 8144;  \
+			case 776: return 970;  \
+			case 787: return 7984;  \
+			case 788: return 7985;  \
+			case 834: return 8150;  \
+			}  \
+			return 0;  \
+		case 959:  \
+			switch (ch2) {  \
+			case 768: return 8056;  \
+			case 769: return 972;  \
+			case 787: return 8000;  \
+			case 788: return 8001;  \
+			}  \
+			return 0;  \
+		case 961:  \
+			switch (ch2) {  \
+			case 787: return 8164;  \
+			case 788: return 8165;  \
+			}  \
+			return 0;  \
+		case 965:  \
+			switch (ch2) {  \
+			case 768: return 8058;  \
+			case 769: return 973;  \
+			case 772: return 8161;  \
+			case 774: return 8160;  \
+			case 776: return 971;  \
+			case 787: return 8016;  \
+			case 788: return 8017;  \
+			case 834: return 8166;  \
+			}  \
+			return 0;  \
+		case 969:  \
+			switch (ch2) {  \
+			case 768: return 8060;  \
+			case 769: return 974;  \
+			case 787: return 8032;  \
+			case 788: return 8033;  \
+			case 834: return 8182;  \
+			case 837: return 8179;  \
+			}  \
+			return 0;  \
+		case 970:  \
+			switch (ch2) {  \
+			case 768: return 8146;  \
+			case 769: return 912;  \
+			case 834: return 8151;  \
+			}  \
+			return 0;  \
+		case 971:  \
+			switch (ch2) {  \
+			case 768: return 8162;  \
+			case 769: return 944;  \
+			case 834: return 8167;  \
+			}  \
+			return 0;  \
+		case 974:  \
+			switch (ch2) {  \
+			case 837: return 8180;  \
+			}  \
+			return 0;  \
+		case 978:  \
+			switch (ch2) {  \
+			case 769: return 979;  \
+			case 776: return 980;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 4:  \
+		switch (ch1) {  \
+		case 1030:  \
+			switch (ch2) {  \
+			case 776: return 1031;  \
+			}  \
+			return 0;  \
+		case 1040:  \
+			switch (ch2) {  \
+			case 774: return 1232;  \
+			case 776: return 1234;  \
+			}  \
+			return 0;  \
+		case 1043:  \
+			switch (ch2) {  \
+			case 769: return 1027;  \
+			}  \
+			return 0;  \
+		case 1045:  \
+			switch (ch2) {  \
+			case 768: return 1024;  \
+			case 774: return 1238;  \
+			case 776: return 1025;  \
+			}  \
+			return 0;  \
+		case 1046:  \
+			switch (ch2) {  \
+			case 774: return 1217;  \
+			case 776: return 1244;  \
+			}  \
+			return 0;  \
+		case 1047:  \
+			switch (ch2) {  \
+			case 776: return 1246;  \
+			}  \
+			return 0;  \
+		case 1048:  \
+			switch (ch2) {  \
+			case 768: return 1037;  \
+			case 772: return 1250;  \
+			case 774: return 1049;  \
+			case 776: return 1252;  \
+			}  \
+			return 0;  \
+		case 1050:  \
+			switch (ch2) {  \
+			case 769: return 1036;  \
+			}  \
+			return 0;  \
+		case 1054:  \
+			switch (ch2) {  \
+			case 776: return 1254;  \
+			}  \
+			return 0;  \
+		case 1059:  \
+			switch (ch2) {  \
+			case 772: return 1262;  \
+			case 774: return 1038;  \
+			case 776: return 1264;  \
+			case 779: return 1266;  \
+			}  \
+			return 0;  \
+		case 1063:  \
+			switch (ch2) {  \
+			case 776: return 1268;  \
+			}  \
+			return 0;  \
+		case 1067:  \
+			switch (ch2) {  \
+			case 776: return 1272;  \
+			}  \
+			return 0;  \
+		case 1069:  \
+			switch (ch2) {  \
+			case 776: return 1260;  \
+			}  \
+			return 0;  \
+		case 1072:  \
+			switch (ch2) {  \
+			case 774: return 1233;  \
+			case 776: return 1235;  \
+			}  \
+			return 0;  \
+		case 1075:  \
+			switch (ch2) {  \
+			case 769: return 1107;  \
+			}  \
+			return 0;  \
+		case 1077:  \
+			switch (ch2) {  \
+			case 768: return 1104;  \
+			case 774: return 1239;  \
+			case 776: return 1105;  \
+			}  \
+			return 0;  \
+		case 1078:  \
+			switch (ch2) {  \
+			case 774: return 1218;  \
+			case 776: return 1245;  \
+			}  \
+			return 0;  \
+		case 1079:  \
+			switch (ch2) {  \
+			case 776: return 1247;  \
+			}  \
+			return 0;  \
+		case 1080:  \
+			switch (ch2) {  \
+			case 768: return 1117;  \
+			case 772: return 1251;  \
+			case 774: return 1081;  \
+			case 776: return 1253;  \
+			}  \
+			return 0;  \
+		case 1082:  \
+			switch (ch2) {  \
+			case 769: return 1116;  \
+			}  \
+			return 0;  \
+		case 1086:  \
+			switch (ch2) {  \
+			case 776: return 1255;  \
+			}  \
+			return 0;  \
+		case 1091:  \
+			switch (ch2) {  \
+			case 772: return 1263;  \
+			case 774: return 1118;  \
+			case 776: return 1265;  \
+			case 779: return 1267;  \
+			}  \
+			return 0;  \
+		case 1095:  \
+			switch (ch2) {  \
+			case 776: return 1269;  \
+			}  \
+			return 0;  \
+		case 1099:  \
+			switch (ch2) {  \
+			case 776: return 1273;  \
+			}  \
+			return 0;  \
+		case 1101:  \
+			switch (ch2) {  \
+			case 776: return 1261;  \
+			}  \
+			return 0;  \
+		case 1110:  \
+			switch (ch2) {  \
+			case 776: return 1111;  \
+			}  \
+			return 0;  \
+		case 1140:  \
+			switch (ch2) {  \
+			case 783: return 1142;  \
+			}  \
+			return 0;  \
+		case 1141:  \
+			switch (ch2) {  \
+			case 783: return 1143;  \
+			}  \
+			return 0;  \
+		case 1240:  \
+			switch (ch2) {  \
+			case 776: return 1242;  \
+			}  \
+			return 0;  \
+		case 1241:  \
+			switch (ch2) {  \
+			case 776: return 1243;  \
+			}  \
+			return 0;  \
+		case 1256:  \
+			switch (ch2) {  \
+			case 776: return 1258;  \
+			}  \
+			return 0;  \
+		case 1257:  \
+			switch (ch2) {  \
+			case 776: return 1259;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 5:  \
+		switch (ch1) {  \
+		case 1488:  \
+			switch (ch2) {  \
+			case 1463: return 64302;  \
+			case 1464: return 64303;  \
+			case 1468: return 64304;  \
+			}  \
+			return 0;  \
+		case 1489:  \
+			switch (ch2) {  \
+			case 1468: return 64305;  \
+			case 1471: return 64332;  \
+			}  \
+			return 0;  \
+		case 1490:  \
+			switch (ch2) {  \
+			case 1468: return 64306;  \
+			}  \
+			return 0;  \
+		case 1491:  \
+			switch (ch2) {  \
+			case 1468: return 64307;  \
+			}  \
+			return 0;  \
+		case 1492:  \
+			switch (ch2) {  \
+			case 1468: return 64308;  \
+			}  \
+			return 0;  \
+		case 1493:  \
+			switch (ch2) {  \
+			case 1465: return 64331;  \
+			case 1468: return 64309;  \
+			}  \
+			return 0;  \
+		case 1494:  \
+			switch (ch2) {  \
+			case 1468: return 64310;  \
+			}  \
+			return 0;  \
+		case 1496:  \
+			switch (ch2) {  \
+			case 1468: return 64312;  \
+			}  \
+			return 0;  \
+		case 1497:  \
+			switch (ch2) {  \
+			case 1460: return 64285;  \
+			case 1468: return 64313;  \
+			}  \
+			return 0;  \
+		case 1498:  \
+			switch (ch2) {  \
+			case 1468: return 64314;  \
+			}  \
+			return 0;  \
+		case 1499:  \
+			switch (ch2) {  \
+			case 1468: return 64315;  \
+			case 1471: return 64333;  \
+			}  \
+			return 0;  \
+		case 1500:  \
+			switch (ch2) {  \
+			case 1468: return 64316;  \
+			}  \
+			return 0;  \
+		case 1502:  \
+			switch (ch2) {  \
+			case 1468: return 64318;  \
+			}  \
+			return 0;  \
+		case 1504:  \
+			switch (ch2) {  \
+			case 1468: return 64320;  \
+			}  \
+			return 0;  \
+		case 1505:  \
+			switch (ch2) {  \
+			case 1468: return 64321;  \
+			}  \
+			return 0;  \
+		case 1507:  \
+			switch (ch2) {  \
+			case 1468: return 64323;  \
+			}  \
+			return 0;  \
+		case 1508:  \
+			switch (ch2) {  \
+			case 1468: return 64324;  \
+			case 1471: return 64334;  \
+			}  \
+			return 0;  \
+		case 1510:  \
+			switch (ch2) {  \
+			case 1468: return 64326;  \
+			}  \
+			return 0;  \
+		case 1511:  \
+			switch (ch2) {  \
+			case 1468: return 64327;  \
+			}  \
+			return 0;  \
+		case 1512:  \
+			switch (ch2) {  \
+			case 1468: return 64328;  \
+			}  \
+			return 0;  \
+		case 1513:  \
+			switch (ch2) {  \
+			case 1468: return 64329;  \
+			case 1473: return 64298;  \
+			case 1474: return 64299;  \
+			}  \
+			return 0;  \
+		case 1514:  \
+			switch (ch2) {  \
+			case 1468: return 64330;  \
+			}  \
+			return 0;  \
+		case 1522:  \
+			switch (ch2) {  \
+			case 1463: return 64287;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 6:  \
+		switch (ch1) {  \
+		case 1575:  \
+			switch (ch2) {  \
+			case 1619: return 1570;  \
+			case 1620: return 1571;  \
+			case 1621: return 1573;  \
+			}  \
+			return 0;  \
+		case 1608:  \
+			switch (ch2) {  \
+			case 1620: return 1572;  \
+			}  \
+			return 0;  \
+		case 1610:  \
+			switch (ch2) {  \
+			case 1620: return 1574;  \
+			}  \
+			return 0;  \
+		case 1729:  \
+			switch (ch2) {  \
+			case 1620: return 1730;  \
+			}  \
+			return 0;  \
+		case 1746:  \
+			switch (ch2) {  \
+			case 1620: return 1747;  \
+			}  \
+			return 0;  \
+		case 1749:  \
+			switch (ch2) {  \
+			case 1620: return 1728;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 9:  \
+		switch (ch1) {  \
+		case 2325:  \
+			switch (ch2) {  \
+			case 2364: return 2392;  \
+			}  \
+			return 0;  \
+		case 2326:  \
+			switch (ch2) {  \
+			case 2364: return 2393;  \
+			}  \
+			return 0;  \
+		case 2327:  \
+			switch (ch2) {  \
+			case 2364: return 2394;  \
+			}  \
+			return 0;  \
+		case 2332:  \
+			switch (ch2) {  \
+			case 2364: return 2395;  \
+			}  \
+			return 0;  \
+		case 2337:  \
+			switch (ch2) {  \
+			case 2364: return 2396;  \
+			}  \
+			return 0;  \
+		case 2338:  \
+			switch (ch2) {  \
+			case 2364: return 2397;  \
+			}  \
+			return 0;  \
+		case 2344:  \
+			switch (ch2) {  \
+			case 2364: return 2345;  \
+			}  \
+			return 0;  \
+		case 2347:  \
+			switch (ch2) {  \
+			case 2364: return 2398;  \
+			}  \
+			return 0;  \
+		case 2351:  \
+			switch (ch2) {  \
+			case 2364: return 2399;  \
+			}  \
+			return 0;  \
+		case 2352:  \
+			switch (ch2) {  \
+			case 2364: return 2353;  \
+			}  \
+			return 0;  \
+		case 2355:  \
+			switch (ch2) {  \
+			case 2364: return 2356;  \
+			}  \
+			return 0;  \
+		case 2465:  \
+			switch (ch2) {  \
+			case 2492: return 2524;  \
+			}  \
+			return 0;  \
+		case 2466:  \
+			switch (ch2) {  \
+			case 2492: return 2525;  \
+			}  \
+			return 0;  \
+		case 2479:  \
+			switch (ch2) {  \
+			case 2492: return 2527;  \
+			}  \
+			return 0;  \
+		case 2503:  \
+			switch (ch2) {  \
+			case 2494: return 2507;  \
+			case 2519: return 2508;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 10:  \
+		switch (ch1) {  \
+		case 2582:  \
+			switch (ch2) {  \
+			case 2620: return 2649;  \
+			}  \
+			return 0;  \
+		case 2583:  \
+			switch (ch2) {  \
+			case 2620: return 2650;  \
+			}  \
+			return 0;  \
+		case 2588:  \
+			switch (ch2) {  \
+			case 2620: return 2651;  \
+			}  \
+			return 0;  \
+		case 2603:  \
+			switch (ch2) {  \
+			case 2620: return 2654;  \
+			}  \
+			return 0;  \
+		case 2610:  \
+			switch (ch2) {  \
+			case 2620: return 2611;  \
+			}  \
+			return 0;  \
+		case 2616:  \
+			switch (ch2) {  \
+			case 2620: return 2614;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 11:  \
+		switch (ch1) {  \
+		case 2849:  \
+			switch (ch2) {  \
+			case 2876: return 2908;  \
+			}  \
+			return 0;  \
+		case 2850:  \
+			switch (ch2) {  \
+			case 2876: return 2909;  \
+			}  \
+			return 0;  \
+		case 2887:  \
+			switch (ch2) {  \
+			case 2878: return 2891;  \
+			case 2902: return 2888;  \
+			case 2903: return 2892;  \
+			}  \
+			return 0;  \
+		case 2962:  \
+			switch (ch2) {  \
+			case 3031: return 2964;  \
+			}  \
+			return 0;  \
+		case 3014:  \
+			switch (ch2) {  \
+			case 3006: return 3018;  \
+			case 3031: return 3020;  \
+			}  \
+			return 0;  \
+		case 3015:  \
+			switch (ch2) {  \
+			case 3006: return 3019;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 12:  \
+		switch (ch1) {  \
+		case 3142:  \
+			switch (ch2) {  \
+			case 3158: return 3144;  \
+			}  \
+			return 0;  \
+		case 3263:  \
+			switch (ch2) {  \
+			case 3285: return 3264;  \
+			}  \
+			return 0;  \
+		case 3270:  \
+			switch (ch2) {  \
+			case 3266: return 3274;  \
+			case 3285: return 3271;  \
+			case 3286: return 3272;  \
+			}  \
+			return 0;  \
+		case 3274:  \
+			switch (ch2) {  \
+			case 3285: return 3275;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 13:  \
+		switch (ch1) {  \
+		case 3398:  \
+			switch (ch2) {  \
+			case 3390: return 3402;  \
+			case 3415: return 3404;  \
+			}  \
+			return 0;  \
+		case 3399:  \
+			switch (ch2) {  \
+			case 3390: return 3403;  \
+			}  \
+			return 0;  \
+		case 3545:  \
+			switch (ch2) {  \
+			case 3530: return 3546;  \
+			case 3535: return 3548;  \
+			case 3551: return 3550;  \
+			}  \
+			return 0;  \
+		case 3548:  \
+			switch (ch2) {  \
+			case 3530: return 3549;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 15:  \
+		switch (ch1) {  \
+		case 3904:  \
+			switch (ch2) {  \
+			case 4021: return 3945;  \
+			}  \
+			return 0;  \
+		case 3906:  \
+			switch (ch2) {  \
+			case 4023: return 3907;  \
+			}  \
+			return 0;  \
+		case 3916:  \
+			switch (ch2) {  \
+			case 4023: return 3917;  \
+			}  \
+			return 0;  \
+		case 3921:  \
+			switch (ch2) {  \
+			case 4023: return 3922;  \
+			}  \
+			return 0;  \
+		case 3926:  \
+			switch (ch2) {  \
+			case 4023: return 3927;  \
+			}  \
+			return 0;  \
+		case 3931:  \
+			switch (ch2) {  \
+			case 4023: return 3932;  \
+			}  \
+			return 0;  \
+		case 3953:  \
+			switch (ch2) {  \
+			case 3954: return 3955;  \
+			case 3956: return 3957;  \
+			case 3968: return 3969;  \
+			}  \
+			return 0;  \
+		case 3984:  \
+			switch (ch2) {  \
+			case 4021: return 4025;  \
+			}  \
+			return 0;  \
+		case 3986:  \
+			switch (ch2) {  \
+			case 4023: return 3987;  \
+			}  \
+			return 0;  \
+		case 3996:  \
+			switch (ch2) {  \
+			case 4023: return 3997;  \
+			}  \
+			return 0;  \
+		case 4001:  \
+			switch (ch2) {  \
+			case 4023: return 4002;  \
+			}  \
+			return 0;  \
+		case 4006:  \
+			switch (ch2) {  \
+			case 4023: return 4007;  \
+			}  \
+			return 0;  \
+		case 4011:  \
+			switch (ch2) {  \
+			case 4023: return 4012;  \
+			}  \
+			return 0;  \
+		case 4018:  \
+			switch (ch2) {  \
+			case 3968: return 3958;  \
+			}  \
+			return 0;  \
+		case 4019:  \
+			switch (ch2) {  \
+			case 3968: return 3960;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 16:  \
+		switch (ch1) {  \
+		case 4133:  \
+			switch (ch2) {  \
+			case 4142: return 4134;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 30:  \
+		switch (ch1) {  \
+		case 7734:  \
+			switch (ch2) {  \
+			case 772: return 7736;  \
+			}  \
+			return 0;  \
+		case 7735:  \
+			switch (ch2) {  \
+			case 772: return 7737;  \
+			}  \
+			return 0;  \
+		case 7770:  \
+			switch (ch2) {  \
+			case 772: return 7772;  \
+			}  \
+			return 0;  \
+		case 7771:  \
+			switch (ch2) {  \
+			case 772: return 7773;  \
+			}  \
+			return 0;  \
+		case 7778:  \
+			switch (ch2) {  \
+			case 775: return 7784;  \
+			}  \
+			return 0;  \
+		case 7779:  \
+			switch (ch2) {  \
+			case 775: return 7785;  \
+			}  \
+			return 0;  \
+		case 7840:  \
+			switch (ch2) {  \
+			case 770: return 7852;  \
+			case 774: return 7862;  \
+			}  \
+			return 0;  \
+		case 7841:  \
+			switch (ch2) {  \
+			case 770: return 7853;  \
+			case 774: return 7863;  \
+			}  \
+			return 0;  \
+		case 7864:  \
+			switch (ch2) {  \
+			case 770: return 7878;  \
+			}  \
+			return 0;  \
+		case 7865:  \
+			switch (ch2) {  \
+			case 770: return 7879;  \
+			}  \
+			return 0;  \
+		case 7884:  \
+			switch (ch2) {  \
+			case 770: return 7896;  \
+			}  \
+			return 0;  \
+		case 7885:  \
+			switch (ch2) {  \
+			case 770: return 7897;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 31:  \
+		switch (ch1) {  \
+		case 7936:  \
+			switch (ch2) {  \
+			case 768: return 7938;  \
+			case 769: return 7940;  \
+			case 834: return 7942;  \
+			case 837: return 8064;  \
+			}  \
+			return 0;  \
+		case 7937:  \
+			switch (ch2) {  \
+			case 768: return 7939;  \
+			case 769: return 7941;  \
+			case 834: return 7943;  \
+			case 837: return 8065;  \
+			}  \
+			return 0;  \
+		case 7938:  \
+			switch (ch2) {  \
+			case 837: return 8066;  \
+			}  \
+			return 0;  \
+		case 7939:  \
+			switch (ch2) {  \
+			case 837: return 8067;  \
+			}  \
+			return 0;  \
+		case 7940:  \
+			switch (ch2) {  \
+			case 837: return 8068;  \
+			}  \
+			return 0;  \
+		case 7941:  \
+			switch (ch2) {  \
+			case 837: return 8069;  \
+			}  \
+			return 0;  \
+		case 7942:  \
+			switch (ch2) {  \
+			case 837: return 8070;  \
+			}  \
+			return 0;  \
+		case 7943:  \
+			switch (ch2) {  \
+			case 837: return 8071;  \
+			}  \
+			return 0;  \
+		case 7944:  \
+			switch (ch2) {  \
+			case 768: return 7946;  \
+			case 769: return 7948;  \
+			case 834: return 7950;  \
+			case 837: return 8072;  \
+			}  \
+			return 0;  \
+		case 7945:  \
+			switch (ch2) {  \
+			case 768: return 7947;  \
+			case 769: return 7949;  \
+			case 834: return 7951;  \
+			case 837: return 8073;  \
+			}  \
+			return 0;  \
+		case 7946:  \
+			switch (ch2) {  \
+			case 837: return 8074;  \
+			}  \
+			return 0;  \
+		case 7947:  \
+			switch (ch2) {  \
+			case 837: return 8075;  \
+			}  \
+			return 0;  \
+		case 7948:  \
+			switch (ch2) {  \
+			case 837: return 8076;  \
+			}  \
+			return 0;  \
+		case 7949:  \
+			switch (ch2) {  \
+			case 837: return 8077;  \
+			}  \
+			return 0;  \
+		case 7950:  \
+			switch (ch2) {  \
+			case 837: return 8078;  \
+			}  \
+			return 0;  \
+		case 7951:  \
+			switch (ch2) {  \
+			case 837: return 8079;  \
+			}  \
+			return 0;  \
+		case 7952:  \
+			switch (ch2) {  \
+			case 768: return 7954;  \
+			case 769: return 7956;  \
+			}  \
+			return 0;  \
+		case 7953:  \
+			switch (ch2) {  \
+			case 768: return 7955;  \
+			case 769: return 7957;  \
+			}  \
+			return 0;  \
+		case 7960:  \
+			switch (ch2) {  \
+			case 768: return 7962;  \
+			case 769: return 7964;  \
+			}  \
+			return 0;  \
+		case 7961:  \
+			switch (ch2) {  \
+			case 768: return 7963;  \
+			case 769: return 7965;  \
+			}  \
+			return 0;  \
+		case 7968:  \
+			switch (ch2) {  \
+			case 768: return 7970;  \
+			case 769: return 7972;  \
+			case 834: return 7974;  \
+			case 837: return 8080;  \
+			}  \
+			return 0;  \
+		case 7969:  \
+			switch (ch2) {  \
+			case 768: return 7971;  \
+			case 769: return 7973;  \
+			case 834: return 7975;  \
+			case 837: return 8081;  \
+			}  \
+			return 0;  \
+		case 7970:  \
+			switch (ch2) {  \
+			case 837: return 8082;  \
+			}  \
+			return 0;  \
+		case 7971:  \
+			switch (ch2) {  \
+			case 837: return 8083;  \
+			}  \
+			return 0;  \
+		case 7972:  \
+			switch (ch2) {  \
+			case 837: return 8084;  \
+			}  \
+			return 0;  \
+		case 7973:  \
+			switch (ch2) {  \
+			case 837: return 8085;  \
+			}  \
+			return 0;  \
+		case 7974:  \
+			switch (ch2) {  \
+			case 837: return 8086;  \
+			}  \
+			return 0;  \
+		case 7975:  \
+			switch (ch2) {  \
+			case 837: return 8087;  \
+			}  \
+			return 0;  \
+		case 7976:  \
+			switch (ch2) {  \
+			case 768: return 7978;  \
+			case 769: return 7980;  \
+			case 834: return 7982;  \
+			case 837: return 8088;  \
+			}  \
+			return 0;  \
+		case 7977:  \
+			switch (ch2) {  \
+			case 768: return 7979;  \
+			case 769: return 7981;  \
+			case 834: return 7983;  \
+			case 837: return 8089;  \
+			}  \
+			return 0;  \
+		case 7978:  \
+			switch (ch2) {  \
+			case 837: return 8090;  \
+			}  \
+			return 0;  \
+		case 7979:  \
+			switch (ch2) {  \
+			case 837: return 8091;  \
+			}  \
+			return 0;  \
+		case 7980:  \
+			switch (ch2) {  \
+			case 837: return 8092;  \
+			}  \
+			return 0;  \
+		case 7981:  \
+			switch (ch2) {  \
+			case 837: return 8093;  \
+			}  \
+			return 0;  \
+		case 7982:  \
+			switch (ch2) {  \
+			case 837: return 8094;  \
+			}  \
+			return 0;  \
+		case 7983:  \
+			switch (ch2) {  \
+			case 837: return 8095;  \
+			}  \
+			return 0;  \
+		case 7984:  \
+			switch (ch2) {  \
+			case 768: return 7986;  \
+			case 769: return 7988;  \
+			case 834: return 7990;  \
+			}  \
+			return 0;  \
+		case 7985:  \
+			switch (ch2) {  \
+			case 768: return 7987;  \
+			case 769: return 7989;  \
+			case 834: return 7991;  \
+			}  \
+			return 0;  \
+		case 7992:  \
+			switch (ch2) {  \
+			case 768: return 7994;  \
+			case 769: return 7996;  \
+			case 834: return 7998;  \
+			}  \
+			return 0;  \
+		case 7993:  \
+			switch (ch2) {  \
+			case 768: return 7995;  \
+			case 769: return 7997;  \
+			case 834: return 7999;  \
+			}  \
+			return 0;  \
+		case 8000:  \
+			switch (ch2) {  \
+			case 768: return 8002;  \
+			case 769: return 8004;  \
+			}  \
+			return 0;  \
+		case 8001:  \
+			switch (ch2) {  \
+			case 768: return 8003;  \
+			case 769: return 8005;  \
+			}  \
+			return 0;  \
+		case 8008:  \
+			switch (ch2) {  \
+			case 768: return 8010;  \
+			case 769: return 8012;  \
+			}  \
+			return 0;  \
+		case 8009:  \
+			switch (ch2) {  \
+			case 768: return 8011;  \
+			case 769: return 8013;  \
+			}  \
+			return 0;  \
+		case 8016:  \
+			switch (ch2) {  \
+			case 768: return 8018;  \
+			case 769: return 8020;  \
+			case 834: return 8022;  \
+			}  \
+			return 0;  \
+		case 8017:  \
+			switch (ch2) {  \
+			case 768: return 8019;  \
+			case 769: return 8021;  \
+			case 834: return 8023;  \
+			}  \
+			return 0;  \
+		case 8025:  \
+			switch (ch2) {  \
+			case 768: return 8027;  \
+			case 769: return 8029;  \
+			case 834: return 8031;  \
+			}  \
+			return 0;  \
+		case 8032:  \
+			switch (ch2) {  \
+			case 768: return 8034;  \
+			case 769: return 8036;  \
+			case 834: return 8038;  \
+			case 837: return 8096;  \
+			}  \
+			return 0;  \
+		case 8033:  \
+			switch (ch2) {  \
+			case 768: return 8035;  \
+			case 769: return 8037;  \
+			case 834: return 8039;  \
+			case 837: return 8097;  \
+			}  \
+			return 0;  \
+		case 8034:  \
+			switch (ch2) {  \
+			case 837: return 8098;  \
+			}  \
+			return 0;  \
+		case 8035:  \
+			switch (ch2) {  \
+			case 837: return 8099;  \
+			}  \
+			return 0;  \
+		case 8036:  \
+			switch (ch2) {  \
+			case 837: return 8100;  \
+			}  \
+			return 0;  \
+		case 8037:  \
+			switch (ch2) {  \
+			case 837: return 8101;  \
+			}  \
+			return 0;  \
+		case 8038:  \
+			switch (ch2) {  \
+			case 837: return 8102;  \
+			}  \
+			return 0;  \
+		case 8039:  \
+			switch (ch2) {  \
+			case 837: return 8103;  \
+			}  \
+			return 0;  \
+		case 8040:  \
+			switch (ch2) {  \
+			case 768: return 8042;  \
+			case 769: return 8044;  \
+			case 834: return 8046;  \
+			case 837: return 8104;  \
+			}  \
+			return 0;  \
+		case 8041:  \
+			switch (ch2) {  \
+			case 768: return 8043;  \
+			case 769: return 8045;  \
+			case 834: return 8047;  \
+			case 837: return 8105;  \
+			}  \
+			return 0;  \
+		case 8042:  \
+			switch (ch2) {  \
+			case 837: return 8106;  \
+			}  \
+			return 0;  \
+		case 8043:  \
+			switch (ch2) {  \
+			case 837: return 8107;  \
+			}  \
+			return 0;  \
+		case 8044:  \
+			switch (ch2) {  \
+			case 837: return 8108;  \
+			}  \
+			return 0;  \
+		case 8045:  \
+			switch (ch2) {  \
+			case 837: return 8109;  \
+			}  \
+			return 0;  \
+		case 8046:  \
+			switch (ch2) {  \
+			case 837: return 8110;  \
+			}  \
+			return 0;  \
+		case 8047:  \
+			switch (ch2) {  \
+			case 837: return 8111;  \
+			}  \
+			return 0;  \
+		case 8048:  \
+			switch (ch2) {  \
+			case 837: return 8114;  \
+			}  \
+			return 0;  \
+		case 8052:  \
+			switch (ch2) {  \
+			case 837: return 8130;  \
+			}  \
+			return 0;  \
+		case 8060:  \
+			switch (ch2) {  \
+			case 837: return 8178;  \
+			}  \
+			return 0;  \
+		case 8118:  \
+			switch (ch2) {  \
+			case 837: return 8119;  \
+			}  \
+			return 0;  \
+		case 8127:  \
+			switch (ch2) {  \
+			case 768: return 8141;  \
+			case 769: return 8142;  \
+			case 834: return 8143;  \
+			}  \
+			return 0;  \
+		case 8134:  \
+			switch (ch2) {  \
+			case 837: return 8135;  \
+			}  \
+			return 0;  \
+		case 8182:  \
+			switch (ch2) {  \
+			case 837: return 8183;  \
+			}  \
+			return 0;  \
+		case 8190:  \
+			switch (ch2) {  \
+			case 768: return 8157;  \
+			case 769: return 8158;  \
+			case 834: return 8159;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 33:  \
+		switch (ch1) {  \
+		case 8592:  \
+			switch (ch2) {  \
+			case 824: return 8602;  \
+			}  \
+			return 0;  \
+		case 8594:  \
+			switch (ch2) {  \
+			case 824: return 8603;  \
+			}  \
+			return 0;  \
+		case 8596:  \
+			switch (ch2) {  \
+			case 824: return 8622;  \
+			}  \
+			return 0;  \
+		case 8656:  \
+			switch (ch2) {  \
+			case 824: return 8653;  \
+			}  \
+			return 0;  \
+		case 8658:  \
+			switch (ch2) {  \
+			case 824: return 8655;  \
+			}  \
+			return 0;  \
+		case 8660:  \
+			switch (ch2) {  \
+			case 824: return 8654;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 34:  \
+		switch (ch1) {  \
+		case 8707:  \
+			switch (ch2) {  \
+			case 824: return 8708;  \
+			}  \
+			return 0;  \
+		case 8712:  \
+			switch (ch2) {  \
+			case 824: return 8713;  \
+			}  \
+			return 0;  \
+		case 8715:  \
+			switch (ch2) {  \
+			case 824: return 8716;  \
+			}  \
+			return 0;  \
+		case 8739:  \
+			switch (ch2) {  \
+			case 824: return 8740;  \
+			}  \
+			return 0;  \
+		case 8741:  \
+			switch (ch2) {  \
+			case 824: return 8742;  \
+			}  \
+			return 0;  \
+		case 8764:  \
+			switch (ch2) {  \
+			case 824: return 8769;  \
+			}  \
+			return 0;  \
+		case 8771:  \
+			switch (ch2) {  \
+			case 824: return 8772;  \
+			}  \
+			return 0;  \
+		case 8773:  \
+			switch (ch2) {  \
+			case 824: return 8775;  \
+			}  \
+			return 0;  \
+		case 8776:  \
+			switch (ch2) {  \
+			case 824: return 8777;  \
+			}  \
+			return 0;  \
+		case 8781:  \
+			switch (ch2) {  \
+			case 824: return 8813;  \
+			}  \
+			return 0;  \
+		case 8801:  \
+			switch (ch2) {  \
+			case 824: return 8802;  \
+			}  \
+			return 0;  \
+		case 8804:  \
+			switch (ch2) {  \
+			case 824: return 8816;  \
+			}  \
+			return 0;  \
+		case 8805:  \
+			switch (ch2) {  \
+			case 824: return 8817;  \
+			}  \
+			return 0;  \
+		case 8818:  \
+			switch (ch2) {  \
+			case 824: return 8820;  \
+			}  \
+			return 0;  \
+		case 8819:  \
+			switch (ch2) {  \
+			case 824: return 8821;  \
+			}  \
+			return 0;  \
+		case 8822:  \
+			switch (ch2) {  \
+			case 824: return 8824;  \
+			}  \
+			return 0;  \
+		case 8823:  \
+			switch (ch2) {  \
+			case 824: return 8825;  \
+			}  \
+			return 0;  \
+		case 8826:  \
+			switch (ch2) {  \
+			case 824: return 8832;  \
+			}  \
+			return 0;  \
+		case 8827:  \
+			switch (ch2) {  \
+			case 824: return 8833;  \
+			}  \
+			return 0;  \
+		case 8828:  \
+			switch (ch2) {  \
+			case 824: return 8928;  \
+			}  \
+			return 0;  \
+		case 8829:  \
+			switch (ch2) {  \
+			case 824: return 8929;  \
+			}  \
+			return 0;  \
+		case 8834:  \
+			switch (ch2) {  \
+			case 824: return 8836;  \
+			}  \
+			return 0;  \
+		case 8835:  \
+			switch (ch2) {  \
+			case 824: return 8837;  \
+			}  \
+			return 0;  \
+		case 8838:  \
+			switch (ch2) {  \
+			case 824: return 8840;  \
+			}  \
+			return 0;  \
+		case 8839:  \
+			switch (ch2) {  \
+			case 824: return 8841;  \
+			}  \
+			return 0;  \
+		case 8849:  \
+			switch (ch2) {  \
+			case 824: return 8930;  \
+			}  \
+			return 0;  \
+		case 8850:  \
+			switch (ch2) {  \
+			case 824: return 8931;  \
+			}  \
+			return 0;  \
+		case 8866:  \
+			switch (ch2) {  \
+			case 824: return 8876;  \
+			}  \
+			return 0;  \
+		case 8872:  \
+			switch (ch2) {  \
+			case 824: return 8877;  \
+			}  \
+			return 0;  \
+		case 8873:  \
+			switch (ch2) {  \
+			case 824: return 8878;  \
+			}  \
+			return 0;  \
+		case 8875:  \
+			switch (ch2) {  \
+			case 824: return 8879;  \
+			}  \
+			return 0;  \
+		case 8882:  \
+			switch (ch2) {  \
+			case 824: return 8938;  \
+			}  \
+			return 0;  \
+		case 8883:  \
+			switch (ch2) {  \
+			case 824: return 8939;  \
+			}  \
+			return 0;  \
+		case 8884:  \
+			switch (ch2) {  \
+			case 824: return 8940;  \
+			}  \
+			return 0;  \
+		case 8885:  \
+			switch (ch2) {  \
+			case 824: return 8941;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 42:  \
+		switch (ch1) {  \
+		case 10973:  \
+			switch (ch2) {  \
+			case 824: return 10972;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 48:  \
+		switch (ch1) {  \
+		case 12358:  \
+			switch (ch2) {  \
+			case 12441: return 12436;  \
+			}  \
+			return 0;  \
+		case 12363:  \
+			switch (ch2) {  \
+			case 12441: return 12364;  \
+			}  \
+			return 0;  \
+		case 12365:  \
+			switch (ch2) {  \
+			case 12441: return 12366;  \
+			}  \
+			return 0;  \
+		case 12367:  \
+			switch (ch2) {  \
+			case 12441: return 12368;  \
+			}  \
+			return 0;  \
+		case 12369:  \
+			switch (ch2) {  \
+			case 12441: return 12370;  \
+			}  \
+			return 0;  \
+		case 12371:  \
+			switch (ch2) {  \
+			case 12441: return 12372;  \
+			}  \
+			return 0;  \
+		case 12373:  \
+			switch (ch2) {  \
+			case 12441: return 12374;  \
+			}  \
+			return 0;  \
+		case 12375:  \
+			switch (ch2) {  \
+			case 12441: return 12376;  \
+			}  \
+			return 0;  \
+		case 12377:  \
+			switch (ch2) {  \
+			case 12441: return 12378;  \
+			}  \
+			return 0;  \
+		case 12379:  \
+			switch (ch2) {  \
+			case 12441: return 12380;  \
+			}  \
+			return 0;  \
+		case 12381:  \
+			switch (ch2) {  \
+			case 12441: return 12382;  \
+			}  \
+			return 0;  \
+		case 12383:  \
+			switch (ch2) {  \
+			case 12441: return 12384;  \
+			}  \
+			return 0;  \
+		case 12385:  \
+			switch (ch2) {  \
+			case 12441: return 12386;  \
+			}  \
+			return 0;  \
+		case 12388:  \
+			switch (ch2) {  \
+			case 12441: return 12389;  \
+			}  \
+			return 0;  \
+		case 12390:  \
+			switch (ch2) {  \
+			case 12441: return 12391;  \
+			}  \
+			return 0;  \
+		case 12392:  \
+			switch (ch2) {  \
+			case 12441: return 12393;  \
+			}  \
+			return 0;  \
+		case 12399:  \
+			switch (ch2) {  \
+			case 12441: return 12400;  \
+			case 12442: return 12401;  \
+			}  \
+			return 0;  \
+		case 12402:  \
+			switch (ch2) {  \
+			case 12441: return 12403;  \
+			case 12442: return 12404;  \
+			}  \
+			return 0;  \
+		case 12405:  \
+			switch (ch2) {  \
+			case 12441: return 12406;  \
+			case 12442: return 12407;  \
+			}  \
+			return 0;  \
+		case 12408:  \
+			switch (ch2) {  \
+			case 12441: return 12409;  \
+			case 12442: return 12410;  \
+			}  \
+			return 0;  \
+		case 12411:  \
+			switch (ch2) {  \
+			case 12441: return 12412;  \
+			case 12442: return 12413;  \
+			}  \
+			return 0;  \
+		case 12445:  \
+			switch (ch2) {  \
+			case 12441: return 12446;  \
+			}  \
+			return 0;  \
+		case 12454:  \
+			switch (ch2) {  \
+			case 12441: return 12532;  \
+			}  \
+			return 0;  \
+		case 12459:  \
+			switch (ch2) {  \
+			case 12441: return 12460;  \
+			}  \
+			return 0;  \
+		case 12461:  \
+			switch (ch2) {  \
+			case 12441: return 12462;  \
+			}  \
+			return 0;  \
+		case 12463:  \
+			switch (ch2) {  \
+			case 12441: return 12464;  \
+			}  \
+			return 0;  \
+		case 12465:  \
+			switch (ch2) {  \
+			case 12441: return 12466;  \
+			}  \
+			return 0;  \
+		case 12467:  \
+			switch (ch2) {  \
+			case 12441: return 12468;  \
+			}  \
+			return 0;  \
+		case 12469:  \
+			switch (ch2) {  \
+			case 12441: return 12470;  \
+			}  \
+			return 0;  \
+		case 12471:  \
+			switch (ch2) {  \
+			case 12441: return 12472;  \
+			}  \
+			return 0;  \
+		case 12473:  \
+			switch (ch2) {  \
+			case 12441: return 12474;  \
+			}  \
+			return 0;  \
+		case 12475:  \
+			switch (ch2) {  \
+			case 12441: return 12476;  \
+			}  \
+			return 0;  \
+		case 12477:  \
+			switch (ch2) {  \
+			case 12441: return 12478;  \
+			}  \
+			return 0;  \
+		case 12479:  \
+			switch (ch2) {  \
+			case 12441: return 12480;  \
+			}  \
+			return 0;  \
+		case 12481:  \
+			switch (ch2) {  \
+			case 12441: return 12482;  \
+			}  \
+			return 0;  \
+		case 12484:  \
+			switch (ch2) {  \
+			case 12441: return 12485;  \
+			}  \
+			return 0;  \
+		case 12486:  \
+			switch (ch2) {  \
+			case 12441: return 12487;  \
+			}  \
+			return 0;  \
+		case 12488:  \
+			switch (ch2) {  \
+			case 12441: return 12489;  \
+			}  \
+			return 0;  \
+		case 12495:  \
+			switch (ch2) {  \
+			case 12441: return 12496;  \
+			case 12442: return 12497;  \
+			}  \
+			return 0;  \
+		case 12498:  \
+			switch (ch2) {  \
+			case 12441: return 12499;  \
+			case 12442: return 12500;  \
+			}  \
+			return 0;  \
+		case 12501:  \
+			switch (ch2) {  \
+			case 12441: return 12502;  \
+			case 12442: return 12503;  \
+			}  \
+			return 0;  \
+		case 12504:  \
+			switch (ch2) {  \
+			case 12441: return 12505;  \
+			case 12442: return 12506;  \
+			}  \
+			return 0;  \
+		case 12507:  \
+			switch (ch2) {  \
+			case 12441: return 12508;  \
+			case 12442: return 12509;  \
+			}  \
+			return 0;  \
+		case 12527:  \
+			switch (ch2) {  \
+			case 12441: return 12535;  \
+			}  \
+			return 0;  \
+		case 12528:  \
+			switch (ch2) {  \
+			case 12441: return 12536;  \
+			}  \
+			return 0;  \
+		case 12529:  \
+			switch (ch2) {  \
+			case 12441: return 12537;  \
+			}  \
+			return 0;  \
+		case 12530:  \
+			switch (ch2) {  \
+			case 12441: return 12538;  \
+			}  \
+			return 0;  \
+		case 12541:  \
+			switch (ch2) {  \
+			case 12441: return 12542;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 251:  \
+		switch (ch1) {  \
+		case 64329:  \
+			switch (ch2) {  \
+			case 1473: return 64300;  \
+			case 1474: return 64301;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	case 465:  \
+		switch (ch1) {  \
+		case 119127:  \
+			switch (ch2) {  \
+			case 119141: return 119134;  \
+			}  \
+			return 0;  \
+		case 119128:  \
+			switch (ch2) {  \
+			case 119141: return 119135;  \
+			}  \
+			return 0;  \
+		case 119135:  \
+			switch (ch2) {  \
+			case 119150: return 119136;  \
+			case 119151: return 119137;  \
+			case 119152: return 119138;  \
+			case 119153: return 119139;  \
+			case 119154: return 119140;  \
+			}  \
+			return 0;  \
+		case 119225:  \
+			switch (ch2) {  \
+			case 119141: return 119227;  \
+			}  \
+			return 0;  \
+		case 119226:  \
+			switch (ch2) {  \
+			case 119141: return 119228;  \
+			}  \
+			return 0;  \
+		case 119227:  \
+			switch (ch2) {  \
+			case 119150: return 119229;  \
+			case 119151: return 119231;  \
+			}  \
+			return 0;  \
+		case 119228:  \
+			switch (ch2) {  \
+			case 119150: return 119230;  \
+			case 119151: return 119232;  \
+			}  \
+			return 0;  \
+		}  \
+		return 0;  \
+	}  \
+	return 0;
 
 glui32 unigen_decomp_data[3247] = {
 	0x41, 0x300, 0x41, 0x301, 0x41, 0x302, 0x41, 0x303,
@@ -7587,7 +7587,8 @@ glui32 unigen_decomp_data[3247] = {
 	0x980b, 0x9829, 0x295b6, 0x98e2, 0x4b33, 0x9929, 0x99a7, 0x99c2,
 	0x99fe, 0x4bce, 0x29b30, 0x9b12, 0x9c40, 0x9cfd, 0x4cce, 0x4ced,
 	0x9d67, 0x2a0ce, 0x4cf8, 0x2a105, 0x2a20e, 0x2a291, 0x9ebb, 0x4d56,
-	0x9ef9, 0x9efe, 0x9f05, 0x9f0f, 0x9f16, 0x9f3b, 0x2a600, };
+	0x9ef9, 0x9efe, 0x9f05, 0x9f0f, 0x9f16, 0x9f3b, 0x2a600,
+};
 
 gli_decomp_block_t unigen_decomp_block_0x0[256] = {
 	{ 0, 0 },
@@ -11475,351 +11476,351 @@ gli_decomp_block_t unigen_decomp_block_0x2fa[256] = {
 };
 
 #define GET_DECOMP_BLOCK(ch, blockptr)  \
-switch ((glui32)(ch) >> 8) {  \
-    case 0x0:  \
-        *blockptr = unigen_decomp_block_0x0;  \
-        break;  \
-    case 0x1:  \
-        *blockptr = unigen_decomp_block_0x1;  \
-        break;  \
-    case 0x2:  \
-        *blockptr = unigen_decomp_block_0x2;  \
-        break;  \
-    case 0x3:  \
-        *blockptr = unigen_decomp_block_0x3;  \
-        break;  \
-    case 0x4:  \
-        *blockptr = unigen_decomp_block_0x4;  \
-        break;  \
-    case 0x1e:  \
-        *blockptr = unigen_decomp_block_0x1e;  \
-        break;  \
-    case 0x1f:  \
-        *blockptr = unigen_decomp_block_0x1f;  \
-        break;  \
-    case 0x22:  \
-        *blockptr = unigen_decomp_block_0x22;  \
-        break;  \
-    case 0x30:  \
-        *blockptr = unigen_decomp_block_0x30;  \
-        break;  \
-    case 0xf9:  \
-        *blockptr = unigen_decomp_block_0xf9;  \
-        break;  \
-    case 0xfa:  \
-        *blockptr = unigen_decomp_block_0xfa;  \
-        break;  \
-    case 0xfb:  \
-        *blockptr = unigen_decomp_block_0xfb;  \
-        break;  \
-    case 0x2f8:  \
-        *blockptr = unigen_decomp_block_0x2f8;  \
-        break;  \
-    case 0x2f9:  \
-        *blockptr = unigen_decomp_block_0x2f9;  \
-        break;  \
-    case 0x2fa:  \
-        *blockptr = unigen_decomp_block_0x2fa;  \
-        break;  \
-    default:  \
-        *blockptr = nullptr;  \
-}
+	switch ((glui32)(ch) >> 8) {  \
+	case 0x0:  \
+		*blockptr = unigen_decomp_block_0x0;  \
+		break;  \
+	case 0x1:  \
+		*blockptr = unigen_decomp_block_0x1;  \
+		break;  \
+	case 0x2:  \
+		*blockptr = unigen_decomp_block_0x2;  \
+		break;  \
+	case 0x3:  \
+		*blockptr = unigen_decomp_block_0x3;  \
+		break;  \
+	case 0x4:  \
+		*blockptr = unigen_decomp_block_0x4;  \
+		break;  \
+	case 0x1e:  \
+		*blockptr = unigen_decomp_block_0x1e;  \
+		break;  \
+	case 0x1f:  \
+		*blockptr = unigen_decomp_block_0x1f;  \
+		break;  \
+	case 0x22:  \
+		*blockptr = unigen_decomp_block_0x22;  \
+		break;  \
+	case 0x30:  \
+		*blockptr = unigen_decomp_block_0x30;  \
+		break;  \
+	case 0xf9:  \
+		*blockptr = unigen_decomp_block_0xf9;  \
+		break;  \
+	case 0xfa:  \
+		*blockptr = unigen_decomp_block_0xfa;  \
+		break;  \
+	case 0xfb:  \
+		*blockptr = unigen_decomp_block_0xfb;  \
+		break;  \
+	case 0x2f8:  \
+		*blockptr = unigen_decomp_block_0x2f8;  \
+		break;  \
+	case 0x2f9:  \
+		*blockptr = unigen_decomp_block_0x2f9;  \
+		break;  \
+	case 0x2fa:  \
+		*blockptr = unigen_decomp_block_0x2fa;  \
+		break;  \
+	default:  \
+		*blockptr = nullptr;  \
+	}
 
 #define GET_DECOMP_SPECIAL(ch, countptr, posptr)  \
-switch (ch) {  \
-    case 0x622:  \
-        *countptr = 2; *posptr = 686;  \
-        break;  \
-    case 0x623:  \
-        *countptr = 2; *posptr = 688;  \
-        break;  \
-    case 0x624:  \
-        *countptr = 2; *posptr = 690;  \
-        break;  \
-    case 0x625:  \
-        *countptr = 2; *posptr = 692;  \
-        break;  \
-    case 0x626:  \
-        *countptr = 2; *posptr = 694;  \
-        break;  \
-    case 0x6c0:  \
-        *countptr = 2; *posptr = 696;  \
-        break;  \
-    case 0x6c2:  \
-        *countptr = 2; *posptr = 698;  \
-        break;  \
-    case 0x6d3:  \
-        *countptr = 2; *posptr = 700;  \
-        break;  \
-    case 0x929:  \
-        *countptr = 2; *posptr = 702;  \
-        break;  \
-    case 0x931:  \
-        *countptr = 2; *posptr = 704;  \
-        break;  \
-    case 0x934:  \
-        *countptr = 2; *posptr = 706;  \
-        break;  \
-    case 0x958:  \
-        *countptr = 2; *posptr = 708;  \
-        break;  \
-    case 0x959:  \
-        *countptr = 2; *posptr = 710;  \
-        break;  \
-    case 0x95a:  \
-        *countptr = 2; *posptr = 712;  \
-        break;  \
-    case 0x95b:  \
-        *countptr = 2; *posptr = 714;  \
-        break;  \
-    case 0x95c:  \
-        *countptr = 2; *posptr = 716;  \
-        break;  \
-    case 0x95d:  \
-        *countptr = 2; *posptr = 718;  \
-        break;  \
-    case 0x95e:  \
-        *countptr = 2; *posptr = 720;  \
-        break;  \
-    case 0x95f:  \
-        *countptr = 2; *posptr = 722;  \
-        break;  \
-    case 0x9cb:  \
-        *countptr = 2; *posptr = 724;  \
-        break;  \
-    case 0x9cc:  \
-        *countptr = 2; *posptr = 726;  \
-        break;  \
-    case 0x9dc:  \
-        *countptr = 2; *posptr = 728;  \
-        break;  \
-    case 0x9dd:  \
-        *countptr = 2; *posptr = 730;  \
-        break;  \
-    case 0x9df:  \
-        *countptr = 2; *posptr = 732;  \
-        break;  \
-    case 0xa33:  \
-        *countptr = 2; *posptr = 734;  \
-        break;  \
-    case 0xa36:  \
-        *countptr = 2; *posptr = 736;  \
-        break;  \
-    case 0xa59:  \
-        *countptr = 2; *posptr = 738;  \
-        break;  \
-    case 0xa5a:  \
-        *countptr = 2; *posptr = 740;  \
-        break;  \
-    case 0xa5b:  \
-        *countptr = 2; *posptr = 742;  \
-        break;  \
-    case 0xa5e:  \
-        *countptr = 2; *posptr = 744;  \
-        break;  \
-    case 0xb48:  \
-        *countptr = 2; *posptr = 746;  \
-        break;  \
-    case 0xb4b:  \
-        *countptr = 2; *posptr = 748;  \
-        break;  \
-    case 0xb4c:  \
-        *countptr = 2; *posptr = 750;  \
-        break;  \
-    case 0xb5c:  \
-        *countptr = 2; *posptr = 752;  \
-        break;  \
-    case 0xb5d:  \
-        *countptr = 2; *posptr = 754;  \
-        break;  \
-    case 0xb94:  \
-        *countptr = 2; *posptr = 756;  \
-        break;  \
-    case 0xbca:  \
-        *countptr = 2; *posptr = 758;  \
-        break;  \
-    case 0xbcb:  \
-        *countptr = 2; *posptr = 760;  \
-        break;  \
-    case 0xbcc:  \
-        *countptr = 2; *posptr = 762;  \
-        break;  \
-    case 0xc48:  \
-        *countptr = 2; *posptr = 764;  \
-        break;  \
-    case 0xcc0:  \
-        *countptr = 2; *posptr = 766;  \
-        break;  \
-    case 0xcc7:  \
-        *countptr = 2; *posptr = 768;  \
-        break;  \
-    case 0xcc8:  \
-        *countptr = 2; *posptr = 770;  \
-        break;  \
-    case 0xcca:  \
-        *countptr = 2; *posptr = 772;  \
-        break;  \
-    case 0xccb:  \
-        *countptr = 3; *posptr = 774;  \
-        break;  \
-    case 0xd4a:  \
-        *countptr = 2; *posptr = 777;  \
-        break;  \
-    case 0xd4b:  \
-        *countptr = 2; *posptr = 779;  \
-        break;  \
-    case 0xd4c:  \
-        *countptr = 2; *posptr = 781;  \
-        break;  \
-    case 0xdda:  \
-        *countptr = 2; *posptr = 783;  \
-        break;  \
-    case 0xddc:  \
-        *countptr = 2; *posptr = 785;  \
-        break;  \
-    case 0xddd:  \
-        *countptr = 3; *posptr = 787;  \
-        break;  \
-    case 0xdde:  \
-        *countptr = 2; *posptr = 790;  \
-        break;  \
-    case 0xf43:  \
-        *countptr = 2; *posptr = 792;  \
-        break;  \
-    case 0xf4d:  \
-        *countptr = 2; *posptr = 794;  \
-        break;  \
-    case 0xf52:  \
-        *countptr = 2; *posptr = 796;  \
-        break;  \
-    case 0xf57:  \
-        *countptr = 2; *posptr = 798;  \
-        break;  \
-    case 0xf5c:  \
-        *countptr = 2; *posptr = 800;  \
-        break;  \
-    case 0xf69:  \
-        *countptr = 2; *posptr = 802;  \
-        break;  \
-    case 0xf73:  \
-        *countptr = 2; *posptr = 804;  \
-        break;  \
-    case 0xf75:  \
-        *countptr = 2; *posptr = 806;  \
-        break;  \
-    case 0xf76:  \
-        *countptr = 2; *posptr = 808;  \
-        break;  \
-    case 0xf78:  \
-        *countptr = 2; *posptr = 810;  \
-        break;  \
-    case 0xf81:  \
-        *countptr = 2; *posptr = 812;  \
-        break;  \
-    case 0xf93:  \
-        *countptr = 2; *posptr = 814;  \
-        break;  \
-    case 0xf9d:  \
-        *countptr = 2; *posptr = 816;  \
-        break;  \
-    case 0xfa2:  \
-        *countptr = 2; *posptr = 818;  \
-        break;  \
-    case 0xfa7:  \
-        *countptr = 2; *posptr = 820;  \
-        break;  \
-    case 0xfac:  \
-        *countptr = 2; *posptr = 822;  \
-        break;  \
-    case 0xfb9:  \
-        *countptr = 2; *posptr = 824;  \
-        break;  \
-    case 0x1026:  \
-        *countptr = 2; *posptr = 826;  \
-        break;  \
-    case 0x2000:  \
-        *countptr = 1; *posptr = 2037;  \
-        break;  \
-    case 0x2001:  \
-        *countptr = 1; *posptr = 2038;  \
-        break;  \
-    case 0x2126:  \
-        *countptr = 1; *posptr = 2039;  \
-        break;  \
-    case 0x212a:  \
-        *countptr = 1; *posptr = 2040;  \
-        break;  \
-    case 0x212b:  \
-        *countptr = 2; *posptr = 2041;  \
-        break;  \
-    case 0x219a:  \
-        *countptr = 2; *posptr = 2043;  \
-        break;  \
-    case 0x219b:  \
-        *countptr = 2; *posptr = 2045;  \
-        break;  \
-    case 0x21ae:  \
-        *countptr = 2; *posptr = 2047;  \
-        break;  \
-    case 0x21cd:  \
-        *countptr = 2; *posptr = 2049;  \
-        break;  \
-    case 0x21ce:  \
-        *countptr = 2; *posptr = 2051;  \
-        break;  \
-    case 0x21cf:  \
-        *countptr = 2; *posptr = 2053;  \
-        break;  \
-    case 0x2329:  \
-        *countptr = 1; *posptr = 2131;  \
-        break;  \
-    case 0x232a:  \
-        *countptr = 1; *posptr = 2132;  \
-        break;  \
-    case 0x2adc:  \
-        *countptr = 2; *posptr = 2133;  \
-        break;  \
-    case 0x1d15e:  \
-        *countptr = 2; *posptr = 2670;  \
-        break;  \
-    case 0x1d15f:  \
-        *countptr = 2; *posptr = 2672;  \
-        break;  \
-    case 0x1d160:  \
-        *countptr = 3; *posptr = 2674;  \
-        break;  \
-    case 0x1d161:  \
-        *countptr = 3; *posptr = 2677;  \
-        break;  \
-    case 0x1d162:  \
-        *countptr = 3; *posptr = 2680;  \
-        break;  \
-    case 0x1d163:  \
-        *countptr = 3; *posptr = 2683;  \
-        break;  \
-    case 0x1d164:  \
-        *countptr = 3; *posptr = 2686;  \
-        break;  \
-    case 0x1d1bb:  \
-        *countptr = 2; *posptr = 2689;  \
-        break;  \
-    case 0x1d1bc:  \
-        *countptr = 2; *posptr = 2691;  \
-        break;  \
-    case 0x1d1bd:  \
-        *countptr = 3; *posptr = 2693;  \
-        break;  \
-    case 0x1d1be:  \
-        *countptr = 3; *posptr = 2696;  \
-        break;  \
-    case 0x1d1bf:  \
-        *countptr = 3; *posptr = 2699;  \
-        break;  \
-    case 0x1d1c0:  \
-        *countptr = 3; *posptr = 2702;  \
-        break;  \
-    default:  \
-        *countptr = 0;  \
-}
+	switch (ch) {  \
+	case 0x622:  \
+		*countptr = 2; *posptr = 686;  \
+		break;  \
+	case 0x623:  \
+		*countptr = 2; *posptr = 688;  \
+		break;  \
+	case 0x624:  \
+		*countptr = 2; *posptr = 690;  \
+		break;  \
+	case 0x625:  \
+		*countptr = 2; *posptr = 692;  \
+		break;  \
+	case 0x626:  \
+		*countptr = 2; *posptr = 694;  \
+		break;  \
+	case 0x6c0:  \
+		*countptr = 2; *posptr = 696;  \
+		break;  \
+	case 0x6c2:  \
+		*countptr = 2; *posptr = 698;  \
+		break;  \
+	case 0x6d3:  \
+		*countptr = 2; *posptr = 700;  \
+		break;  \
+	case 0x929:  \
+		*countptr = 2; *posptr = 702;  \
+		break;  \
+	case 0x931:  \
+		*countptr = 2; *posptr = 704;  \
+		break;  \
+	case 0x934:  \
+		*countptr = 2; *posptr = 706;  \
+		break;  \
+	case 0x958:  \
+		*countptr = 2; *posptr = 708;  \
+		break;  \
+	case 0x959:  \
+		*countptr = 2; *posptr = 710;  \
+		break;  \
+	case 0x95a:  \
+		*countptr = 2; *posptr = 712;  \
+		break;  \
+	case 0x95b:  \
+		*countptr = 2; *posptr = 714;  \
+		break;  \
+	case 0x95c:  \
+		*countptr = 2; *posptr = 716;  \
+		break;  \
+	case 0x95d:  \
+		*countptr = 2; *posptr = 718;  \
+		break;  \
+	case 0x95e:  \
+		*countptr = 2; *posptr = 720;  \
+		break;  \
+	case 0x95f:  \
+		*countptr = 2; *posptr = 722;  \
+		break;  \
+	case 0x9cb:  \
+		*countptr = 2; *posptr = 724;  \
+		break;  \
+	case 0x9cc:  \
+		*countptr = 2; *posptr = 726;  \
+		break;  \
+	case 0x9dc:  \
+		*countptr = 2; *posptr = 728;  \
+		break;  \
+	case 0x9dd:  \
+		*countptr = 2; *posptr = 730;  \
+		break;  \
+	case 0x9df:  \
+		*countptr = 2; *posptr = 732;  \
+		break;  \
+	case 0xa33:  \
+		*countptr = 2; *posptr = 734;  \
+		break;  \
+	case 0xa36:  \
+		*countptr = 2; *posptr = 736;  \
+		break;  \
+	case 0xa59:  \
+		*countptr = 2; *posptr = 738;  \
+		break;  \
+	case 0xa5a:  \
+		*countptr = 2; *posptr = 740;  \
+		break;  \
+	case 0xa5b:  \
+		*countptr = 2; *posptr = 742;  \
+		break;  \
+	case 0xa5e:  \
+		*countptr = 2; *posptr = 744;  \
+		break;  \
+	case 0xb48:  \
+		*countptr = 2; *posptr = 746;  \
+		break;  \
+	case 0xb4b:  \
+		*countptr = 2; *posptr = 748;  \
+		break;  \
+	case 0xb4c:  \
+		*countptr = 2; *posptr = 750;  \
+		break;  \
+	case 0xb5c:  \
+		*countptr = 2; *posptr = 752;  \
+		break;  \
+	case 0xb5d:  \
+		*countptr = 2; *posptr = 754;  \
+		break;  \
+	case 0xb94:  \
+		*countptr = 2; *posptr = 756;  \
+		break;  \
+	case 0xbca:  \
+		*countptr = 2; *posptr = 758;  \
+		break;  \
+	case 0xbcb:  \
+		*countptr = 2; *posptr = 760;  \
+		break;  \
+	case 0xbcc:  \
+		*countptr = 2; *posptr = 762;  \
+		break;  \
+	case 0xc48:  \
+		*countptr = 2; *posptr = 764;  \
+		break;  \
+	case 0xcc0:  \
+		*countptr = 2; *posptr = 766;  \
+		break;  \
+	case 0xcc7:  \
+		*countptr = 2; *posptr = 768;  \
+		break;  \
+	case 0xcc8:  \
+		*countptr = 2; *posptr = 770;  \
+		break;  \
+	case 0xcca:  \
+		*countptr = 2; *posptr = 772;  \
+		break;  \
+	case 0xccb:  \
+		*countptr = 3; *posptr = 774;  \
+		break;  \
+	case 0xd4a:  \
+		*countptr = 2; *posptr = 777;  \
+		break;  \
+	case 0xd4b:  \
+		*countptr = 2; *posptr = 779;  \
+		break;  \
+	case 0xd4c:  \
+		*countptr = 2; *posptr = 781;  \
+		break;  \
+	case 0xdda:  \
+		*countptr = 2; *posptr = 783;  \
+		break;  \
+	case 0xddc:  \
+		*countptr = 2; *posptr = 785;  \
+		break;  \
+	case 0xddd:  \
+		*countptr = 3; *posptr = 787;  \
+		break;  \
+	case 0xdde:  \
+		*countptr = 2; *posptr = 790;  \
+		break;  \
+	case 0xf43:  \
+		*countptr = 2; *posptr = 792;  \
+		break;  \
+	case 0xf4d:  \
+		*countptr = 2; *posptr = 794;  \
+		break;  \
+	case 0xf52:  \
+		*countptr = 2; *posptr = 796;  \
+		break;  \
+	case 0xf57:  \
+		*countptr = 2; *posptr = 798;  \
+		break;  \
+	case 0xf5c:  \
+		*countptr = 2; *posptr = 800;  \
+		break;  \
+	case 0xf69:  \
+		*countptr = 2; *posptr = 802;  \
+		break;  \
+	case 0xf73:  \
+		*countptr = 2; *posptr = 804;  \
+		break;  \
+	case 0xf75:  \
+		*countptr = 2; *posptr = 806;  \
+		break;  \
+	case 0xf76:  \
+		*countptr = 2; *posptr = 808;  \
+		break;  \
+	case 0xf78:  \
+		*countptr = 2; *posptr = 810;  \
+		break;  \
+	case 0xf81:  \
+		*countptr = 2; *posptr = 812;  \
+		break;  \
+	case 0xf93:  \
+		*countptr = 2; *posptr = 814;  \
+		break;  \
+	case 0xf9d:  \
+		*countptr = 2; *posptr = 816;  \
+		break;  \
+	case 0xfa2:  \
+		*countptr = 2; *posptr = 818;  \
+		break;  \
+	case 0xfa7:  \
+		*countptr = 2; *posptr = 820;  \
+		break;  \
+	case 0xfac:  \
+		*countptr = 2; *posptr = 822;  \
+		break;  \
+	case 0xfb9:  \
+		*countptr = 2; *posptr = 824;  \
+		break;  \
+	case 0x1026:  \
+		*countptr = 2; *posptr = 826;  \
+		break;  \
+	case 0x2000:  \
+		*countptr = 1; *posptr = 2037;  \
+		break;  \
+	case 0x2001:  \
+		*countptr = 1; *posptr = 2038;  \
+		break;  \
+	case 0x2126:  \
+		*countptr = 1; *posptr = 2039;  \
+		break;  \
+	case 0x212a:  \
+		*countptr = 1; *posptr = 2040;  \
+		break;  \
+	case 0x212b:  \
+		*countptr = 2; *posptr = 2041;  \
+		break;  \
+	case 0x219a:  \
+		*countptr = 2; *posptr = 2043;  \
+		break;  \
+	case 0x219b:  \
+		*countptr = 2; *posptr = 2045;  \
+		break;  \
+	case 0x21ae:  \
+		*countptr = 2; *posptr = 2047;  \
+		break;  \
+	case 0x21cd:  \
+		*countptr = 2; *posptr = 2049;  \
+		break;  \
+	case 0x21ce:  \
+		*countptr = 2; *posptr = 2051;  \
+		break;  \
+	case 0x21cf:  \
+		*countptr = 2; *posptr = 2053;  \
+		break;  \
+	case 0x2329:  \
+		*countptr = 1; *posptr = 2131;  \
+		break;  \
+	case 0x232a:  \
+		*countptr = 1; *posptr = 2132;  \
+		break;  \
+	case 0x2adc:  \
+		*countptr = 2; *posptr = 2133;  \
+		break;  \
+	case 0x1d15e:  \
+		*countptr = 2; *posptr = 2670;  \
+		break;  \
+	case 0x1d15f:  \
+		*countptr = 2; *posptr = 2672;  \
+		break;  \
+	case 0x1d160:  \
+		*countptr = 3; *posptr = 2674;  \
+		break;  \
+	case 0x1d161:  \
+		*countptr = 3; *posptr = 2677;  \
+		break;  \
+	case 0x1d162:  \
+		*countptr = 3; *posptr = 2680;  \
+		break;  \
+	case 0x1d163:  \
+		*countptr = 3; *posptr = 2683;  \
+		break;  \
+	case 0x1d164:  \
+		*countptr = 3; *posptr = 2686;  \
+		break;  \
+	case 0x1d1bb:  \
+		*countptr = 2; *posptr = 2689;  \
+		break;  \
+	case 0x1d1bc:  \
+		*countptr = 2; *posptr = 2691;  \
+		break;  \
+	case 0x1d1bd:  \
+		*countptr = 3; *posptr = 2693;  \
+		break;  \
+	case 0x1d1be:  \
+		*countptr = 3; *posptr = 2696;  \
+		break;  \
+	case 0x1d1bf:  \
+		*countptr = 3; *posptr = 2699;  \
+		break;  \
+	case 0x1d1c0:  \
+		*countptr = 3; *posptr = 2702;  \
+		break;  \
+	default:  \
+		*countptr = 0;  \
+	}
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/unicode_gen.h b/engines/gargoyle/unicode_gen.h
index 1539ec4..7c86e9b 100644
--- a/engines/gargoyle/unicode_gen.h
+++ b/engines/gargoyle/unicode_gen.h
@@ -28,400 +28,400 @@
 namespace Gargoyle {
 
 #define GET_CASE_BLOCK(ch, blockptr)  \
-switch ((glui32)(ch) >> 8) {  \
-case 0x0:  \
-    *blockptr = unigen_case_block_0x0;  \
-    break;  \
-case 0x1:  \
-    *blockptr = unigen_case_block_0x1;  \
-    break;  \
-case 0x2:  \
-    *blockptr = unigen_case_block_0x2;  \
-    break;  \
-case 0x3:  \
-    *blockptr = unigen_case_block_0x3;  \
-    break;  \
-case 0x4:  \
-    *blockptr = unigen_case_block_0x4;  \
-    break;  \
-case 0x5:  \
-    *blockptr = unigen_case_block_0x5;  \
-    break;  \
-case 0x1e:  \
-    *blockptr = unigen_case_block_0x1e;  \
-    break;  \
-case 0x1f:  \
-    *blockptr = unigen_case_block_0x1f;  \
-    break;  \
-case 0x21:  \
-    *blockptr = unigen_case_block_0x21;  \
-    break;  \
-case 0x24:  \
-    *blockptr = unigen_case_block_0x24;  \
-    break;  \
-case 0xfb:  \
-    *blockptr = unigen_case_block_0xfb;  \
-    break;  \
-case 0xff:  \
-    *blockptr = unigen_case_block_0xff;  \
-    break;  \
-case 0x104:  \
-    *blockptr = unigen_case_block_0x104;  \
-    break;  \
-default:  \
-    *blockptr = nullptr;  \
-}
+	switch ((glui32)(ch) >> 8) {  \
+	case 0x0:  \
+		*blockptr = unigen_case_block_0x0;  \
+		break;  \
+	case 0x1:  \
+		*blockptr = unigen_case_block_0x1;  \
+		break;  \
+	case 0x2:  \
+		*blockptr = unigen_case_block_0x2;  \
+		break;  \
+	case 0x3:  \
+		*blockptr = unigen_case_block_0x3;  \
+		break;  \
+	case 0x4:  \
+		*blockptr = unigen_case_block_0x4;  \
+		break;  \
+	case 0x5:  \
+		*blockptr = unigen_case_block_0x5;  \
+		break;  \
+	case 0x1e:  \
+		*blockptr = unigen_case_block_0x1e;  \
+		break;  \
+	case 0x1f:  \
+		*blockptr = unigen_case_block_0x1f;  \
+		break;  \
+	case 0x21:  \
+		*blockptr = unigen_case_block_0x21;  \
+		break;  \
+	case 0x24:  \
+		*blockptr = unigen_case_block_0x24;  \
+		break;  \
+	case 0xfb:  \
+		*blockptr = unigen_case_block_0xfb;  \
+		break;  \
+	case 0xff:  \
+		*blockptr = unigen_case_block_0xff;  \
+		break;  \
+	case 0x104:  \
+		*blockptr = unigen_case_block_0x104;  \
+		break;  \
+	default:  \
+		*blockptr = nullptr;  \
+	}
 
 #define GET_CASE_SPECIAL(ch, specptr)  \
-switch (ch) {  \
-case 0xdf:  \
-    *specptr = unigen_special_0xdf;  \
-    break;  \
-case 0x130:  \
-    *specptr = unigen_special_0x130;  \
-    break;  \
-case 0x149:  \
-    *specptr = unigen_special_0x149;  \
-    break;  \
-case 0x1c4:  \
-    *specptr = unigen_special_0x1c4;  \
-    break;  \
-case 0x1c5:  \
-    *specptr = unigen_special_0x1c5;  \
-    break;  \
-case 0x1c6:  \
-    *specptr = unigen_special_0x1c6;  \
-    break;  \
-case 0x1c7:  \
-    *specptr = unigen_special_0x1c7;  \
-    break;  \
-case 0x1c8:  \
-    *specptr = unigen_special_0x1c8;  \
-    break;  \
-case 0x1c9:  \
-    *specptr = unigen_special_0x1c9;  \
-    break;  \
-case 0x1ca:  \
-    *specptr = unigen_special_0x1ca;  \
-    break;  \
-case 0x1cb:  \
-    *specptr = unigen_special_0x1cb;  \
-    break;  \
-case 0x1cc:  \
-    *specptr = unigen_special_0x1cc;  \
-    break;  \
-case 0x1f0:  \
-    *specptr = unigen_special_0x1f0;  \
-    break;  \
-case 0x1f1:  \
-    *specptr = unigen_special_0x1f1;  \
-    break;  \
-case 0x1f2:  \
-    *specptr = unigen_special_0x1f2;  \
-    break;  \
-case 0x1f3:  \
-    *specptr = unigen_special_0x1f3;  \
-    break;  \
-case 0x390:  \
-    *specptr = unigen_special_0x390;  \
-    break;  \
-case 0x3b0:  \
-    *specptr = unigen_special_0x3b0;  \
-    break;  \
-case 0x587:  \
-    *specptr = unigen_special_0x587;  \
-    break;  \
-case 0x1e96:  \
-    *specptr = unigen_special_0x1e96;  \
-    break;  \
-case 0x1e97:  \
-    *specptr = unigen_special_0x1e97;  \
-    break;  \
-case 0x1e98:  \
-    *specptr = unigen_special_0x1e98;  \
-    break;  \
-case 0x1e99:  \
-    *specptr = unigen_special_0x1e99;  \
-    break;  \
-case 0x1e9a:  \
-    *specptr = unigen_special_0x1e9a;  \
-    break;  \
-case 0x1f50:  \
-    *specptr = unigen_special_0x1f50;  \
-    break;  \
-case 0x1f52:  \
-    *specptr = unigen_special_0x1f52;  \
-    break;  \
-case 0x1f54:  \
-    *specptr = unigen_special_0x1f54;  \
-    break;  \
-case 0x1f56:  \
-    *specptr = unigen_special_0x1f56;  \
-    break;  \
-case 0x1f80:  \
-    *specptr = unigen_special_0x1f80;  \
-    break;  \
-case 0x1f81:  \
-    *specptr = unigen_special_0x1f81;  \
-    break;  \
-case 0x1f82:  \
-    *specptr = unigen_special_0x1f82;  \
-    break;  \
-case 0x1f83:  \
-    *specptr = unigen_special_0x1f83;  \
-    break;  \
-case 0x1f84:  \
-    *specptr = unigen_special_0x1f84;  \
-    break;  \
-case 0x1f85:  \
-    *specptr = unigen_special_0x1f85;  \
-    break;  \
-case 0x1f86:  \
-    *specptr = unigen_special_0x1f86;  \
-    break;  \
-case 0x1f87:  \
-    *specptr = unigen_special_0x1f87;  \
-    break;  \
-case 0x1f88:  \
-    *specptr = unigen_special_0x1f88;  \
-    break;  \
-case 0x1f89:  \
-    *specptr = unigen_special_0x1f89;  \
-    break;  \
-case 0x1f8a:  \
-    *specptr = unigen_special_0x1f8a;  \
-    break;  \
-case 0x1f8b:  \
-    *specptr = unigen_special_0x1f8b;  \
-    break;  \
-case 0x1f8c:  \
-    *specptr = unigen_special_0x1f8c;  \
-    break;  \
-case 0x1f8d:  \
-    *specptr = unigen_special_0x1f8d;  \
-    break;  \
-case 0x1f8e:  \
-    *specptr = unigen_special_0x1f8e;  \
-    break;  \
-case 0x1f8f:  \
-    *specptr = unigen_special_0x1f8f;  \
-    break;  \
-case 0x1f90:  \
-    *specptr = unigen_special_0x1f90;  \
-    break;  \
-case 0x1f91:  \
-    *specptr = unigen_special_0x1f91;  \
-    break;  \
-case 0x1f92:  \
-    *specptr = unigen_special_0x1f92;  \
-    break;  \
-case 0x1f93:  \
-    *specptr = unigen_special_0x1f93;  \
-    break;  \
-case 0x1f94:  \
-    *specptr = unigen_special_0x1f94;  \
-    break;  \
-case 0x1f95:  \
-    *specptr = unigen_special_0x1f95;  \
-    break;  \
-case 0x1f96:  \
-    *specptr = unigen_special_0x1f96;  \
-    break;  \
-case 0x1f97:  \
-    *specptr = unigen_special_0x1f97;  \
-    break;  \
-case 0x1f98:  \
-    *specptr = unigen_special_0x1f98;  \
-    break;  \
-case 0x1f99:  \
-    *specptr = unigen_special_0x1f99;  \
-    break;  \
-case 0x1f9a:  \
-    *specptr = unigen_special_0x1f9a;  \
-    break;  \
-case 0x1f9b:  \
-    *specptr = unigen_special_0x1f9b;  \
-    break;  \
-case 0x1f9c:  \
-    *specptr = unigen_special_0x1f9c;  \
-    break;  \
-case 0x1f9d:  \
-    *specptr = unigen_special_0x1f9d;  \
-    break;  \
-case 0x1f9e:  \
-    *specptr = unigen_special_0x1f9e;  \
-    break;  \
-case 0x1f9f:  \
-    *specptr = unigen_special_0x1f9f;  \
-    break;  \
-case 0x1fa0:  \
-    *specptr = unigen_special_0x1fa0;  \
-    break;  \
-case 0x1fa1:  \
-    *specptr = unigen_special_0x1fa1;  \
-    break;  \
-case 0x1fa2:  \
-    *specptr = unigen_special_0x1fa2;  \
-    break;  \
-case 0x1fa3:  \
-    *specptr = unigen_special_0x1fa3;  \
-    break;  \
-case 0x1fa4:  \
-    *specptr = unigen_special_0x1fa4;  \
-    break;  \
-case 0x1fa5:  \
-    *specptr = unigen_special_0x1fa5;  \
-    break;  \
-case 0x1fa6:  \
-    *specptr = unigen_special_0x1fa6;  \
-    break;  \
-case 0x1fa7:  \
-    *specptr = unigen_special_0x1fa7;  \
-    break;  \
-case 0x1fa8:  \
-    *specptr = unigen_special_0x1fa8;  \
-    break;  \
-case 0x1fa9:  \
-    *specptr = unigen_special_0x1fa9;  \
-    break;  \
-case 0x1faa:  \
-    *specptr = unigen_special_0x1faa;  \
-    break;  \
-case 0x1fab:  \
-    *specptr = unigen_special_0x1fab;  \
-    break;  \
-case 0x1fac:  \
-    *specptr = unigen_special_0x1fac;  \
-    break;  \
-case 0x1fad:  \
-    *specptr = unigen_special_0x1fad;  \
-    break;  \
-case 0x1fae:  \
-    *specptr = unigen_special_0x1fae;  \
-    break;  \
-case 0x1faf:  \
-    *specptr = unigen_special_0x1faf;  \
-    break;  \
-case 0x1fb2:  \
-    *specptr = unigen_special_0x1fb2;  \
-    break;  \
-case 0x1fb3:  \
-    *specptr = unigen_special_0x1fb3;  \
-    break;  \
-case 0x1fb4:  \
-    *specptr = unigen_special_0x1fb4;  \
-    break;  \
-case 0x1fb6:  \
-    *specptr = unigen_special_0x1fb6;  \
-    break;  \
-case 0x1fb7:  \
-    *specptr = unigen_special_0x1fb7;  \
-    break;  \
-case 0x1fbc:  \
-    *specptr = unigen_special_0x1fbc;  \
-    break;  \
-case 0x1fc2:  \
-    *specptr = unigen_special_0x1fc2;  \
-    break;  \
-case 0x1fc3:  \
-    *specptr = unigen_special_0x1fc3;  \
-    break;  \
-case 0x1fc4:  \
-    *specptr = unigen_special_0x1fc4;  \
-    break;  \
-case 0x1fc6:  \
-    *specptr = unigen_special_0x1fc6;  \
-    break;  \
-case 0x1fc7:  \
-    *specptr = unigen_special_0x1fc7;  \
-    break;  \
-case 0x1fcc:  \
-    *specptr = unigen_special_0x1fcc;  \
-    break;  \
-case 0x1fd2:  \
-    *specptr = unigen_special_0x1fd2;  \
-    break;  \
-case 0x1fd3:  \
-    *specptr = unigen_special_0x1fd3;  \
-    break;  \
-case 0x1fd6:  \
-    *specptr = unigen_special_0x1fd6;  \
-    break;  \
-case 0x1fd7:  \
-    *specptr = unigen_special_0x1fd7;  \
-    break;  \
-case 0x1fe2:  \
-    *specptr = unigen_special_0x1fe2;  \
-    break;  \
-case 0x1fe3:  \
-    *specptr = unigen_special_0x1fe3;  \
-    break;  \
-case 0x1fe4:  \
-    *specptr = unigen_special_0x1fe4;  \
-    break;  \
-case 0x1fe6:  \
-    *specptr = unigen_special_0x1fe6;  \
-    break;  \
-case 0x1fe7:  \
-    *specptr = unigen_special_0x1fe7;  \
-    break;  \
-case 0x1ff2:  \
-    *specptr = unigen_special_0x1ff2;  \
-    break;  \
-case 0x1ff3:  \
-    *specptr = unigen_special_0x1ff3;  \
-    break;  \
-case 0x1ff4:  \
-    *specptr = unigen_special_0x1ff4;  \
-    break;  \
-case 0x1ff6:  \
-    *specptr = unigen_special_0x1ff6;  \
-    break;  \
-case 0x1ff7:  \
-    *specptr = unigen_special_0x1ff7;  \
-    break;  \
-case 0x1ffc:  \
-    *specptr = unigen_special_0x1ffc;  \
-    break;  \
-case 0xfb00:  \
-    *specptr = unigen_special_0xfb00;  \
-    break;  \
-case 0xfb01:  \
-    *specptr = unigen_special_0xfb01;  \
-    break;  \
-case 0xfb02:  \
-    *specptr = unigen_special_0xfb02;  \
-    break;  \
-case 0xfb03:  \
-    *specptr = unigen_special_0xfb03;  \
-    break;  \
-case 0xfb04:  \
-    *specptr = unigen_special_0xfb04;  \
-    break;  \
-case 0xfb05:  \
-    *specptr = unigen_special_0xfb05;  \
-    break;  \
-case 0xfb06:  \
-    *specptr = unigen_special_0xfb06;  \
-    break;  \
-case 0xfb13:  \
-    *specptr = unigen_special_0xfb13;  \
-    break;  \
-case 0xfb14:  \
-    *specptr = unigen_special_0xfb14;  \
-    break;  \
-case 0xfb15:  \
-    *specptr = unigen_special_0xfb15;  \
-    break;  \
-case 0xfb16:  \
-    *specptr = unigen_special_0xfb16;  \
-    break;  \
-case 0xfb17:  \
-    *specptr = unigen_special_0xfb17;  \
-    break;  \
-default:  \
-    *specptr = nullptr;  \
-}
+	switch (ch) {  \
+	case 0xdf:  \
+		*specptr = unigen_special_0xdf;  \
+		break;  \
+	case 0x130:  \
+		*specptr = unigen_special_0x130;  \
+		break;  \
+	case 0x149:  \
+		*specptr = unigen_special_0x149;  \
+		break;  \
+	case 0x1c4:  \
+		*specptr = unigen_special_0x1c4;  \
+		break;  \
+	case 0x1c5:  \
+		*specptr = unigen_special_0x1c5;  \
+		break;  \
+	case 0x1c6:  \
+		*specptr = unigen_special_0x1c6;  \
+		break;  \
+	case 0x1c7:  \
+		*specptr = unigen_special_0x1c7;  \
+		break;  \
+	case 0x1c8:  \
+		*specptr = unigen_special_0x1c8;  \
+		break;  \
+	case 0x1c9:  \
+		*specptr = unigen_special_0x1c9;  \
+		break;  \
+	case 0x1ca:  \
+		*specptr = unigen_special_0x1ca;  \
+		break;  \
+	case 0x1cb:  \
+		*specptr = unigen_special_0x1cb;  \
+		break;  \
+	case 0x1cc:  \
+		*specptr = unigen_special_0x1cc;  \
+		break;  \
+	case 0x1f0:  \
+		*specptr = unigen_special_0x1f0;  \
+		break;  \
+	case 0x1f1:  \
+		*specptr = unigen_special_0x1f1;  \
+		break;  \
+	case 0x1f2:  \
+		*specptr = unigen_special_0x1f2;  \
+		break;  \
+	case 0x1f3:  \
+		*specptr = unigen_special_0x1f3;  \
+		break;  \
+	case 0x390:  \
+		*specptr = unigen_special_0x390;  \
+		break;  \
+	case 0x3b0:  \
+		*specptr = unigen_special_0x3b0;  \
+		break;  \
+	case 0x587:  \
+		*specptr = unigen_special_0x587;  \
+		break;  \
+	case 0x1e96:  \
+		*specptr = unigen_special_0x1e96;  \
+		break;  \
+	case 0x1e97:  \
+		*specptr = unigen_special_0x1e97;  \
+		break;  \
+	case 0x1e98:  \
+		*specptr = unigen_special_0x1e98;  \
+		break;  \
+	case 0x1e99:  \
+		*specptr = unigen_special_0x1e99;  \
+		break;  \
+	case 0x1e9a:  \
+		*specptr = unigen_special_0x1e9a;  \
+		break;  \
+	case 0x1f50:  \
+		*specptr = unigen_special_0x1f50;  \
+		break;  \
+	case 0x1f52:  \
+		*specptr = unigen_special_0x1f52;  \
+		break;  \
+	case 0x1f54:  \
+		*specptr = unigen_special_0x1f54;  \
+		break;  \
+	case 0x1f56:  \
+		*specptr = unigen_special_0x1f56;  \
+		break;  \
+	case 0x1f80:  \
+		*specptr = unigen_special_0x1f80;  \
+		break;  \
+	case 0x1f81:  \
+		*specptr = unigen_special_0x1f81;  \
+		break;  \
+	case 0x1f82:  \
+		*specptr = unigen_special_0x1f82;  \
+		break;  \
+	case 0x1f83:  \
+		*specptr = unigen_special_0x1f83;  \
+		break;  \
+	case 0x1f84:  \
+		*specptr = unigen_special_0x1f84;  \
+		break;  \
+	case 0x1f85:  \
+		*specptr = unigen_special_0x1f85;  \
+		break;  \
+	case 0x1f86:  \
+		*specptr = unigen_special_0x1f86;  \
+		break;  \
+	case 0x1f87:  \
+		*specptr = unigen_special_0x1f87;  \
+		break;  \
+	case 0x1f88:  \
+		*specptr = unigen_special_0x1f88;  \
+		break;  \
+	case 0x1f89:  \
+		*specptr = unigen_special_0x1f89;  \
+		break;  \
+	case 0x1f8a:  \
+		*specptr = unigen_special_0x1f8a;  \
+		break;  \
+	case 0x1f8b:  \
+		*specptr = unigen_special_0x1f8b;  \
+		break;  \
+	case 0x1f8c:  \
+		*specptr = unigen_special_0x1f8c;  \
+		break;  \
+	case 0x1f8d:  \
+		*specptr = unigen_special_0x1f8d;  \
+		break;  \
+	case 0x1f8e:  \
+		*specptr = unigen_special_0x1f8e;  \
+		break;  \
+	case 0x1f8f:  \
+		*specptr = unigen_special_0x1f8f;  \
+		break;  \
+	case 0x1f90:  \
+		*specptr = unigen_special_0x1f90;  \
+		break;  \
+	case 0x1f91:  \
+		*specptr = unigen_special_0x1f91;  \
+		break;  \
+	case 0x1f92:  \
+		*specptr = unigen_special_0x1f92;  \
+		break;  \
+	case 0x1f93:  \
+		*specptr = unigen_special_0x1f93;  \
+		break;  \
+	case 0x1f94:  \
+		*specptr = unigen_special_0x1f94;  \
+		break;  \
+	case 0x1f95:  \
+		*specptr = unigen_special_0x1f95;  \
+		break;  \
+	case 0x1f96:  \
+		*specptr = unigen_special_0x1f96;  \
+		break;  \
+	case 0x1f97:  \
+		*specptr = unigen_special_0x1f97;  \
+		break;  \
+	case 0x1f98:  \
+		*specptr = unigen_special_0x1f98;  \
+		break;  \
+	case 0x1f99:  \
+		*specptr = unigen_special_0x1f99;  \
+		break;  \
+	case 0x1f9a:  \
+		*specptr = unigen_special_0x1f9a;  \
+		break;  \
+	case 0x1f9b:  \
+		*specptr = unigen_special_0x1f9b;  \
+		break;  \
+	case 0x1f9c:  \
+		*specptr = unigen_special_0x1f9c;  \
+		break;  \
+	case 0x1f9d:  \
+		*specptr = unigen_special_0x1f9d;  \
+		break;  \
+	case 0x1f9e:  \
+		*specptr = unigen_special_0x1f9e;  \
+		break;  \
+	case 0x1f9f:  \
+		*specptr = unigen_special_0x1f9f;  \
+		break;  \
+	case 0x1fa0:  \
+		*specptr = unigen_special_0x1fa0;  \
+		break;  \
+	case 0x1fa1:  \
+		*specptr = unigen_special_0x1fa1;  \
+		break;  \
+	case 0x1fa2:  \
+		*specptr = unigen_special_0x1fa2;  \
+		break;  \
+	case 0x1fa3:  \
+		*specptr = unigen_special_0x1fa3;  \
+		break;  \
+	case 0x1fa4:  \
+		*specptr = unigen_special_0x1fa4;  \
+		break;  \
+	case 0x1fa5:  \
+		*specptr = unigen_special_0x1fa5;  \
+		break;  \
+	case 0x1fa6:  \
+		*specptr = unigen_special_0x1fa6;  \
+		break;  \
+	case 0x1fa7:  \
+		*specptr = unigen_special_0x1fa7;  \
+		break;  \
+	case 0x1fa8:  \
+		*specptr = unigen_special_0x1fa8;  \
+		break;  \
+	case 0x1fa9:  \
+		*specptr = unigen_special_0x1fa9;  \
+		break;  \
+	case 0x1faa:  \
+		*specptr = unigen_special_0x1faa;  \
+		break;  \
+	case 0x1fab:  \
+		*specptr = unigen_special_0x1fab;  \
+		break;  \
+	case 0x1fac:  \
+		*specptr = unigen_special_0x1fac;  \
+		break;  \
+	case 0x1fad:  \
+		*specptr = unigen_special_0x1fad;  \
+		break;  \
+	case 0x1fae:  \
+		*specptr = unigen_special_0x1fae;  \
+		break;  \
+	case 0x1faf:  \
+		*specptr = unigen_special_0x1faf;  \
+		break;  \
+	case 0x1fb2:  \
+		*specptr = unigen_special_0x1fb2;  \
+		break;  \
+	case 0x1fb3:  \
+		*specptr = unigen_special_0x1fb3;  \
+		break;  \
+	case 0x1fb4:  \
+		*specptr = unigen_special_0x1fb4;  \
+		break;  \
+	case 0x1fb6:  \
+		*specptr = unigen_special_0x1fb6;  \
+		break;  \
+	case 0x1fb7:  \
+		*specptr = unigen_special_0x1fb7;  \
+		break;  \
+	case 0x1fbc:  \
+		*specptr = unigen_special_0x1fbc;  \
+		break;  \
+	case 0x1fc2:  \
+		*specptr = unigen_special_0x1fc2;  \
+		break;  \
+	case 0x1fc3:  \
+		*specptr = unigen_special_0x1fc3;  \
+		break;  \
+	case 0x1fc4:  \
+		*specptr = unigen_special_0x1fc4;  \
+		break;  \
+	case 0x1fc6:  \
+		*specptr = unigen_special_0x1fc6;  \
+		break;  \
+	case 0x1fc7:  \
+		*specptr = unigen_special_0x1fc7;  \
+		break;  \
+	case 0x1fcc:  \
+		*specptr = unigen_special_0x1fcc;  \
+		break;  \
+	case 0x1fd2:  \
+		*specptr = unigen_special_0x1fd2;  \
+		break;  \
+	case 0x1fd3:  \
+		*specptr = unigen_special_0x1fd3;  \
+		break;  \
+	case 0x1fd6:  \
+		*specptr = unigen_special_0x1fd6;  \
+		break;  \
+	case 0x1fd7:  \


Commit: a698756c24c50059b84b6ca8833530b5f8cfef59
    https://github.com/scummvm/scummvm/commit/a698756c24c50059b84b6ca8833530b5f8cfef59
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Don't show intro text when loading savegame from launcher

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


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 329cfb3..001eff5 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -29,7 +29,7 @@ namespace Scott {
 Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
 	Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
 	Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
-	split_screen(true), Bottom(0), Top(0), BitFlags(0) {
+	split_screen(true), Bottom(0), Top(0), BitFlags(0), _saveSlot(-1) {
 	Common::fill(&NounText[0], &NounText[16], '\0');
 	Common::fill(&Counters[0], &Counters[16], 0);
 	Common::fill(&RoomSaved[0], &RoomSaved[16], 0);
@@ -37,7 +37,6 @@ Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst,
 
 void Scott::runGame(Common::SeekableReadStream *gameFile) {
 	int vb, no;
-	int saveSlot;
 	initialize();
 
 	Bottom = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
@@ -63,24 +62,24 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 		Top = Bottom;
 	}
 
+	output("ScummVM support adapted from Scott Free, A Scott Adams game driver in C.\n\n");
+
 	// Check for savegame
-	saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
+	_saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
 
-	output("\
-Scott Free, A Scott Adams game driver in C.\n\
-Release 1.14, (c) 1993,1994,1995 Swansea University Computer Society.\n\
-Distributed under the GNU software license\n\n");
+	// Load the game
 	loadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
 
+	// Main game loop
 	while (!shouldQuit()) {
 		glk_tick();
 
 		performActions(0, 0);
 
-		if (saveSlot >= 0) {
+		if (_saveSlot >= 0) {
 			// Load any savegame during startup
-			loadGameState(saveSlot);
-			saveSlot = -1;
+			loadGameState(_saveSlot);
+			_saveSlot = -1;
 		}
 
 		look();
@@ -404,7 +403,8 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 }
 
 void Scott::output(const char *a) {
-	display(Bottom, "%s", a);
+	if (_saveSlot == -1)
+		display(Bottom, "%s", a);
 }
 
 void Scott::outputNumber(int a) {
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
index 0ff8d33..168476c 100644
--- a/engines/gargoyle/scott/scott.h
+++ b/engines/gargoyle/scott/scott.h
@@ -131,6 +131,7 @@ private:
 	bool split_screen;
 	winid_t Bottom, Top;
 	uint32 BitFlags;    ///< Might be >32 flags - I haven't seen >32 yet
+	int _saveSlot;		///< Save slot when loading savegame from launcher
 private:
 	/**
 	 * Initialization code


Commit: fbed3ed7a3ab37cedf05d551e9c904db44a35b88
    https://github.com/scummvm/scummvm/commit/fbed3ed7a3ab37cedf05d551e9c904db44a35b88
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Handle negative values in readInts

Changed paths:
    engines/gargoyle/scott/scott.cpp


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 001eff5..9f3a87d 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -1243,10 +1243,17 @@ void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
 		// Get the next value
 		int *val = (int *)va_arg(va, int);
 		*val = 0;
+
+		int factor = c == '-' ? -1 : 1;
+		if (factor == -1)
+			c = f->readByte();
+
 		while (Common::isDigit(c)) {
 			*val = (*val * 10) + (c - '0');
 			c = f->readByte();
 		}
+
+		*val *= factor;		// Handle negatives
 	}
 
 	va_end(va);


Commit: 62315b969d851166c94c6fb73a3da7ac4ab44a73
    https://github.com/scummvm/scummvm/commit/62315b969d851166c94c6fb73a3da7ac4ab44a73
Author: dreammaster (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: gcc compilation fixes

Changed paths:
    engines/gargoyle/detection.cpp
    engines/gargoyle/detection_tables.h
    engines/gargoyle/events.h
    engines/gargoyle/gargoyle.h
    engines/gargoyle/glk_types.h
    engines/gargoyle/module.mk
    engines/gargoyle/scott/scott.cpp
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h
    engines/gargoyle/window_text_buffer.cpp


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 4363c46..245790a 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -142,8 +142,7 @@ bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
 }
 
 bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
-	Gargoyle::GargoyleGameDescription *gd = (Gargoyle::GargoyleGameDescription *)desc;
-	gd->_filename = ConfMan.get("filename");
+	const Gargoyle::GargoyleGameDescription *gd = (const Gargoyle::GargoyleGameDescription *)desc;
 
 	switch (gd->_interpType) {
 	case Gargoyle::INTERPRETER_SCOTT:
diff --git a/engines/gargoyle/detection_tables.h b/engines/gargoyle/detection_tables.h
index 777d4db..30491f6 100644
--- a/engines/gargoyle/detection_tables.h
+++ b/engines/gargoyle/detection_tables.h
@@ -23,19 +23,7 @@
 namespace Gargoyle {
 
 static const GargoyleGameDescription gameDescriptions[] = {
-	{
-		{
-			"Gargoyle",
-			0,
-			AD_ENTRY1s("dummy", "0", 0),
-			Common::EN_ANY,
-			Common::kPlatformDOS,
-			ADGF_NO_FLAGS,
-			GUIO0()
-		},
-	},
-
-	{ AD_TABLE_END_MARKER }
+	{ AD_TABLE_END_MARKER, "", (InterpreterType)0, "" }
 };
 
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
index b69cd04..745f1ae 100644
--- a/engines/gargoyle/events.h
+++ b/engines/gargoyle/events.h
@@ -258,7 +258,7 @@ public:
 	/**
 	 * Set the total number of frames played
 	 */
-	void Events::setTotalPlayTicks(uint frames) {
+	void setTotalPlayTicks(uint frames) {
 		_frameCounter = frames;
 	}
 
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index e131be1..aeaa0c0 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -164,7 +164,7 @@ public:
 	/**
 	 * Returns the primary filename for the game
 	 */
-	const Common::String &GargoyleEngine::getFilename() const;
+	const Common::String &getFilename() const;
 
 	/**
 	 * Return the game engine's target name
diff --git a/engines/gargoyle/glk_types.h b/engines/gargoyle/glk_types.h
index 7258e69..21a8b75 100644
--- a/engines/gargoyle/glk_types.h
+++ b/engines/gargoyle/glk_types.h
@@ -103,7 +103,7 @@ enum Gestalt {
 	gestalt_LineTerminatorKey      = 19,
 	gestalt_DateTime               = 20,
 	gestalt_Sound2                 = 21,
-	gestalt_GarglkText             = 0x1100,
+	gestalt_GarglkText             = 0x1100
 };
 
 enum Style {
@@ -118,7 +118,7 @@ enum Style {
 	style_Input        = 8,
 	style_User1        = 9,
 	style_User2        = 10,
-	style_NUMSTYLES    = 11,
+	style_NUMSTYLES    = 11
 };
 
 enum WinType {
@@ -127,7 +127,7 @@ enum WinType {
 	wintype_Blank      = 2,
 	wintype_TextBuffer = 3,
 	wintype_TextGrid   = 4,
-	wintype_Graphics   = 5,
+	wintype_Graphics   = 5
 };
 
 enum WinMethod {
@@ -143,7 +143,7 @@ enum WinMethod {
 
 	winmethod_Border     = 0x000,
 	winmethod_NoBorder   = 0x100,
-	winmethod_BorderMask = 0x100,
+	winmethod_BorderMask = 0x100
 };
 
 enum StyleHint {
@@ -162,7 +162,7 @@ enum StyleHint {
 	stylehint_just_LeftFlush  = 0,
 	stylehint_just_LeftRight  = 1,
 	stylehint_just_Centered   = 2,
-	stylehint_just_RightFlush = 3,
+	stylehint_just_RightFlush = 3
 };
 
 /**
@@ -176,7 +176,7 @@ enum giDisp {
 	gidisp_Class_Window   = 0,
 	gidisp_Class_Stream   = 1,
 	gidisp_Class_Fileref  = 2,
-	gidisp_Class_Schannel = 3,
+	gidisp_Class_Schannel = 3
 };
 
 enum zcolor {
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index bf4138a..d24a041 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -16,7 +16,6 @@ MODULE_OBJS := \
 	unicode_gen.o \
 	utils.o \
 	windows.o \
-	window_mask.o \
 	window_graphics.o \
 	window_pair.o \
 	window_text_buffer.o \
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 9f3a87d..3316995 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -175,7 +175,7 @@ void Scott::delay(int seconds) {
 }
 
 void Scott::fatal(const char *x) {
-	error(x);
+	error("%s", x);
 }
 
 void Scott::clearScreen(void) {
@@ -412,7 +412,7 @@ void Scott::outputNumber(int a) {
 }
 
 void Scott::look(void) {
-	static char *ExitNames[6] = { "North", "South", "East", "West", "Up", "Down" };
+	const char *const ExitNames[6] = { "North", "South", "East", "West", "Up", "Down" };
 	Room *r;
 	int ct, f;
 	int pos;
@@ -555,7 +555,7 @@ Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
 		glk_put_string_stream(file, msg.c_str());
 	}
 
-	msg = Common::String::format("%lu %d %hd %d %d %hd\n",
+	msg = Common::String::format("%u %d %hd %d %d %hd\n",
 	                             BitFlags, (BitFlags & (1 << DARKBIT)) ? 1 : 0,
 	                             MyLoc, CurrentCounter, SavedRoom, GameHeader.LightTime);
 	glk_put_string_stream(file, msg.c_str());
@@ -602,7 +602,7 @@ Common::Error Scott::loadGameState(int slot) {
 	}
 
 	glk_get_line_stream(file, buf, sizeof buf);
-	sscanf(buf, "%ld %hd %d %d %d %d\n",
+	sscanf(buf, "%u %hd %d %d %d %d\n",
 	       &BitFlags, &darkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
 	       &GameHeader.LightTime);
 
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 75fb205..659fcd0 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -850,7 +850,7 @@ void FileStream::putBuffer(const char *buf, size_t len) {
 
 	ensureOp(filemode_Write);
 	for (size_t lx = 0; lx < len; lx++) {
-		unsigned char ch = ((unsigned char *)buf)[lx];
+		unsigned char ch = ((const unsigned char *)buf)[lx];
 		if (!_unicode) {
 			_outFile->writeByte(ch);
 		} else if (_textFile) {
@@ -1100,7 +1100,7 @@ glui32 FileStream::getBuffer(char *buf, glui32 len) {
 		for (lx = 0; lx < len; lx++) {
 			glui32 ch;
 			ch = getCharUtf8();
-			if (ch == -1)
+			if (ch == (glui32)-1)
 				break;
 			_readCount++;
 			if (ch >= 0x100)
@@ -1161,7 +1161,7 @@ glui32 FileStream::getBufferUni(glui32 *buf, glui32 len) {
 		for (lx = 0; lx < len; lx++) {
 			glui32 ch;
 			ch = getCharUtf8();
-			if (ch == -1)
+			if (ch == (glui32)-1)
 				break;
 			_readCount++;
 			buf[lx] = ch;
@@ -1221,7 +1221,7 @@ glui32 FileStream::getLine(char *buf, glui32 len) {
 		for (lx = 0; lx < len && !gotNewline; lx++) {
 			glui32 ch;
 			ch = getCharUtf8();
-			if (ch == -1)
+			if (ch == (glui32)-1)
 				break;
 			_readCount++;
 			if (ch >= 0x100)
@@ -1295,7 +1295,7 @@ glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
 			glui32 ch;
 			ch = getCharUtf8();
-			if (ch == -1)
+			if (ch == (glui32)-1)
 				break;
 			_readCount++;
 			ubuf[lx] = ch;
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 8a497e0..772b021 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -44,20 +44,20 @@ enum FileUsage {
 	fileusage_TypeMask = 0x0f,
 
 	fileusage_TextMode = 0x100,
-	fileusage_BinaryMode = 0x000,
+	fileusage_BinaryMode = 0x000
 };
 
 enum FileMode {
 	filemode_Write = 0x01,
 	filemode_Read = 0x02,
 	filemode_ReadWrite = 0x03,
-	filemode_WriteAppend = 0x05,
+	filemode_WriteAppend = 0x05
 };
 
 enum SeekMode {
 	seekmode_Start = 0,
 	seekmode_Current = 1,
-	seekmode_End = 2,
+	seekmode_End = 2
 };
 
 struct StreamResult {
diff --git a/engines/gargoyle/window_text_buffer.cpp b/engines/gargoyle/window_text_buffer.cpp
index ca89167..5cf8f34 100644
--- a/engines/gargoyle/window_text_buffer.cpp
+++ b/engines/gargoyle/window_text_buffer.cpp
@@ -43,7 +43,7 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windo
 	_radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr),
 	_spaced(0), _dashed(0), _copyBuf(0), _copyPos(0) {
 	_type = wintype_TextBuffer;
-	Common::fill(&_history[0], &_history[HISTORYLEN], nullptr);
+	Common::fill(&_history[0], &_history[HISTORYLEN], (glui32 *)nullptr);
 
 	_lines.resize(SCROLLBACK);
 	_chars = _lines[0]._chars;
@@ -908,7 +908,7 @@ void TextBufferWindow::redraw() {
 					for (tsc = 0; tsc < linelen; tsc++) {
 						tsw = calcWidth(ln->_chars, ln->_attrs, 0, tsc, spw) / GLI_SUBPIX;
 						if (tsw + tx >= sx0 ||
-						        tsw + tx + GLI_SUBPIX >= sx0 && ln->_chars[tsc] != ' ') {
+						        ((tsw + tx + GLI_SUBPIX) >= sx0 && ln->_chars[tsc] != ' ')) {
 							lsc = tsc;
 							selchar = true;
 							break;


Commit: cde50e00edea1c0cc0235aa814501038e6a1792c
    https://github.com/scummvm/scummvm/commit/cde50e00edea1c0cc0235aa814501038e6a1792c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix handling of negative file offsets in MemoryStream::setPosition

Changed paths:
    engines/gargoyle/streams.cpp


diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 659fcd0..41f3caf 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -459,29 +459,31 @@ glui32 MemoryStream::getPosition() const {
 }
 
 void MemoryStream::setPosition(glui32 pos, glui32 seekMode) {
+	glsi32 newPos = pos;
+
 	if (!_unicode) {
 		if (seekMode == seekmode_Current)
-			pos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + pos;
+			newPos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + newPos;
 		else if (seekMode == seekmode_End)
-			pos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + pos;
+			newPos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + newPos;
 		else
-			/* pos = pos */;
-		if (pos < 0)
-			pos = 0;
-		if (pos > (glui32)((unsigned char *)_bufEof - (unsigned char *)_buf))
-			pos = ((unsigned char *)_bufEof - (unsigned char *)_buf);
-		_bufPtr = (unsigned char *)_buf + pos;
+			/* newPos = newPos */;
+		if (newPos < 0)
+			newPos = 0;
+		if (newPos > ((unsigned char *)_bufEof - (unsigned char *)_buf))
+			newPos = ((unsigned char *)_bufEof - (unsigned char *)_buf);
+		_bufPtr = (unsigned char *)_buf + newPos;
 	} else {
 		if (seekMode == seekmode_Current)
-			pos = ((glui32 *)_bufPtr - (glui32 *)_buf) + pos;
+			newPos = ((glui32 *)_bufPtr - (glui32 *)_buf) + newPos;
 		else if (seekMode == seekmode_End)
-			pos = ((glui32 *)_bufEof - (glui32 *)_buf) + pos;
+			newPos = ((glui32 *)_bufEof - (glui32 *)_buf) + newPos;
 
-		if (pos < 0)
-			pos = 0;
-		if (pos > (glui32)((glui32 *)_bufEof - (glui32 *)_buf))
-			pos = ((glui32 *)_bufEof - (glui32 *)_buf);
-		_bufPtr = (glui32 *)_buf + pos;
+		if (newPos < 0)
+			newPos = 0;
+		if (newPos > ((glui32 *)_bufEof - (glui32 *)_buf))
+			newPos = ((glui32 *)_bufEof - (glui32 *)_buf);
+		_bufPtr = (glui32 *)_buf + newPos;
 	}
 }
 


Commit: 43cee5fb0f4fee9669098c21777ac9b41865bee1
    https://github.com/scummvm/scummvm/commit/43cee5fb0f4fee9669098c21777ac9b41865bee1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix setTime converting seconds to a date/time structure

Changed paths:
    engines/gargoyle/time.cpp


diff --git a/engines/gargoyle/time.cpp b/engines/gargoyle/time.cpp
index 9a99d3b..39c3b7c 100644
--- a/engines/gargoyle/time.cpp
+++ b/engines/gargoyle/time.cpp
@@ -63,15 +63,17 @@ TimeAndDate::operator Timestamp() const {
 
 void TimeAndDate::setTime(const TimeSeconds &ts) {
 	TimeSeconds total = ts;
-	int daysInYear, secsInYear;
+	int daysInYear = 0, secsInYear = 0;
 
 	// Figure out the year
 	this->year = 1969;
 	do {
 		++this->year;
-		daysInYear = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 365 : 365;
+		total -= secsInYear;
+
+		daysInYear = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 366 : 365;
 		secsInYear = daysInYear * 24 * 60 * 60;
-	} while (total >= daysInYear);
+	} while (total >= secsInYear);
 
 	// Figure out month and day
 	int dayInYear = total / (24 * 60 * 60);


Commit: c589b807e2b4ae67928cfadb0a5b2e18cef38ff3
    https://github.com/scummvm/scummvm/commit/c589b807e2b4ae67928cfadb0a5b2e18cef38ff3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Skeleton sub-engine for Frotz interpreter

Changed paths:
  A engines/gargoyle/frotz/detection.cpp
  A engines/gargoyle/frotz/detection.h
  A engines/gargoyle/frotz/frotz.cpp
  A engines/gargoyle/frotz/frotz.h
  A engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/detection.cpp
    engines/gargoyle/module.mk
    engines/gargoyle/scott/detection.cpp
    engines/gargoyle/scott/detection.h


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 245790a..f3b7ca0 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -69,6 +69,7 @@ const Common::String &GargoyleEngine::getGameMD5() const {
 } // End of namespace Gargoyle
 
 static const PlainGameDescriptor gargoyleGames[] = {
+	{"zcode", "Zcode Games" },
 	{"scottadams", "Scott Adams Games"},
 
 	// Scott Adams games
@@ -94,6 +95,8 @@ static const PlainGameDescriptor gargoyleGames[] = {
 #include "common/config-manager.h"
 #include "common/file.h"
 #include "gargoyle/detection_tables.h"
+#include "gargoyle/frotz/detection.h"
+#include "gargoyle/frotz/frotz.h"
 #include "gargoyle/scott/detection.h"
 #include "gargoyle/scott/scott.h"
 
@@ -145,6 +148,9 @@ bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const AD
 	const Gargoyle::GargoyleGameDescription *gd = (const Gargoyle::GargoyleGameDescription *)desc;
 
 	switch (gd->_interpType) {
+	case Gargoyle::INTERPRETER_FROTZ:
+		*engine = new Gargoyle::Frotz::Frotz(syst, gd);
+		break;
 	case Gargoyle::INTERPRETER_SCOTT:
 		*engine = new Gargoyle::Scott::Scott(syst, gd);
 		break;
@@ -216,6 +222,7 @@ SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, i
 
 DetectedGames GargoyleMetaEngine::detectGames(const Common::FSList &fslist) const {
 	DetectedGames detectedGames;
+	Gargoyle::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
 	Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
 
 	return detectedGames;
@@ -234,11 +241,19 @@ ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, con
 	ADDetectedGames results;
 	Common::File f;
 
-	Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
-	if (detectedGames.size() > 0 && f.open(parent.getChild(filename))) {
+	// Check each sub-engine for any detected games
+	if (Gargoyle::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames))
+		gameDescription._interpType = Gargoyle::INTERPRETER_FROTZ;
+	else if (Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames))
+		gameDescription._interpType = Gargoyle::INTERPRETER_SCOTT;
+	else
+		// No match found, so return no results
+		return results;
+
+	// Set up the game description and return it
+	if (f.open(parent.getChild(filename))) {
 		DetectedGame gd = detectedGames.front();
 
-		gameDescription._interpType = Gargoyle::INTERPRETER_SCOTT;
 		gameDescription._desc.gameId = gameId;
 		gameDescription._desc.language = gd.language;
 		gameDescription._desc.platform = gd.platform;
diff --git a/engines/gargoyle/frotz/detection.cpp b/engines/gargoyle/frotz/detection.cpp
new file mode 100644
index 0000000..6760777
--- /dev/null
+++ b/engines/gargoyle/frotz/detection.cpp
@@ -0,0 +1,83 @@
+/* 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 "gargoyle/frotz/detection.h"
+#include "common/file.h"
+#include "common/md5.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+struct FrotzGame {
+	const char *_md5;
+	const char *_gameId;
+	int32 _filesize;
+	const char *_desc;
+};
+
+const FrotzGame FROTZ_GAMES[] = {
+	{ nullptr, nullptr, 0, nullptr }
+};
+
+bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+	Common::File gameFile;
+	Common::String md5;
+	const char *const EXTENSIONS[9] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb" };
+
+	// 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 (int idx = 0; idx < 9 && !hasExt; ++idx)
+			hasExt = filename.hasSuffixIgnoreCase(EXTENSIONS[idx]);
+		if (!hasExt)
+			continue;
+
+		// Check for known game
+		if (gameFile.open(*file)) {
+			md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+
+			// Scan through the game list for a match
+			const FrotzGame *p = FROTZ_GAMES;
+			while (p->_md5 && p->_filesize != gameFile.size() && md5 != p->_md5)
+				++p;
+
+			if (p->_filesize) {
+				// Found a match
+				DetectedGame gd(p->_gameId, p->_desc, Common::EN_ANY, Common::kPlatformUnknown);
+				gd.addExtraEntry("filename", file->getName());
+
+				gameList.push_back(gd);
+			}
+
+			gameFile.close();
+		}
+	}
+
+	return !gameList.empty();
+}
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/detection.h b/engines/gargoyle/frotz/detection.h
new file mode 100644
index 0000000..2e70d7c
--- /dev/null
+++ b/engines/gargoyle/frotz/detection.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_FROTZ_DETECTION
+#define GARGOYLE_FROTZ_DETECTION
+
+#include "common/fs.h"
+#include "engines/game.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+class FrotzMetaEngine {
+public:
+	/**
+	 * Detect supported games
+	 */
+	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
new file mode 100644
index 0000000..c13f5b2
--- /dev/null
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -0,0 +1,45 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 "gargoyle/frotz/frotz.h"
+#include "gargoyle/frotz/frotz_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc) {
+}
+
+void Frotz::runGame(Common::SeekableReadStream *gameFile) {
+	// TODO
+}
+
+Common::Error Frotz::loadGameState(int slot) {
+	return Common::kNoError;
+}
+
+Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
+	return Common::kNoError;
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
new file mode 100644
index 0000000..8aa2ce7
--- /dev/null
+++ b/engines/gargoyle/frotz/frotz.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 GARGOYLE_FROTZ_FROTZ
+#define GARGOYLE_FROTZ_FROTZ
+
+#include "gargoyle/glk.h"
+#include "gargoyle/frotz/frotz_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+/**
+ * Frotz interpreter for Z-code games
+ */
+class Frotz : public Glk {
+private:
+	/**
+	 *
+	 */
+public:
+	/**
+	 * Constructor
+	 */
+	Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+	/**
+	 * Execute the game
+	 */
+	virtual void runGame(Common::SeekableReadStream *gameFile) override;
+
+	/**
+	 * Load a savegame
+	 */
+	virtual Common::Error loadGameState(int slot) override;
+
+	/**
+	 * Save the game
+	 */
+	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
new file mode 100644
index 0000000..d4a6d8c
--- /dev/null
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -0,0 +1,37 @@
+/* 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 GARGOYLE_FROTZ_FROTZ_TYPES
+#define GARGOYLE_FROTZ_FROTZ_TYPES
+
+#include "gargoyle/glk_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+typedef byte zbyte;
+typedef uint16 zword;
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index d24a041..135b39c 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -20,6 +20,8 @@ MODULE_OBJS := \
 	window_pair.o \
 	window_text_buffer.o \
 	window_text_grid.o \
+	frotz/detection.o \
+	frotz/frotz.o \
 	scott/detection.o \
 	scott/scott.o
 
diff --git a/engines/gargoyle/scott/detection.cpp b/engines/gargoyle/scott/detection.cpp
index 04d4f83..1b8041a 100644
--- a/engines/gargoyle/scott/detection.cpp
+++ b/engines/gargoyle/scott/detection.cpp
@@ -53,7 +53,7 @@ const ScottGame SCOTT_GAMES[] = {
 	{ nullptr, nullptr, 0, nullptr }
 };
 
-void ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
 	Common::File gameFile;
 	Common::String md5;
 
@@ -81,6 +81,8 @@ void ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 			gameFile.close();
 		}
 	}
+
+	return !gameList.empty();
 }
 
 } // End of namespace Scott
diff --git a/engines/gargoyle/scott/detection.h b/engines/gargoyle/scott/detection.h
index e2aa93e..01e1ffe 100644
--- a/engines/gargoyle/scott/detection.h
+++ b/engines/gargoyle/scott/detection.h
@@ -32,9 +32,9 @@ namespace Scott {
 class ScottMetaEngine {
 public:
 	/**
-	 * Detect Scott Adams games
+	 * Detect supported games
 	 */
-	static void detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
 };
 
 } // End of namespace Scott


Commit: 1aaf2fd145e30937e8cd4c6736bee448f2b05a2c
    https://github.com/scummvm/scummvm/commit/1aaf2fd145e30937e8cd4c6736bee448f2b05a2c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fleshing out detection logic and added some entries

Changed paths:
  A engines/gargoyle/frotz/detection_tables.cpp
  A engines/gargoyle/frotz/detection_tables.h
    engines/gargoyle/detection.cpp
    engines/gargoyle/frotz/detection.cpp
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index f3b7ca0..2836e04 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -68,10 +68,55 @@ const Common::String &GargoyleEngine::getGameMD5() const {
 
 } // End of namespace Gargoyle
 
+#include "gargoyle/frotz/detection_tables.h"
+#define ZCODE(ID, NAME) { ID, Gargoyle::Frotz::NAME }
+
 static const PlainGameDescriptor gargoyleGames[] = {
 	{"zcode", "Zcode Games" },
 	{"scottadams", "Scott Adams Games"},
 
+	// Infocom/Z-code games
+	ZCODE("amfv", AMFV),
+	ZCODE("arthur", ARTHUR),
+	ZCODE("ballyhoo", BALLYHOO),
+	ZCODE("beyondzork", BEYONDZORK),
+	ZCODE("borderzone", BORDERZONE),
+	ZCODE("bureaucracy", BUREAUCRACY),
+	ZCODE("cutthroats", CUTTHROATS),
+	ZCODE("deadline", DEADLINE),
+	ZCODE("enchanter", ENCHANTER),
+	ZCODE("hhgttg", HHGTTG),
+	ZCODE("hijinx", HIJINX),
+	ZCODE("infidel", INFIDEL),
+	ZCODE("journey", JOURNEY),
+	ZCODE("lgop", LGOP),
+	ZCODE("lgop2", LGOP2),
+	ZCODE("lurking", LURKING),
+	ZCODE("minizork1", MINIZORK1),
+	ZCODE("moonmist", MOONMIST),
+	ZCODE("nordbert", NORDBERT),
+	ZCODE("planetfall", PLANETFALL),
+	ZCODE("plundered", PLUNDERED),
+	ZCODE("sampler1", SAMPLER1),
+	ZCODE("sampler2", SAMPLER2),
+	ZCODE("seastalker", SEASTALKER),
+	ZCODE("sherlockriddle", SHERLOCKRIDDLE),
+	ZCODE("shogun", SHOGUN),
+	ZCODE("sorcerer", SORCERER),
+	ZCODE("spellbreaker", SPELLBREAKER),
+	ZCODE("starcross", STARCROSS),
+	ZCODE("stationfall", STATIONFALL),
+	ZCODE("suspect", SUSPECT),
+	ZCODE("suspended", SUSPENDED),
+	ZCODE("trinity", TRINITY),
+	ZCODE("wishbringer", WISHBRINGER),
+	ZCODE("witness", WITNESS),
+	ZCODE("zork0", ZORK0),
+	ZCODE("zork1", ZORK1),
+	ZCODE("zork2", ZORK2),
+	ZCODE("zork3", ZORK3),
+	ZCODE("ztuu", ZTUU),
+
 	// Scott Adams games
 	{ "adventureland", "Adventureland" },
 	{ "pirateadventure", "Pirate Adventure" },
diff --git a/engines/gargoyle/frotz/detection.cpp b/engines/gargoyle/frotz/detection.cpp
index 6760777..fff8005 100644
--- a/engines/gargoyle/frotz/detection.cpp
+++ b/engines/gargoyle/frotz/detection.cpp
@@ -24,23 +24,12 @@
 #include "common/file.h"
 #include "common/md5.h"
 
+#include "gargoyle/frotz/detection_tables.h"
+
 namespace Gargoyle {
 namespace Frotz {
 
-struct FrotzGame {
-	const char *_md5;
-	const char *_gameId;
-	int32 _filesize;
-	const char *_desc;
-};
-
-const FrotzGame FROTZ_GAMES[] = {
-	{ nullptr, nullptr, 0, nullptr }
-};
-
 bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
-	Common::File gameFile;
-	Common::String md5;
 	const char *const EXTENSIONS[9] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb" };
 
 	// Loop through the files of the folder
@@ -55,25 +44,33 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 		if (!hasExt)
 			continue;
 
-		// Check for known game
-		if (gameFile.open(*file)) {
-			md5 = Common::computeStreamMD5AsString(gameFile, 5000);
-
-			// Scan through the game list for a match
-			const FrotzGame *p = FROTZ_GAMES;
-			while (p->_md5 && p->_filesize != gameFile.size() && md5 != p->_md5)
-				++p;
+		// Open up the file and calculate the md5
+		Common::File gameFile;
+		if (!gameFile.open(*file))
+			continue;
+		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+		size_t filesize = gameFile.size();
+		gameFile.close();
 
-			if (p->_filesize) {
-				// Found a match
-				DetectedGame gd(p->_gameId, p->_desc, Common::EN_ANY, Common::kPlatformUnknown);
-				gd.addExtraEntry("filename", file->getName());
+		// Check for known game
+		const FrotzGameDescription *p = FROTZ_GAMES;
+		while (p->_gameId && p->_md5 && (md5 != p->_md5 || filesize != p->_filesize))
+			++p;
 
-				gameList.push_back(gd);
-			}
+		DetectedGame gd;
+		if (!p->_gameId) {
+			// Generic .dat files don't get reported as matches unless they have a known md5
+			if (filename.hasSuffixIgnoreCase(".dat"))
+				continue;
 
-			gameFile.close();
+			warning("Uknown zcode game %s - %s %d", filename.c_str(), md5.c_str(), filesize);
+			gd = DetectedGame("zcode", "Unrecognised zcode game", Common::UNK_LANG, Common::kPlatformUnknown);
+		} else {
+			gd = DetectedGame(p->_gameId, p->_description, p->_language, Common::kPlatformUnknown, p->_extra);
 		}
+
+		gd.addExtraEntry("filename", filename);
+		gameList.push_back(gd);
 	}
 
 	return !gameList.empty();
diff --git a/engines/gargoyle/frotz/detection_tables.cpp b/engines/gargoyle/frotz/detection_tables.cpp
new file mode 100644
index 0000000..f8d3f42
--- /dev/null
+++ b/engines/gargoyle/frotz/detection_tables.cpp
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/detection_tables.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+const char *const AMFV = "A Mind Forever Voyaging";
+const char *const ARTHUR = "Arthur: The Quest for Excalibur";
+const char *const BALLYHOO = "Ballyhoo";
+const char *const BEYONDZORK = "Beyond Zork";
+const char *const BORDERZONE = "Border Zone";
+const char *const BUREAUCRACY = "Bureaucracy";
+const char *const CUTTHROATS = "Cutthroats";
+const char *const DEADLINE = "Deadline";
+const char *const ENCHANTER = "Enchanter";
+const char *const HHGTTG = "The Hitchhiker's Guide to the Galaxy";
+const char *const HIJINX = "Hollywood Hijinx";
+const char *const INFIDEL = "Infidel";
+const char *const JOURNEY = "Journey";
+const char *const LGOP = "Leather Goddesses of Phobos";
+const char *const LGOP2 = "Leather Goddesses of Phobos 2";
+const char *const LURKING = "The Lurking Horror";
+const char *const MINIZORK1 = "Mini Zork I: The Great Underground Empire";
+const char *const MOONMIST = "Moonmist";
+const char *const NORDBERT = "Nord and Bert Couldn't Make Head or Tail of It";
+const char *const PLANETFALL = "Planetfall";
+const char *const PLUNDERED = "Plundered Hearts";
+const char *const SAMPLER1 = "Infocom Sampler 1";
+const char *const SAMPLER2 = "Infocom Sampler 2";
+const char *const SEASTALKER = "Seastalker";
+const char *const SHERLOCKRIDDLE = "Sherlock: The Riddle of the Crown Jewels";
+const char *const SHOGUN = "James Clavell's Shogun";
+const char *const SORCERER = "Sorcerer";
+const char *const SPELLBREAKER = "Spellbreaker";
+const char *const STARCROSS = "Starcross";
+const char *const STATIONFALL = "Stationfall";
+const char *const SUSPECT = "Suspect";
+const char *const SUSPENDED = "Suspended";
+const char *const TRINITY = "Trinity";
+const char *const WISHBRINGER = "Wishbringer";
+const char *const WITNESS = "The Witness";
+const char *const ZORK0 = "Zork Zero: The Revenge of Megaboz";
+const char *const ZORK1 = "Zork I: The Great Underground Empire";
+const char *const ZORK2 = "Zork II: The Wizard of Frobozz";
+const char *const ZORK3 = "Zork III: The Dungeon Master";
+const char *const ZTUU = "Zork: The Undiscovered Underground";
+
+#define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
+#define ENTRY0(ID, DESC, VERSION, MD5, FILESIZE) { ID, DESC, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
+#define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
+
+const FrotzGameDescription FROTZ_GAMES[] = {
+	ENTRY0("hhgttg", HHGTTG, "v31 Solid Gold", "379022bcd4ec74b90274c6100c33f579", 158412),
+	ENTRY0("hhgttg", HHGTTG, "v47", "fdda8f4239819402c62db866bb61a648", 112622),
+	ENTRY0("hhgttg", HHGTTG, "v56", "a214fcb42bc9f554d07d983a12f6a062", 113444),
+	ENTRY0("hhgttg", HHGTTG, "v58", "e867d49ad1fb9406ff4e0678a4ee2ac9", 113332),
+	ENTRY0("hhgttg", HHGTTG, "v59", "34f6abc1f2a42be127ef434fc475f0ee", 113334),
+
+	FROTZ_TABLE_END_MARKER
+};
+
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/detection_tables.h b/engines/gargoyle/frotz/detection_tables.h
new file mode 100644
index 0000000..1a9e4ee
--- /dev/null
+++ b/engines/gargoyle/frotz/detection_tables.h
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/advancedDetector.h"
+#include "common/language.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+/**
+ * Game descriptor for ZCode games
+ */
+struct FrotzGameDescription {
+	const char *const _gameId;
+	const char *const _description;
+	const char *const _extra;
+	const char *const _md5;
+	size_t _filesize;
+	Common::Language _language;
+	const char *const _guiOptions;
+};
+
+extern const FrotzGameDescription FROTZ_GAMES[];
+extern const char *const AMFV;
+extern const char *const ARTHUR;
+extern const char *const BALLYHOO;
+extern const char *const BEYONDZORK;
+extern const char *const BORDERZONE;
+extern const char *const BUREAUCRACY;
+extern const char *const CUTTHROATS;
+extern const char *const DEADLINE;
+extern const char *const ENCHANTER;
+extern const char *const HHGTTG;
+extern const char *const HIJINX;
+extern const char *const INFIDEL;
+extern const char *const JOURNEY;
+extern const char *const LGOP;
+extern const char *const LGOP2;
+extern const char *const LURKING;
+extern const char *const MINIZORK1;
+extern const char *const MOONMIST;
+extern const char *const NORDBERT;
+extern const char *const PLANETFALL;
+extern const char *const PLUNDERED;
+extern const char *const SAMPLER1;
+extern const char *const SAMPLER2;
+extern const char *const SEASTALKER;
+extern const char *const SHERLOCKRIDDLE;
+extern const char *const SHOGUN;
+extern const char *const SORCERER;
+extern const char *const SPELLBREAKER;
+extern const char *const STARCROSS;
+extern const char *const STATIONFALL;
+extern const char *const SUSPECT;
+extern const char *const SUSPENDED;
+extern const char *const TRINITY;
+extern const char *const WISHBRINGER;
+extern const char *const WITNESS;
+extern const char *const ZORK0;
+extern const char *const ZORK1;
+extern const char *const ZORK2;
+extern const char *const ZORK3;
+extern const char *const ZTUU;
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 135b39c..8e16bfe 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -21,6 +21,7 @@ MODULE_OBJS := \
 	window_text_buffer.o \
 	window_text_grid.o \
 	frotz/detection.o \
+	frotz/detection_tables.o \
 	frotz/frotz.o \
 	scott/detection.o \
 	scott/scott.o


Commit: 8316c1a5b28f0a185df32d8bbafd08374a0aed32
    https://github.com/scummvm/scummvm/commit/8316c1a5b28f0a185df32d8bbafd08374a0aed32
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Beginnings of initialization

Changed paths:
  A engines/gargoyle/frotz/buffer.cpp
  A engines/gargoyle/frotz/buffer.h
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/frotz.h
    engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/frotz/buffer.cpp b/engines/gargoyle/frotz/buffer.cpp
new file mode 100644
index 0000000..15ec771
--- /dev/null
+++ b/engines/gargoyle/frotz/buffer.cpp
@@ -0,0 +1,104 @@
+/* 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 "gargoyle/frotz/buffer.h"
+#include "gargoyle/frotz/frotz.h"
+#include "common/algorithm.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Replace these method stubs with correct calls
+void stream_char(zchar) {}
+void stream_word(const zchar *) {}
+void stream_new_line(void) {}
+
+Buffer::Buffer() : _bufPos(0), _locked(false), _prevC('\0') {
+	Common::fill(&_buffer[0], &_buffer[TEXT_BUFFER_SIZE], '\0');
+}
+
+void Buffer::flush() {
+	/* Make sure we stop when flush_buffer is called from flush_buffer.
+	 * Note that this is difficult to avoid as we might print a newline
+	 * during flush_buffer, which might cause a newline interrupt, that
+	 * might execute any arbitrary opcode, which might flush the buffer.
+	 */
+	if (_locked || empty())
+		return;
+
+	// Send the buffer to the output streams
+	_buffer[_bufPos] = '\0';
+	
+	_locked = true;
+	stream_word(_buffer);
+	_locked = false;
+
+	// Reset the buffer
+	_bufPos = 0;
+	_prevC = '\0';
+}
+
+void Buffer::printChar(zchar c) {
+	static bool flag = false;
+
+	if (g_vm->_message || g_vm->_ostream_memory || g_vm->_enableBuffering) {
+		if (!flag) {
+			// Characters 0 and ZC_RETURN are special cases
+			if (c == ZC_RETURN) {
+				newLine();
+				return;
+			}
+			if (c == 0)
+				return;
+
+			// Flush the buffer before a whitespace or after a hyphen
+			if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (_prevC == '-' && c != '-'))
+				flush();
+
+			// Set the flag if this is part one of a style or font change
+			if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
+				flag = true;
+
+			// Remember the current character code
+			_prevC = c;
+		} else {
+			flag = false;
+		}
+
+		// Insert the character into the buffer
+		_buffer[_bufPos++] = c;
+
+		if (_bufPos == TEXT_BUFFER_SIZE)
+			error("Text buffer overflow");
+	} else {
+		stream_char(c);
+	}
+}
+
+void Buffer::newLine()  {
+	flush();
+	stream_new_line();
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/buffer.h b/engines/gargoyle/frotz/buffer.h
new file mode 100644
index 0000000..608e087
--- /dev/null
+++ b/engines/gargoyle/frotz/buffer.h
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_FROTZ_BUFFER
+#define GARGOYLE_FROTZ_BUFFER
+
+#include "gargoyle/frotz/frotz_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define TEXT_BUFFER_SIZE 200
+
+/**
+ * Text buffer class
+ */
+class Buffer {
+public:
+	zchar _buffer[TEXT_BUFFER_SIZE];
+	size_t _bufPos;
+	bool _locked;
+	zchar _prevC;
+public:
+	/**
+	 * Constructor
+	 */
+	Buffer();
+
+	/**
+	 * Copy the contents of the text buffer to the output streams.
+	 */
+	void flush();
+
+	 /**
+	  * High level output function.
+	  */
+	void printChar(zchar c);
+
+	 /**
+	  * High level newline function.
+	  */
+	void newLine();
+
+	/**
+	 * Returns true if the buffer is empty
+	 */
+	bool empty() const { return !_bufPos; }
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index c13f5b2..e7c6972 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -26,7 +26,17 @@
 namespace Gargoyle {
 namespace Frotz {
 
-Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc) {
+Frotz *g_vm;
+
+Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
+		_storyId(STORY_UNKNOWN), _storySize(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
+		_ostream_screen(true), _ostream_script(false), _ostream_memory(false),
+		_ostream_record(false), _istream_replay(false), _message(false),
+		_cwin(0), _mwin(0), _mouse_x(0), _mouse_y(0), _menu_selected(0),
+		_enableWrapping(false), _enableScripting(true), _enableScrolling(false),
+		_enableBuffering(false), _reserveMem(0) {
+	g_vm = this;
+	Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
 }
 
 void Frotz::runGame(Common::SeekableReadStream *gameFile) {
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index 8aa2ce7..6ed226a 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -33,10 +33,44 @@ namespace Frotz {
  * Frotz interpreter for Z-code games
  */
 class Frotz : public Glk {
-private:
-	/**
-	 *
-	 */
+public:
+	UserOptions _options;
+	Header _header;
+
+	// Story file name, id number and size
+	Common::String _storyName;
+	Story _storyId;
+	size_t _storySize;
+
+	// Stack data
+	zword _stack[STACK_SIZE];
+	zword *_sp;
+	zword *_fp;
+	zword _frameCount;
+
+	// IO streams
+	bool _ostream_screen;
+	bool _ostream_script;
+	bool _ostream_memory;
+	bool _ostream_record;
+	bool _istream_replay;
+	bool _message;
+
+	// Current window and mouse data
+	int _cwin;
+	int _mwin;
+	int _mouse_y;
+	int _mouse_x;
+	int _menu_selected;
+
+	// Window attributes
+	bool _enableWrapping;
+	bool _enableScripting;
+	bool _enableScrolling;
+	bool _enableBuffering;
+
+	// Size of memory to reserve (in bytes)
+	size_t _reserveMem;
 public:
 	/**
 	 * Constructor
@@ -59,6 +93,8 @@ public:
 	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
 };
 
+extern Frotz *g_vm;
+
 } // End of namespace Frotz
 } // End of namespace Gargoyle
 
diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index d4a6d8c..6fee562 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -24,13 +24,177 @@
 #define GARGOYLE_FROTZ_FROTZ_TYPES
 
 #include "gargoyle/glk_types.h"
+#include "common/algorithm.h"
 
 namespace Gargoyle {
 namespace Frotz {
 
+#define MAX_UNDO_SLOTS 500
+#define STACK_SIZE 20
+
+/* There are four error reporting modes: never report errors;
+ * report only the first time a given error type occurs;
+ * report every time an error occurs;
+ * or treat all errors as fatal errors, killing the interpreter.
+ * I strongly recommend "report once" as the default. But you can compile in a
+ * different default by changing the definition of ERR_DEFAULT_REPORT_MODE.
+ */
+enum ErrorReport {
+	ERR_REPORT_NEVER  = 0,
+	ERR_REPORT_ONCE   = 1,
+	ERR_REPORT_ALWAYS = 2,
+	ERR_REPORT_FATAL  = 3,
+
+	ERR_DEFAULT_REPORT_MODE = ERR_REPORT_NEVER
+};
+
+/**
+ * Character codes
+ */
+enum ZCode {
+	ZC_TIME_OUT      = 0x00,
+	ZC_NEW_STYLE     = 0x01,
+	ZC_NEW_FONT      = 0x02,
+	ZC_BACKSPACE     = 0x08,
+	ZC_INDENT        = 0x09,
+	ZC_GAP           = 0x0b,
+	ZC_RETURN        = 0x0d,
+	ZC_HKEY_MIN      = 0x0e,
+	ZC_HKEY_RECORD   = 0x0e,
+	ZC_HKEY_PLAYBACK = 0x0f,
+	ZC_HKEY_SEED     = 0x10,
+	ZC_HKEY_UNDO     = 0x11,
+	ZC_HKEY_RESTART  = 0x12,
+	ZC_HKEY_QUIT     = 0x13,
+	ZC_HKEY_DEBUG    = 0x14,
+	ZC_HKEY_HELP     = 0x15,
+	ZC_HKEY_MAX      = 0x15,
+	ZC_ESCAPE        = 0x1b,
+	ZC_ASCII_MIN     = 0x20,
+	ZC_ASCII_MAX     = 0x7e,
+	ZC_BAD           = 0x7f,
+	ZC_ARROW_MIN     = 0x81,
+	ZC_ARROW_UP      = 0x81,
+	ZC_ARROW_DOWN    = 0x82,
+	ZC_ARROW_LEFT    = 0x83,
+	ZC_ARROW_RIGHT   = 0x84,
+	ZC_ARROW_MAX     = 0x84,
+	ZC_FKEY_MIN      = 0x85,
+	ZC_FKEY_MAX      = 0x90,
+	ZC_NUMPAD_MIN    = 0x91,
+	ZC_NUMPAD_MAX    = 0x9a,
+	ZC_SINGLE_CLICK  = 0x9b,
+	ZC_DOUBLE_CLICK  = 0x9c,
+	ZC_MENU_CLICK    = 0x9d,
+	ZC_LATIN1_MIN    = 0xa0,
+	ZC_LATIN1_MAX    = 0xff
+};
+
+enum Story {
+	STORY_BEYOND_ZORK,
+	STORY_SHERLOCK,
+	STORY_ZORK_ZERO,
+	STORY_SHOGUN,
+	STORY_ARTHUR,
+	STORY_JOURNEY,
+	STORY_LURKING_HORROR,
+	STORY_UNKNOWN
+};
+
 typedef byte zbyte;
+typedef char zchar;
 typedef uint16 zword;
 
+/**
+ * User options
+ */
+struct UserOptions {
+	int _attribute_assignment;
+	int _attribute_testing;
+	int _context_lines;
+	int _object_locating;
+	int _object_movement;
+	int _left_margin;
+	int _right_margin;
+	bool _ignore_errors;
+	bool _piracy;
+	int _undo_slots;
+	int _expand_abbreviations;
+	int _script_cols;
+	bool _save_quetzal;
+	int _err_report_mode;
+	bool _sound;
+
+	UserOptions() : _attribute_assignment(0), _attribute_testing(0),
+		_context_lines(0), _object_locating(0), _object_movement(0),
+		_left_margin(0), _right_margin(0), _ignore_errors(false), _piracy(false),
+		_undo_slots(MAX_UNDO_SLOTS), _expand_abbreviations(0), _script_cols(80),
+		_save_quetzal(true),
+		_err_report_mode(ERR_DEFAULT_REPORT_MODE), _sound(true) {
+	}
+};
+
+/**
+ * Story file header data
+ */
+struct Header {
+	zbyte _version;
+	zbyte _config;
+	zword _release;
+	zword _resident_size;
+	zword _start_pc;
+	zword _dictionary;
+	zword _objects;
+	zword _globals;
+	zword _dynamic_size;
+	zword _flags;
+	zbyte _serial[6];
+	zword _abbreviations;
+	zword _file_size;
+	zword _checksum;
+	zbyte _interpreter_number;
+	zbyte _interpreter_version;
+	zbyte _screen_rows;
+	zbyte _screen_cols;
+	zword _screen_width;
+	zword _screen_height;
+	zbyte _font_height = 1;
+	zbyte _font_width = 1;
+	zword _functions_offset;
+	zword _strings_offset;
+	zbyte _default_background;
+	zbyte _default_foreground;
+	zword _terminating_keys;
+	zword _line_width;
+	zbyte _standard_high;
+	zbyte _standard_low;
+	zword _alphabet;
+	zword _extension_table;
+	zbyte _user_name[8];
+
+	zword _hx_table_size;
+	zword _hx_mouse_x;
+	zword _hx_mouse_y;
+	zword _hx_unicode_table;
+	zword _hx_flags;
+	zword _hx_fore_colour;
+	zword _hx_back_colour;
+
+	Header() : _version(0), _config(0), _release(0), _resident_size(0), _start_pc(0),
+			_dictionary(0), _objects(0), _globals(0), _dynamic_size(0), _flags(0),
+			_abbreviations(0), _file_size(0), _checksum(0), _interpreter_number(0),
+			_interpreter_version(0), _screen_rows(0), _screen_cols(0), _screen_width(0),
+			_screen_height(0), _font_height(1), _font_width(1), _functions_offset(0),
+			_strings_offset(0), _default_background(0), _default_foreground(0),
+			_terminating_keys(0), _line_width(0), _standard_high(1), _standard_low(1),
+			_alphabet(0), _extension_table(0),
+			_hx_table_size(0), _hx_mouse_x(0), _hx_mouse_y(0), _hx_unicode_table(0),
+			_hx_flags(0), _hx_fore_colour(0), _hx_back_colour(0) {
+		Common::fill(&_serial[0], &_serial[6], '\0');
+		Common::fill(&_user_name[0], &_user_name[8], '\0');
+	}
+};
+
 } // End of namespace Frotz
 } // End of namespace Gargoyle
 
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 8e16bfe..85bd055 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -20,6 +20,7 @@ MODULE_OBJS := \
 	window_pair.o \
 	window_text_buffer.o \
 	window_text_grid.o \
+	frotz/buffer.o \
 	frotz/detection.o \
 	frotz/detection_tables.o \
 	frotz/frotz.o \


Commit: 7d20a3552bd26d10e220261c97a8f65f2d3add54
    https://github.com/scummvm/scummvm/commit/7d20a3552bd26d10e220261c97a8f65f2d3add54
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added error handling

Changed paths:
  A engines/gargoyle/frotz/err.cpp
  A engines/gargoyle/frotz/err.h
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/frotz.h
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/frotz/err.cpp b/engines/gargoyle/frotz/err.cpp
new file mode 100644
index 0000000..b25b75c
--- /dev/null
+++ b/engines/gargoyle/frotz/err.cpp
@@ -0,0 +1,131 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/err.h"
+#include "gargoyle/frotz/frotz.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+const char *const Errors::ERR_MESSAGES[ERR_NUM_ERRORS] = {
+    "Text buffer overflow",
+    "Store out of dynamic memory",
+    "Division by zero",
+    "Illegal object",
+    "Illegal attribute",
+    "No such property",
+    "Stack overflow",
+    "Call to illegal address",
+    "Call to non-routine",
+    "Stack underflow",
+    "Illegal opcode",
+    "Bad stack frame",
+    "Jump to illegal address",
+    "Can't save while in interrupt",
+    "Nesting stream #3 too deep",
+    "Illegal window",
+    "Illegal window property",
+    "Print at illegal address",
+    "Illegal dictionary word length",
+    "@jin called with object 0",
+    "@get_child called with object 0",
+    "@get_parent called with object 0",
+    "@get_sibling called with object 0",
+    "@get_prop_addr called with object 0",
+    "@get_prop called with object 0",
+    "@put_prop called with object 0",
+    "@clear_attr called with object 0",
+    "@set_attr called with object 0",
+    "@test_attr called with object 0",
+    "@move_object called moving object 0",
+    "@move_object called moving into object 0",
+    "@remove_object called with object 0",
+    "@get_next_prop called with object 0"
+};
+
+Errors::Errors() {
+	Common::fill(&_count[0], &_count[ERR_NUM_ERRORS], 0);
+}
+
+void Errors::runtimeError(int errNum) {
+    int wasfirst;
+    
+    if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
+	return;
+
+    if (g_vm->_options._err_report_mode == ERR_REPORT_FATAL
+		|| (!g_vm->_options._ignore_errors && errNum <= ERR_MAX_FATAL)) {
+		g_vm->_buffer.flush();
+		error(ERR_MESSAGES[errNum - 1]);
+		return;
+    }
+
+    wasfirst = (_count[errNum - 1] == 0);
+    _count[errNum - 1]++;
+    
+    if ((g_vm->_options._err_report_mode == ERR_REPORT_ALWAYS)
+			|| (g_vm->_options._err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
+		long pc = g_vm->getPC();
+		printString("Warning: ");
+		printString(ERR_MESSAGES[errNum - 1]);
+		printString(" (PC = ");
+		printLong(pc, 16);
+		printChar(')');
+        
+		if (g_vm->_options._err_report_mode == ERR_REPORT_ONCE) {
+			printString(" (will ignore further occurrences)");
+		} else {
+			printString(" (occurence ");
+			printLong(_count[errNum - 1], 10);
+			printChar(')');
+		}
+
+		newLine();
+    }
+}
+
+void Errors::printLong(uint value, int base) {
+    unsigned long i;
+    char c;
+
+    for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base)
+	if (value >= i || i == 1) {
+	    c = (value / i) % base;
+	    printChar(c + (c <= 9 ? '0' : 'a' - 10));
+	}
+}
+
+void Errors::printChar(const char c) {
+	// TODO
+}
+
+void Errors::printString(const char *str) {
+	// TODO
+}
+
+void Errors::newLine() {
+	// TODO
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/err.h b/engines/gargoyle/frotz/err.h
new file mode 100644
index 0000000..f61d726
--- /dev/null
+++ b/engines/gargoyle/frotz/err.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GARGOYLE_FROTZ_ERR
+#define GARGOYLE_FROTZ_ERR
+
+#include "gargoyle/frotz/frotz_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define ERR_NUM_ERRORS 33
+#define ERR_MAX_FATAL 19
+
+class Errors {
+private:
+	static const char *const ERR_MESSAGES[ERR_NUM_ERRORS];
+	int _count[ERR_NUM_ERRORS];
+public:
+	/**
+	 * Constructor
+	 */
+	Errors();
+
+	/**
+	 * An error has occurred. Ignore it, pass it to os_fatal or report
+	 * it according to err_report_mode.
+	 * @param errNum		Numeric code for error (1 to ERR_NUM_ERRORS)
+	 */
+	void runtimeError(int errNum);
+
+	/**
+	 * Print an unsigned 32bit number in decimal or hex.
+	 */
+	void printLong(uint value, int base);
+
+	/**
+	 * Print a character
+	 */
+	void printChar(const char c);
+
+	/**
+	 * Print a string
+	 */
+	void printString(const char *str);
+
+	/**
+	 * Add a newline
+	 */
+	void newLine();
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index e7c6972..2a7bcd0 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -51,5 +51,10 @@ Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
 	return Common::kNoError;
 }
 
+uint Frotz::getPC() const {
+	// TODO
+	return 0;
+}
+
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index 6ed226a..b0a7e1d 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -25,6 +25,8 @@
 
 #include "gargoyle/glk.h"
 #include "gargoyle/frotz/frotz_types.h"
+#include "gargoyle/frotz/buffer.h"
+#include "gargoyle/frotz/err.h"
 
 namespace Gargoyle {
 namespace Frotz {
@@ -36,6 +38,7 @@ class Frotz : public Glk {
 public:
 	UserOptions _options;
 	Header _header;
+	Buffer _buffer;
 
 	// Story file name, id number and size
 	Common::String _storyName;
@@ -91,6 +94,11 @@ public:
 	 * Save the game
 	 */
 	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
+
+	/**
+	 * Get the current PC
+	 */
+	uint getPC() const;
 };
 
 extern Frotz *g_vm;
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 85bd055..8fdf82a 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -23,6 +23,7 @@ MODULE_OBJS := \
 	frotz/buffer.o \
 	frotz/detection.o \
 	frotz/detection_tables.o \
+	frotz/err.o \
 	frotz/frotz.o \
 	scott/detection.o \
 	scott/scott.o


Commit: 4497a55a860a5ec5de47075e11cf69deffd2031d
    https://github.com/scummvm/scummvm/commit/4497a55a860a5ec5de47075e11cf69deffd2031d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Beginnings of Mem class

Changed paths:
  A engines/gargoyle/frotz/mem.cpp
  A engines/gargoyle/frotz/mem.h
    engines/gargoyle/detection.cpp
    engines/gargoyle/frotz/detection_tables.cpp
    engines/gargoyle/frotz/detection_tables.h
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/frotz.h
    engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 2836e04..4238e64 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -69,7 +69,7 @@ const Common::String &GargoyleEngine::getGameMD5() const {
 } // End of namespace Gargoyle
 
 #include "gargoyle/frotz/detection_tables.h"
-#define ZCODE(ID, NAME) { ID, Gargoyle::Frotz::NAME }
+#define ZCODE(ID, NAME) { ID, Gargoyle::Frotz::NAME##_DESC }
 
 static const PlainGameDescriptor gargoyleGames[] = {
 	{"zcode", "Zcode Games" },
diff --git a/engines/gargoyle/frotz/detection_tables.cpp b/engines/gargoyle/frotz/detection_tables.cpp
index f8d3f42..af856ed 100644
--- a/engines/gargoyle/frotz/detection_tables.cpp
+++ b/engines/gargoyle/frotz/detection_tables.cpp
@@ -25,49 +25,49 @@
 namespace Gargoyle {
 namespace Frotz {
 
-const char *const AMFV = "A Mind Forever Voyaging";
-const char *const ARTHUR = "Arthur: The Quest for Excalibur";
-const char *const BALLYHOO = "Ballyhoo";
-const char *const BEYONDZORK = "Beyond Zork";
-const char *const BORDERZONE = "Border Zone";
-const char *const BUREAUCRACY = "Bureaucracy";
-const char *const CUTTHROATS = "Cutthroats";
-const char *const DEADLINE = "Deadline";
-const char *const ENCHANTER = "Enchanter";
-const char *const HHGTTG = "The Hitchhiker's Guide to the Galaxy";
-const char *const HIJINX = "Hollywood Hijinx";
-const char *const INFIDEL = "Infidel";
-const char *const JOURNEY = "Journey";
-const char *const LGOP = "Leather Goddesses of Phobos";
-const char *const LGOP2 = "Leather Goddesses of Phobos 2";
-const char *const LURKING = "The Lurking Horror";
-const char *const MINIZORK1 = "Mini Zork I: The Great Underground Empire";
-const char *const MOONMIST = "Moonmist";
-const char *const NORDBERT = "Nord and Bert Couldn't Make Head or Tail of It";
-const char *const PLANETFALL = "Planetfall";
-const char *const PLUNDERED = "Plundered Hearts";
-const char *const SAMPLER1 = "Infocom Sampler 1";
-const char *const SAMPLER2 = "Infocom Sampler 2";
-const char *const SEASTALKER = "Seastalker";
-const char *const SHERLOCKRIDDLE = "Sherlock: The Riddle of the Crown Jewels";
-const char *const SHOGUN = "James Clavell's Shogun";
-const char *const SORCERER = "Sorcerer";
-const char *const SPELLBREAKER = "Spellbreaker";
-const char *const STARCROSS = "Starcross";
-const char *const STATIONFALL = "Stationfall";
-const char *const SUSPECT = "Suspect";
-const char *const SUSPENDED = "Suspended";
-const char *const TRINITY = "Trinity";
-const char *const WISHBRINGER = "Wishbringer";
-const char *const WITNESS = "The Witness";
-const char *const ZORK0 = "Zork Zero: The Revenge of Megaboz";
-const char *const ZORK1 = "Zork I: The Great Underground Empire";
-const char *const ZORK2 = "Zork II: The Wizard of Frobozz";
-const char *const ZORK3 = "Zork III: The Dungeon Master";
-const char *const ZTUU = "Zork: The Undiscovered Underground";
+const char *const AMFV_DESC = "A Mind Forever Voyaging";
+const char *const ARTHUR_DESC = "Arthur: The Quest for Excalibur";
+const char *const BALLYHOO_DESC = "Ballyhoo";
+const char *const BEYONDZORK_DESC = "Beyond Zork";
+const char *const BORDERZONE_DESC = "Border Zone";
+const char *const BUREAUCRACY_DESC = "Bureaucracy";
+const char *const CUTTHROATS_DESC = "Cutthroats";
+const char *const DEADLINE_DESC = "Deadline";
+const char *const ENCHANTER_DESC = "Enchanter";
+const char *const HHGTTG_DESC = "The Hitchhiker's Guide to the Galaxy";
+const char *const HIJINX_DESC = "Hollywood Hijinx";
+const char *const INFIDEL_DESC = "Infidel";
+const char *const JOURNEY_DESC = "Journey";
+const char *const LGOP_DESC = "Leather Goddesses of Phobos";
+const char *const LGOP2_DESC = "Leather Goddesses of Phobos 2";
+const char *const LURKING_DESC = "The Lurking Horror";
+const char *const MINIZORK1_DESC = "Mini Zork I: The Great Underground Empire";
+const char *const MOONMIST_DESC = "Moonmist";
+const char *const NORDBERT_DESC = "Nord and Bert Couldn't Make Head or Tail of It";
+const char *const PLANETFALL_DESC = "Planetfall";
+const char *const PLUNDERED_DESC = "Plundered Hearts";
+const char *const SAMPLER1_DESC = "Infocom Sampler 1";
+const char *const SAMPLER2_DESC = "Infocom Sampler 2";
+const char *const SEASTALKER_DESC = "Seastalker";
+const char *const SHERLOCKRIDDLE_DESC = "Sherlock: The Riddle of the Crown Jewels";
+const char *const SHOGUN_DESC = "James Clavell's Shogun";
+const char *const SORCERER_DESC = "Sorcerer";
+const char *const SPELLBREAKER_DESC = "Spellbreaker";
+const char *const STARCROSS_DESC = "Starcross";
+const char *const STATIONFALL_DESC = "Stationfall";
+const char *const SUSPECT_DESC = "Suspect";
+const char *const SUSPENDED_DESC = "Suspended";
+const char *const TRINITY_DESC = "Trinity";
+const char *const WISHBRINGER_DESC = "Wishbringer";
+const char *const WITNESS_DESC = "The Witness";
+const char *const ZORK0_DESC = "Zork Zero: The Revenge of Megaboz";
+const char *const ZORK1_DESC = "Zork I: The Great Underground Empire";
+const char *const ZORK2_DESC = "Zork II: The Wizard of Frobozz";
+const char *const ZORK3_DESC = "Zork III: The Dungeon Master";
+const char *const ZTUU_DESC = "Zork: The Undiscovered Underground";
 
 #define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
-#define ENTRY0(ID, DESC, VERSION, MD5, FILESIZE) { ID, DESC, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
+#define ENTRY0(ID, DESCRIPTION, VERSION, MD5, FILESIZE) { ID, DESCRIPTION##_DESC, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
 #define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
 
 const FrotzGameDescription FROTZ_GAMES[] = {
diff --git a/engines/gargoyle/frotz/detection_tables.h b/engines/gargoyle/frotz/detection_tables.h
index 1a9e4ee..48e0eb0 100644
--- a/engines/gargoyle/frotz/detection_tables.h
+++ b/engines/gargoyle/frotz/detection_tables.h
@@ -40,46 +40,46 @@ struct FrotzGameDescription {
 };
 
 extern const FrotzGameDescription FROTZ_GAMES[];
-extern const char *const AMFV;
-extern const char *const ARTHUR;
-extern const char *const BALLYHOO;
-extern const char *const BEYONDZORK;
-extern const char *const BORDERZONE;
-extern const char *const BUREAUCRACY;
-extern const char *const CUTTHROATS;
-extern const char *const DEADLINE;
-extern const char *const ENCHANTER;
-extern const char *const HHGTTG;
-extern const char *const HIJINX;
-extern const char *const INFIDEL;
-extern const char *const JOURNEY;
-extern const char *const LGOP;
-extern const char *const LGOP2;
-extern const char *const LURKING;
-extern const char *const MINIZORK1;
-extern const char *const MOONMIST;
-extern const char *const NORDBERT;
-extern const char *const PLANETFALL;
-extern const char *const PLUNDERED;
-extern const char *const SAMPLER1;
-extern const char *const SAMPLER2;
-extern const char *const SEASTALKER;
-extern const char *const SHERLOCKRIDDLE;
-extern const char *const SHOGUN;
-extern const char *const SORCERER;
-extern const char *const SPELLBREAKER;
-extern const char *const STARCROSS;
-extern const char *const STATIONFALL;
-extern const char *const SUSPECT;
-extern const char *const SUSPENDED;
-extern const char *const TRINITY;
-extern const char *const WISHBRINGER;
-extern const char *const WITNESS;
-extern const char *const ZORK0;
-extern const char *const ZORK1;
-extern const char *const ZORK2;
-extern const char *const ZORK3;
-extern const char *const ZTUU;
+extern const char *const AMFV_DESC;
+extern const char *const ARTHUR_DESC;
+extern const char *const BALLYHOO_DESC;
+extern const char *const BEYONDZORK_DESC;
+extern const char *const BORDERZONE_DESC;
+extern const char *const BUREAUCRACY_DESC;
+extern const char *const CUTTHROATS_DESC;
+extern const char *const DEADLINE_DESC;
+extern const char *const ENCHANTER_DESC;
+extern const char *const HHGTTG_DESC;
+extern const char *const HIJINX_DESC;
+extern const char *const INFIDEL_DESC;
+extern const char *const JOURNEY_DESC;
+extern const char *const LGOP_DESC;
+extern const char *const LGOP2_DESC;
+extern const char *const LURKING_DESC;
+extern const char *const MINIZORK1_DESC;
+extern const char *const MOONMIST_DESC;
+extern const char *const NORDBERT_DESC;
+extern const char *const PLANETFALL_DESC;
+extern const char *const PLUNDERED_DESC;
+extern const char *const SAMPLER1_DESC;
+extern const char *const SAMPLER2_DESC;
+extern const char *const SEASTALKER_DESC;
+extern const char *const SHERLOCKRIDDLE_DESC;
+extern const char *const SHOGUN_DESC;
+extern const char *const SORCERER_DESC;
+extern const char *const SPELLBREAKER_DESC;
+extern const char *const STARCROSS_DESC;
+extern const char *const STATIONFALL_DESC;
+extern const char *const SUSPECT_DESC;
+extern const char *const SUSPENDED_DESC;
+extern const char *const TRINITY_DESC;
+extern const char *const WISHBRINGER_DESC;
+extern const char *const WITNESS_DESC;
+extern const char *const ZORK0_DESC;
+extern const char *const ZORK1_DESC;
+extern const char *const ZORK2_DESC;
+extern const char *const ZORK3_DESC;
+extern const char *const ZTUU_DESC;
 
 } // End of namespace Frotz
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index 2a7bcd0..77c3303 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -29,7 +29,7 @@ namespace Frotz {
 Frotz *g_vm;
 
 Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
-		_storyId(STORY_UNKNOWN), _storySize(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
+		_storyId(UNKNOWN), _storySize(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		_ostream_screen(true), _ostream_script(false), _ostream_memory(false),
 		_ostream_record(false), _istream_replay(false), _message(false),
 		_cwin(0), _mwin(0), _mouse_x(0), _mouse_y(0), _menu_selected(0),
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index b0a7e1d..e1f3d9f 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -27,6 +27,7 @@
 #include "gargoyle/frotz/frotz_types.h"
 #include "gargoyle/frotz/buffer.h"
 #include "gargoyle/frotz/err.h"
+#include "gargoyle/frotz/mem.h"
 
 namespace Gargoyle {
 namespace Frotz {
@@ -39,6 +40,7 @@ public:
 	UserOptions _options;
 	Header _header;
 	Buffer _buffer;
+	Mem _mem;
 
 	// Story file name, id number and size
 	Common::String _storyName;
diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index 6fee562..505d656 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -91,18 +91,18 @@ enum ZCode {
 };
 
 enum Story {
-	STORY_BEYOND_ZORK,
-	STORY_SHERLOCK,
-	STORY_ZORK_ZERO,
-	STORY_SHOGUN,
-	STORY_ARTHUR,
-	STORY_JOURNEY,
-	STORY_LURKING_HORROR,
-	STORY_UNKNOWN
+	BEYOND_ZORK,
+	SHERLOCK,
+	ZORK_ZERO,
+	SHOGUN,
+	ARTHUR,
+	JOURNEY,
+	LURKING_HORROR,
+	UNKNOWN
 };
 
 typedef byte zbyte;
-typedef char zchar;
+typedef uint zchar;
 typedef uint16 zword;
 
 /**
diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
new file mode 100644
index 0000000..4bd2324
--- /dev/null
+++ b/engines/gargoyle/frotz/mem.cpp
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/mem.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+const Mem::StoryEntry Mem::RECORDS[25] = {
+	{       SHERLOCK,  21, "871214" },
+	{       SHERLOCK,  26, "880127" },
+	{    BEYOND_ZORK,  47, "870915" },
+	{    BEYOND_ZORK,  49, "870917" },
+	{    BEYOND_ZORK,  51, "870923" },
+	{    BEYOND_ZORK,  57, "871221" },
+	{      ZORK_ZERO, 296, "881019" },
+	{      ZORK_ZERO, 366, "890323" },
+	{      ZORK_ZERO, 383, "890602" },
+	{      ZORK_ZERO, 393, "890714" },
+	{         SHOGUN, 292, "890314" },
+	{         SHOGUN, 295, "890321" },
+	{         SHOGUN, 311, "890510" },
+	{         SHOGUN, 322, "890706" },
+	{         ARTHUR,  54, "890606" },
+	{         ARTHUR,  63, "890622" },
+	{         ARTHUR,  74, "890714" },
+	{        JOURNEY,  26, "890316" },
+	{        JOURNEY,  30, "890322" },
+	{        JOURNEY,  77, "890616" },
+	{        JOURNEY,  83, "890706" },
+	{ LURKING_HORROR, 203, "870506" },
+	{ LURKING_HORROR, 219, "870912" },
+	{ LURKING_HORROR, 221, "870918" },
+	{        UNKNOWN,   0, "------" }
+};
+
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
new file mode 100644
index 0000000..4abe464
--- /dev/null
+++ b/engines/gargoyle/frotz/mem.h
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_FROTZ_MEM
+#define GARGOYLE_FROTZ_MEM
+
+#include "gargoyle/frotz/frotz_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+class Mem {
+	struct StoryEntry {
+		Story _storyId;
+		zword _release;
+		char _serial[7];
+	};
+	static const StoryEntry RECORDS[25];
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 8fdf82a..f40a79a 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -25,6 +25,7 @@ MODULE_OBJS := \
 	frotz/detection_tables.o \
 	frotz/err.o \
 	frotz/frotz.o \
+	frotz/mem.o \
 	scott/detection.o \
 	scott/scott.o
 


Commit: b8fcb62dd242db6d50a252ec7cfabe97fe5c9b6c
    https://github.com/scummvm/scummvm/commit/b8fcb62dd242db6d50a252ec7cfabe97fe5c9b6c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added Blorb container file handling

Changed paths:
  A engines/gargoyle/blorb.cpp
  A engines/gargoyle/blorb.h
    engines/gargoyle/glk.h
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/blorb.cpp b/engines/gargoyle/blorb.cpp
new file mode 100644
index 0000000..064f9c4
--- /dev/null
+++ b/engines/gargoyle/blorb.cpp
@@ -0,0 +1,543 @@
+/* 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 "gargoyle/blorb.h"
+
+namespace Gargoyle {
+
+#define giblorb_Inited_Magic 0xB7012BEDU
+
+/**
+ * Describes one chunk of the Blorb file.
+ */
+struct giblorb_chunkdesc_struct {
+    glui32 type;
+    glui32 len;
+    glui32 startpos;	///< start of chunk header
+    glui32 datpos;		///< start of data (either startpos or startpos+8)
+
+    void *ptr;		///< pointer to malloc'd data, if loaded
+    int auxdatnum;	///< entry in the auxsound/auxpict array; -1 if none. This only applies to chunks that represent resources;
+};
+typedef giblorb_chunkdesc_struct giblorb_chunkdesc_t;
+
+/**
+ * Describes one resource in the Blorb file.
+ */
+struct giblorb_resdesc_struct {
+	glui32 usage;
+	glui32 resnum;
+	glui32 chunknum;
+};
+
+/**
+ * Holds the complete description of an open Blorb file. 
+ */
+struct giblorb_map_struct {
+    glui32 inited; ///< holds giblorb_Inited_Magic if the map structure is valid
+	Common::SeekableReadStream *file;
+
+    uint numchunks;
+    giblorb_chunkdesc_t *chunks;	///< list of chunk descriptors
+
+    int numresources;
+    giblorb_resdesc_t *resources;	///< list of resource descriptors
+    giblorb_resdesc_t **ressorted;	///< list of pointers to descriptors in map->resources -- sorted by usage and resource number.
+};
+
+/*--------------------------------------------------------------------------*/
+
+giblorb_err_t Blorb::giblorb_initialize() {
+	_file = nullptr;
+	_map = nullptr;
+	return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_create_map(Common::SeekableReadStream *file, giblorb_map_t **newmap) {
+	giblorb_err_t err;
+	giblorb_map_t *map;
+	glui32 readlen;
+	glui32 nextpos, totallength;
+	giblorb_chunkdesc_t *chunks;
+	int chunks_size, numchunks;
+	char buffer[16];
+
+	*newmap = nullptr;
+
+	if (!_libInited) {
+		err = giblorb_initialize();
+		if (err)
+			return err;
+		_libInited = true;
+	}
+
+	/* First, chew through the file and index the chunks. */
+	file->seek(0);
+
+	readlen = file->read(buffer, 12);
+	if (readlen != 12)
+		return giblorb_err_Read;
+
+	if (READ_BE_INT32(buffer + 0) != giblorb_ID_FORM)
+		return giblorb_err_Format;
+	if (READ_BE_INT32(buffer + 8) != giblorb_ID_IFRS)
+		return giblorb_err_Format;
+
+	totallength = READ_BE_INT32(buffer + 4) + 8;
+	nextpos = 12;
+
+	chunks_size = 8;
+	numchunks = 0;
+	chunks = new giblorb_chunkdesc_t[chunks_size];
+
+	while (nextpos < totallength) {
+		glui32 type, len;
+		int chunum;
+		giblorb_chunkdesc_t *chu;
+
+		file->seek(nextpos);
+
+		readlen = file->read(buffer, 8);
+		if (readlen != 8) {
+			delete[] chunks;
+			return giblorb_err_Read;
+		}
+
+		type = READ_BE_INT32(buffer + 0);
+		len = READ_BE_INT32(buffer + 4);
+
+		if (numchunks >= chunks_size) {
+			chunks_size *= 2;
+			chunks = new giblorb_chunkdesc_t[chunks_size];
+		}
+
+		chunum = numchunks;
+		chu = &(chunks[chunum]);
+		numchunks++;
+
+		chu->type = type;
+		chu->startpos = nextpos;
+		if (type == giblorb_ID_FORM) {
+			chu->datpos = nextpos;
+			chu->len = len + 8;
+		} else {
+			chu->datpos = nextpos + 8;
+			chu->len = len;
+		}
+		chu->ptr = nullptr;
+		chu->auxdatnum = -1;
+
+		nextpos = nextpos + len + 8;
+		if (nextpos & 1)
+			nextpos++;
+
+		if (nextpos > totallength) {
+			delete[] chunks;
+			return giblorb_err_Format;
+		}
+	}
+
+	// The basic IFF structure seems to be ok, and we have a list of chunks.
+	// Now we allocate the map structure itself.
+	map = new giblorb_map_t();
+	if (!map) {
+		delete[] chunks;
+		return giblorb_err_Alloc;
+	}
+
+	map->inited = giblorb_Inited_Magic;
+	map->file = file;
+	map->chunks = chunks;
+	map->numchunks = numchunks;
+	map->resources = nullptr;
+	map->ressorted = nullptr;
+	map->numresources = 0;
+
+	// Now we do everything else involved in loading the Blorb file,
+	// such as building resource lists.
+	err = giblorb_initialize_map(map);
+	if (err) {
+		giblorb_destroy_map(map);
+		return err;
+	}
+
+	*newmap = map;
+	return giblorb_err_None;
+}
+
+
+giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
+	// It is important that the map structure be kept valid during this function.
+	// If this returns an error, giblorb_destroy_map() will be called.
+	uint ix, jx;
+	giblorb_result_t chunkres;
+	giblorb_err_t err;
+	char *ptr;
+	glui32 len;
+	glui32 numres;
+	int gotindex = false;
+
+	for (ix = 0; ix<map->numchunks; ix++) {
+		giblorb_chunkdesc_t *chu = &map->chunks[ix];
+
+		switch (chu->type) {
+		case giblorb_ID_RIdx:
+			// Resource index chunk: build the resource list and sort it.
+
+			if (gotindex)
+				return giblorb_err_Format; // duplicate index chunk
+			err = giblorb_load_chunk_by_number(map, giblorb_method_Memory, &chunkres, ix);
+			if (err)
+				return err;
+
+			ptr = (char *)chunkres.data.ptr;
+			len = chunkres.length;
+			numres = READ_BE_INT32(ptr + 0);
+
+			if (numres) {
+				uint ix2;
+				giblorb_resdesc_t *resources;
+				giblorb_resdesc_t **ressorted;
+
+				if (len != numres * 12 + 4)
+					return giblorb_err_Format; // bad length field
+
+				resources = new giblorb_resdesc_t[numres];
+				ressorted = new giblorb_resdesc_t *[numres];
+				if (!ressorted || !resources) {
+					delete[] resources;
+					delete[] ressorted;
+					return giblorb_err_Alloc;
+				}
+
+				ix2 = 0;
+				for (jx = 0; jx < numres; jx++) {
+					giblorb_resdesc_t *res = &(resources[jx]);
+					glui32 respos;
+
+					res->usage = READ_BE_INT32(ptr + jx * 12 + 4);
+					res->resnum = READ_BE_INT32(ptr + jx * 12 + 8);
+					respos = READ_BE_INT32(ptr + jx * 12 + 12);
+
+					while (ix2 < map->numchunks
+						&& map->chunks[ix2].startpos < respos)
+						ix2++;
+
+					if (ix2 >= map->numchunks
+						|| map->chunks[ix2].startpos != respos) {
+						delete[] resources;
+						delete[] ressorted;
+						return giblorb_err_Format; // start pos does not match a real chunk
+					}
+
+					res->chunknum = ix2;
+
+					ressorted[jx] = res;
+				}
+
+				// Sort a resource list (actually a list of pointers to structures in map->resources.)
+				// This makes it easy to find resources by usage and resource number.
+				giblorb_qsort(ressorted, numres);
+
+				map->numresources = numres;
+				map->resources = resources;
+				map->ressorted = ressorted;
+			}
+
+			giblorb_unload_chunk(map, ix);
+			gotindex = true;
+			break;
+		}
+	}
+
+	return giblorb_err_None;
+}
+
+void Blorb::giblorb_qsort(giblorb_resdesc_t **list, size_t len) {
+	int ix, jx, res;
+	giblorb_resdesc_t *tmpptr, *pivot;
+
+	if (len < 6) {
+		// The list is short enough for a bubble-sort.
+		for (jx = len - 1; jx>0; jx--) {
+			for (ix = 0; ix<jx; ix++) {
+				res = sortsplot(list[ix], list[ix + 1]);
+				if (res > 0) {
+					tmpptr = list[ix];
+					list[ix] = list[ix + 1];
+					list[ix + 1] = tmpptr;
+				}
+			}
+		}
+	} else {
+		// Split the list.
+		pivot = list[len / 2];
+		ix = 0;
+		jx = len;
+		for (;;) {
+			while (ix < jx - 1 && sortsplot(list[ix], pivot) < 0)
+				ix++;
+			while (ix < jx - 1 && sortsplot(list[jx - 1], pivot) > 0)
+				jx--;
+			if (ix >= jx - 1)
+				break;
+			tmpptr = list[ix];
+			list[ix] = list[jx - 1];
+			list[jx - 1] = tmpptr;
+		}
+		ix++;
+		// Sort the halves.
+		giblorb_qsort(list + 0, ix);
+		giblorb_qsort(list + ix, len - ix);
+	}
+}
+
+giblorb_resdesc_t *Blorb::giblorb_bsearch(giblorb_resdesc_t *sample,
+	giblorb_resdesc_t **list, int len) {
+	int top, bot, val, res;
+
+	bot = 0;
+	top = len;
+
+	while (bot < top) {
+		val = (top + bot) / 2;
+		res = sortsplot(list[val], sample);
+		if (res == 0)
+			return list[val];
+		if (res < 0) {
+			bot = val + 1;
+		} else {
+			top = val;
+		}
+	}
+
+	return nullptr;
+}
+
+int Blorb::sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2) {
+	if (v1->usage < v2->usage)
+		return -1;
+	if (v1->usage > v2->usage)
+		return 1;
+	if (v1->resnum < v2->resnum)
+		return -1;
+	if (v1->resnum > v2->resnum)
+		return 1;
+	return 0;
+}
+
+giblorb_err_t Blorb::giblorb_destroy_map(giblorb_map_t *map) {
+	if (!map || !map->chunks || map->inited != giblorb_Inited_Magic)
+		return giblorb_err_NotAMap;
+
+	for (uint ix = 0; ix<map->numchunks; ix++) {
+		giblorb_chunkdesc_t *chu = &(map->chunks[ix]);
+		if (chu->ptr) {
+			delete chu->ptr;
+			chu->ptr = nullptr;
+		}
+	}
+
+	if (map->chunks) {
+		delete[] map->chunks;
+		map->chunks = nullptr;
+	}
+
+	map->numchunks = 0;
+
+	if (map->resources) {
+		delete[] map->resources;
+		map->resources = nullptr;
+	}
+
+	if (map->ressorted) {
+		delete[] map->ressorted;
+		map->ressorted = nullptr;
+	}
+
+	map->numresources = 0;
+	map->file = nullptr;
+	map->inited = 0;
+
+	delete map;
+
+	return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_load_chunk_by_type(giblorb_map_t *map,
+		glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count) {
+	uint ix;
+
+	for (ix = 0; ix < map->numchunks; ix++) {
+		if (map->chunks[ix].type == chunktype) {
+			if (count == 0)
+				break;
+			count--;
+		}
+	}
+
+	if (ix >= map->numchunks) {
+		return giblorb_err_NotFound;
+	}
+
+	return giblorb_load_chunk_by_number(map, method, res, ix);
+}
+
+giblorb_err_t Blorb::giblorb_load_chunk_by_number(giblorb_map_t *map,
+		glui32 method, giblorb_result_t *res, glui32 chunknum) {
+	giblorb_chunkdesc_t *chu;
+
+	if (chunknum < 0 || chunknum >= map->numchunks)
+		return giblorb_err_NotFound;
+
+	chu = &(map->chunks[chunknum]);
+
+	switch (method) {
+	case giblorb_method_DontLoad:
+		// do nothing
+		break;
+
+	case giblorb_method_FilePos:
+		res->data.startpos = chu->datpos;
+		break;
+
+	case giblorb_method_Memory:
+		if (!chu->ptr) {
+			glui32 readlen;
+			byte *dat = new byte[chu->len];
+
+			if (!dat)
+				return giblorb_err_Alloc;
+
+			map->file->seek(chu->datpos);
+
+			readlen = map->file->read(dat, chu->len);
+			if (readlen != chu->len)
+				return giblorb_err_Read;
+
+			chu->ptr = dat;
+		}
+
+		res->data.ptr = chu->ptr;
+		break;
+	}
+
+	res->chunknum = chunknum;
+	res->length = chu->len;
+	res->chunktype = chu->type;
+
+	return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum) {
+	giblorb_chunkdesc_t *chu;
+
+	if (chunknum < 0 || chunknum >= map->numchunks)
+		return giblorb_err_NotFound;
+
+	chu = &(map->chunks[chunknum]);
+
+	if (chu->ptr) {
+		delete chu->ptr;
+		chu->ptr = nullptr;
+	}
+
+	return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_load_resource(giblorb_map_t *map, glui32 method,
+		giblorb_result_t *res, glui32 usage, glui32 resnum) {
+	giblorb_resdesc_t sample;
+	giblorb_resdesc_t *found;
+
+	sample.usage = usage;
+	sample.resnum = resnum;
+
+	found = giblorb_bsearch(&sample, map->ressorted, map->numresources);
+
+	if (!found)
+		return giblorb_err_NotFound;
+
+	return giblorb_load_chunk_by_number(map, method, res, found->chunknum);
+}
+
+giblorb_err_t Blorb::giblorb_count_resources(giblorb_map_t *map,
+		glui32 usage, glui32 *num, glui32 *min, glui32 *max) {
+	int ix;
+	int count;
+	glui32 val;
+	glui32 minval, maxval;
+
+	count = 0;
+	minval = 0;
+	maxval = 0;
+
+	for (ix = 0; ix<map->numresources; ix++) {
+		if (map->resources[ix].usage == usage) {
+			val = map->resources[ix].resnum;
+			if (count == 0) {
+				count++;
+				minval = val;
+				maxval = val;
+			}
+			else {
+				count++;
+				if (val < minval)
+					minval = val;
+				if (val > maxval)
+					maxval = val;
+			}
+		}
+	}
+
+	if (num)
+		*num = count;
+	if (min)
+		*min = minval;
+	if (max)
+		*max = maxval;
+
+	return giblorb_err_None;
+}
+
+giblorb_err_t Blorb::giblorb_set_resource_map(Common::SeekableReadStream *file) {
+	giblorb_err_t err;
+
+	err = giblorb_create_map(file, &_map);
+	if (err) {
+		_map = nullptr;
+		return err;
+	}
+
+	_file = file;
+	return giblorb_err_None;
+}
+
+giblorb_map_t *Blorb::giblorb_get_resource_map(void) {
+	return _map;
+}
+
+bool Blorb::giblorb_is_resource_map(void) const {
+	return _map != nullptr;
+}
+
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/blorb.h b/engines/gargoyle/blorb.h
new file mode 100644
index 0000000..9de1947
--- /dev/null
+++ b/engines/gargoyle/blorb.h
@@ -0,0 +1,146 @@
+/* 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 GARGOYLE_BLORB_H
+#define GARGOYLE_BLORB_H
+
+#include "gargoyle/glk_types.h"
+#include "gargoyle/streams.h"
+
+namespace Gargoyle {
+
+
+/**
+ * Error type
+ */
+typedef glui32 giblorb_err_t;
+
+/**
+ * Error codes
+ */
+enum giblorbError {
+	giblorb_err_None        = 0,
+	giblorb_err_CompileTime = 1,
+	giblorb_err_Alloc       = 2,
+	giblorb_err_Read        = 3,
+	giblorb_err_NotAMap     = 4,
+	giblorb_err_Format      = 5,
+	giblorb_err_NotFound    = 6
+};
+
+/**
+ * Methods for loading a chunk
+ */
+enum giblorbMethod {
+	giblorb_method_DontLoad = 0,
+	giblorb_method_Memory   = 1,
+	giblorb_method_FilePos  = 2
+};
+
+enum {
+	giblorb_ID_Snd       = MKTAG('S', 'n', 'd', ' '),
+	giblorb_ID_Exec      = MKTAG('E', 'x', 'e', 'c'),
+	giblorb_ID_Pict      = MKTAG('P', 'i', 'c', 't'),
+	giblorb_ID_Copyright = MKTAG('(', 'c', ')', ' '),
+	giblorb_ID_AUTH      = MKTAG('A', 'U', 'T', 'H'),
+	giblorb_ID_ANNO      = MKTAG('A', 'N', 'N', 'O')
+};
+
+
+enum {
+	giblorb_ID_MOD  = MKTAG('M', 'O', 'D', ' '),
+	giblorb_ID_FORM = MKTAG('F', 'O', 'R', 'M'),
+	giblorb_ID_IFRS = MKTAG('I', 'F', 'R', 'S'),
+	giblorb_ID_RIdx = MKTAG('R', 'I', 'd', 'x'),
+	giblorb_ID_OGG  = MKTAG('O', 'G', 'G', 'V'),
+
+	// non-standard types
+	giblorb_ID_MIDI = MKTAG('M', 'I', 'D', 'I'),
+	giblorb_ID_MP3  = MKTAG('M', 'P', '3', ' '),
+	giblorb_ID_WAVE = MKTAG('W', 'A', 'V', 'E')
+};
+
+/**
+ * Holds the complete description of an open Blorb file.
+ * This type is opaque for normal interpreter use.
+ */
+typedef struct giblorb_map_struct giblorb_map_t;
+
+/* giblorb_result_t: Result when you try to load a chunk. */
+typedef struct giblorb_result_struct {
+	glui32 chunknum; // The chunk number (for use in giblorb_unload_chunk(), etc.)
+	union {
+		void *ptr;			///< A pointer to the data (if you used giblorb_method_Memory)
+		glui32 startpos;	///< The position in the file (if you used giblorb_method_FilePos)
+	} data;
+
+	glui32 length;			///< The length of the data
+	glui32 chunktype;		///< The type of the chunk.
+} giblorb_result_t;
+
+typedef struct giblorb_resdesc_struct giblorb_resdesc_t;
+
+class Blorb {
+private:
+	bool _libInited;
+	Common::SeekableReadStream *_file;
+	giblorb_map_t *_map;
+private:
+	/**
+	 * Initializes Blorb
+	 */
+	giblorb_err_t giblorb_initialize();
+
+	giblorb_err_t giblorb_initialize_map(giblorb_map_t *map);
+	void giblorb_qsort(giblorb_resdesc_t **list, size_t len);
+	giblorb_resdesc_t *giblorb_bsearch(giblorb_resdesc_t *sample,
+		giblorb_resdesc_t **list, int len);
+	int sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2);
+public:
+	/**
+	 * Constructor
+	 */
+	Blorb() : _libInited(false), _file(nullptr), _map(nullptr) {}
+
+	giblorb_err_t giblorb_set_resource_map(Common::SeekableReadStream *file);
+	giblorb_map_t *giblorb_get_resource_map(void);
+	bool giblorb_is_resource_map(void) const;
+
+
+	giblorb_err_t giblorb_create_map(Common::SeekableReadStream *file, giblorb_map_t **newmap);
+	giblorb_err_t giblorb_destroy_map(giblorb_map_t *map);
+
+	giblorb_err_t giblorb_load_chunk_by_type(giblorb_map_t *map,
+		glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count);
+	giblorb_err_t giblorb_load_chunk_by_number(giblorb_map_t *map,
+		glui32 method, giblorb_result_t *res, glui32 chunknum);
+	giblorb_err_t giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum);
+
+	giblorb_err_t giblorb_load_resource(giblorb_map_t *map, glui32 method,
+		giblorb_result_t *res, glui32 usage, glui32 resnum);
+	giblorb_err_t giblorb_count_resources(giblorb_map_t *map,
+		glui32 usage, glui32 *num, glui32 *min, glui32 *max);
+};
+
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index b14546c..9dc7f97 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -25,6 +25,7 @@
 
 #include "gargoyle/gargoyle.h"
 #include "gargoyle/glk_types.h"
+#include "gargoyle/blorb.h"
 #include "gargoyle/time.h"
 #include "gargoyle/windows.h"
 
@@ -33,7 +34,7 @@ namespace Gargoyle {
 /**
  * Implements the GLK interface
  */
-class Glk : public GargoyleEngine {
+class Glk : public GargoyleEngine, public Blorb {
 private:
 	bool _gliFirstEvent;
 	unsigned char _charTolowerTable[256];
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index f40a79a..d359429 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -1,6 +1,7 @@
 MODULE := engines/gargoyle
 
 MODULE_OBJS := \
+	blorb.o \
 	conf.o \
 	detection.o \
 	events.o \


Commit: fc4c3293d10ae25c6792cff8c5648ff4d4369697
    https://github.com/scummvm/scummvm/commit/fc4c3293d10ae25c6792cff8c5648ff4d4369697
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Add parsing if game file is a Blorb container

Changed paths:
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/frotz.h
    engines/gargoyle/frotz/mem.cpp
    engines/gargoyle/frotz/mem.h


diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index 77c3303..d5cdaf3 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -40,9 +40,15 @@ Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst,
 }
 
 void Frotz::runGame(Common::SeekableReadStream *gameFile) {
+	initialize();
+
 	// TODO
 }
 
+void Frotz::initialize() {
+	_mem.initialize();
+}
+
 Common::Error Frotz::loadGameState(int slot) {
 	return Common::kNoError;
 }
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index e1f3d9f..e1fb36c 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -36,6 +36,11 @@ namespace Frotz {
  * Frotz interpreter for Z-code games
  */
 class Frotz : public Glk {
+private:
+	/**
+	 * Perform any initialization
+	 */
+	void initialize();
 public:
 	UserOptions _options;
 	Header _header;
@@ -43,7 +48,7 @@ public:
 	Mem _mem;
 
 	// Story file name, id number and size
-	Common::String _storyName;
+	Common::SeekableReadStream *_gameFile;
 	Story _storyId;
 	size_t _storySize;
 
diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
index 4bd2324..1b43e93 100644
--- a/engines/gargoyle/frotz/mem.cpp
+++ b/engines/gargoyle/frotz/mem.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "gargoyle/frotz/mem.h"
+#include "gargoyle/frotz/frotz.h"
 #include "common/textconsole.h"
 
 namespace Gargoyle {
@@ -54,6 +55,51 @@ const Mem::StoryEntry Mem::RECORDS[25] = {
 	{        UNKNOWN,   0, "------" }
 };
 
+Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0) {
+}
+
+void Mem::initialize() {
+/*
+	long size;
+	zword addr;
+	unsigned n;
+	int i, j;
+	*/
+	initializeStoryFile();
+
+	// TODO: More stuff
+}
+
+void Mem::initializeStoryFile() {
+	Common::SeekableReadStream *f = g_vm->_gameFile;
+	giblorb_map_t *map;
+	giblorb_result_t res;
+	uint32 magic;
+
+	story_fp = f;
+	magic = f->readUint32BE();
+
+	if (magic == MKTAG('F', 'O', 'R', 'M')) {
+		if (g_vm->giblorb_set_resource_map(f))
+			error("This Blorb file seems to be invalid.");
+
+		map = g_vm->giblorb_get_resource_map();
+
+		if (g_vm->giblorb_load_resource(map, giblorb_method_FilePos, &res, giblorb_ID_Exec, 0))
+			error("This Blorb file does not contain an executable chunk.");
+		if (res.chunktype != MKTAG('Z', 'C', 'O', 'D'))
+			error("This Blorb file contains an executable chunk, but it is not a Z-code file.");
+
+		blorb_ofs = res.data.startpos;
+		blorb_len = res.length;
+	} else {
+		blorb_ofs = 0;
+		blorb_len = f->size();
+	}
+
+	if (blorb_len < 64)
+		error("This file is too small to be a Z-code file.");
+}
 
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index 4abe464..8882a58 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -35,6 +35,24 @@ class Mem {
 		char _serial[7];
 	};
 	static const StoryEntry RECORDS[25];
+private:
+	Common::SeekableReadStream *story_fp;
+	uint blorb_ofs, blorb_len;
+private:
+	/**
+	 * Handles setting the story file, parsing it if it's a Blorb file
+	 */
+	void initializeStoryFile();
+public:
+	/**
+	 * Constructor
+	 */
+	Mem();
+
+	/**
+	 * Initialize
+	 */
+	void initialize();
 };
 
 } // End of namespace Frotz


Commit: f19f92e396e348e095e51becc902cdb5ef62618b
    https://github.com/scummvm/scummvm/commit/f19f92e396e348e095e51becc902cdb5ef62618b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Move Header to be an ancestor class to Mem

Changed paths:
    engines/gargoyle/frotz/err.cpp
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/frotz.h
    engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/frotz/mem.h


diff --git a/engines/gargoyle/frotz/err.cpp b/engines/gargoyle/frotz/err.cpp
index b25b75c..f9a3ce9 100644
--- a/engines/gargoyle/frotz/err.cpp
+++ b/engines/gargoyle/frotz/err.cpp
@@ -85,7 +85,8 @@ void Errors::runtimeError(int errNum) {
     
     if ((g_vm->_options._err_report_mode == ERR_REPORT_ALWAYS)
 			|| (g_vm->_options._err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
-		long pc = g_vm->getPC();
+		long pc;
+		GET_PC(pc);
 		printString("Warning: ");
 		printString(ERR_MESSAGES[errNum - 1]);
 		printString(" (PC = ");
diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index d5cdaf3..64b9f54 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -57,10 +57,5 @@ Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
 	return Common::kNoError;
 }
 
-uint Frotz::getPC() const {
-	// TODO
-	return 0;
-}
-
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index e1fb36c..4dd5834 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -101,11 +101,6 @@ public:
 	 * Save the game
 	 */
 	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
-
-	/**
-	 * Get the current PC
-	 */
-	uint getPC() const;
 };
 
 extern Frotz *g_vm;
diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index 505d656..3c7312c 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -32,6 +32,18 @@ namespace Frotz {
 #define MAX_UNDO_SLOTS 500
 #define STACK_SIZE 20
 
+#define lo(v)	(v & 0xff)
+#define hi(v)	(v >> 8)
+
+#define SET_WORD(addr,v)  WRITE_BE_UINT16(addr,v)
+#define LOW_WORD(addr,v)  READ_BE_UINT16(addr,v)
+#define HIGH_WORD(addr,v) READ_BE_UINT16(addr,v)
+#define HIGH_LONG(addr,v) READ_BE_UINT32(addr,v)
+#define CODE_WORD(v)       v = g_vm->_mem.readWord()
+#define CODE_IDX_WORD(v,i) v = g_vm->_mem.readWord(i)
+#define GET_PC(v)          v = g_vm->_mem.getPC()
+#define SET_PC(v)          g_vm->_mem.setPC(v);
+
 /* There are four error reporting modes: never report errors;
  * report only the first time a given error type occurs;
  * report every time an error occurs;
@@ -134,67 +146,6 @@ struct UserOptions {
 	}
 };
 
-/**
- * Story file header data
- */
-struct Header {
-	zbyte _version;
-	zbyte _config;
-	zword _release;
-	zword _resident_size;
-	zword _start_pc;
-	zword _dictionary;
-	zword _objects;
-	zword _globals;
-	zword _dynamic_size;
-	zword _flags;
-	zbyte _serial[6];
-	zword _abbreviations;
-	zword _file_size;
-	zword _checksum;
-	zbyte _interpreter_number;
-	zbyte _interpreter_version;
-	zbyte _screen_rows;
-	zbyte _screen_cols;
-	zword _screen_width;
-	zword _screen_height;
-	zbyte _font_height = 1;
-	zbyte _font_width = 1;
-	zword _functions_offset;
-	zword _strings_offset;
-	zbyte _default_background;
-	zbyte _default_foreground;
-	zword _terminating_keys;
-	zword _line_width;
-	zbyte _standard_high;
-	zbyte _standard_low;
-	zword _alphabet;
-	zword _extension_table;
-	zbyte _user_name[8];
-
-	zword _hx_table_size;
-	zword _hx_mouse_x;
-	zword _hx_mouse_y;
-	zword _hx_unicode_table;
-	zword _hx_flags;
-	zword _hx_fore_colour;
-	zword _hx_back_colour;
-
-	Header() : _version(0), _config(0), _release(0), _resident_size(0), _start_pc(0),
-			_dictionary(0), _objects(0), _globals(0), _dynamic_size(0), _flags(0),
-			_abbreviations(0), _file_size(0), _checksum(0), _interpreter_number(0),
-			_interpreter_version(0), _screen_rows(0), _screen_cols(0), _screen_width(0),
-			_screen_height(0), _font_height(1), _font_width(1), _functions_offset(0),
-			_strings_offset(0), _default_background(0), _default_foreground(0),
-			_terminating_keys(0), _line_width(0), _standard_high(1), _standard_low(1),
-			_alphabet(0), _extension_table(0),
-			_hx_table_size(0), _hx_mouse_x(0), _hx_mouse_y(0), _hx_unicode_table(0),
-			_hx_flags(0), _hx_fore_colour(0), _hx_back_colour(0) {
-		Common::fill(&_serial[0], &_serial[6], '\0');
-		Common::fill(&_user_name[0], &_user_name[8], '\0');
-	}
-};
-
 } // End of namespace Frotz
 } // End of namespace Gargoyle
 
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index 8882a58..68c7b85 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -28,7 +28,69 @@
 namespace Gargoyle {
 namespace Frotz {
 
-class Mem {
+
+/**
+ * Story file header data
+ */
+struct Header {
+	zbyte h_version;
+	zbyte h_config;
+	zword h_release;
+	zword h_resident_size;
+	zword h_start_pc;
+	zword h_dictionary;
+	zword h_objects;
+	zword h_globals;
+	zword h_dynamic_size;
+	zword h_flags;
+	zbyte h_serial[6];
+	zword h_abbreviations;
+	zword h_file_size;
+	zword h_checksum;
+	zbyte h_interpreter_number;
+	zbyte h_interpreter_version;
+	zbyte h_screen_rows;
+	zbyte h_screen_cols;
+	zword h_screen_width;
+	zword h_screen_height;
+	zbyte h_font_height = 1;
+	zbyte h_font_width = 1;
+	zword h_functions_offset;
+	zword h_strings_offset;
+	zbyte h_default_background;
+	zbyte h_default_foreground;
+	zword h_terminating_keys;
+	zword h_line_width;
+	zbyte h_standard_high;
+	zbyte h_standard_low;
+	zword h_alphabet;
+	zword h_extension_table;
+	zbyte h_user_name[8];
+
+	zword hx_table_size;
+	zword hx_mouse_x;
+	zword hx_mouse_y;
+	zword hx_unicode_table;
+	zword hx_flags;
+	zword hx_fore_colour;
+	zword hx_back_colour;
+
+	Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
+			h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
+			h_abbreviations(0), h_file_size(0), h_checksum(0), h_interpreter_number(0),
+			h_interpreter_version(0), h_screen_rows(0), h_screen_cols(0), h_screen_width(0),
+			h_screen_height(0), h_font_height(1), h_font_width(1), h_functions_offset(0),
+			h_strings_offset(0), h_default_background(0), h_default_foreground(0),
+			h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
+			h_alphabet(0), h_extension_table(0),
+			hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
+			hx_flags(0), hx_fore_colour(0), hx_back_colour(0) {
+		Common::fill(&h_serial[0], &h_serial[6], '\0');
+		Common::fill(&h_user_name[0], &h_user_name[8], '\0');
+	}
+};
+
+class Mem : public Header {
 	struct StoryEntry {
 		Story _storyId;
 		zword _release;
@@ -38,6 +100,8 @@ class Mem {
 private:
 	Common::SeekableReadStream *story_fp;
 	uint blorb_ofs, blorb_len;
+	byte *pcp;
+	byte *zmp;
 private:
 	/**
 	 * Handles setting the story file, parsing it if it's a Blorb file
@@ -53,6 +117,31 @@ public:
 	 * Initialize
 	 */
 	void initialize();
+
+	/**
+	 * Read a word
+	 */
+	zword readWord() {
+		pcp += 2;
+		return READ_BE_UINT16(pcp - 2);
+	}
+
+	/**
+	 * Read a word at a given index relative to pcp
+	 */
+	zword readWord(size_t ofs) {
+		return READ_BE_UINT16(pcp + ofs);
+	}
+
+	/**
+	 * Get the PC
+	 */
+	uint getPC() const { return pcp - zmp; }
+
+	/**
+	 * Set the PC
+	 */
+	void setPC(uint ofs) { pcp = zmp + ofs; }
 };
 
 } // End of namespace Frotz


Commit: cc9b52e5ac9c0e08e86f07c94713c3ebc54dc680
    https://github.com/scummvm/scummvm/commit/cc9b52e5ac9c0e08e86f07c94713c3ebc54dc680
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Implement the removeSaveState method

Changed paths:
    engines/gargoyle/detection.cpp


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 4238e64..5ff0cc7 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -242,6 +242,8 @@ int GargoyleMetaEngine::getMaximumSaveSlot() const {
 }
 
 void GargoyleMetaEngine::removeSaveState(const char *target, int slot) const {
+	Common::String filename = Common::String::format("%s.%03d", target, slot);
+	g_system->getSavefileManager()->removeSavefile(filename);
 }
 
 SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {


Commit: 38a458139aef488acf48abfcf61233c529984cb1
    https://github.com/scummvm/scummvm/commit/38a458139aef488acf48abfcf61233c529984cb1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add Gargoyle engine to credits.pl

Changed paths:
    devtools/credits.pl


diff --git a/devtools/credits.pl b/devtools/credits.pl
index ad011b7..3cfa7b7 100755
--- a/devtools/credits.pl
+++ b/devtools/credits.pl
@@ -623,6 +623,13 @@ begin_credits("Credits");
 			begin_section("Fullpipe");
 				add_person("Eugene Sandulenko", "sev", "");
 			end_section();
+
+			begin_section("Gargoyle");
+				add_person("Paul Gilbert", "dreammaster", "");
+				add_person("Tor Andersson", "", "GLK library");
+				add_person("Stefan Jokisch", "", "Frotz interpreter");
+				add_person("Alan Cox", "", "ScottFree interpreter");
+			end_section();
 			
 			begin_section("Gnap");
 				add_person("Arnaud Boutonné", "Strangerke", "");


Commit: a083eb3d5ca682a0b465d383413f62ff38b8e814
    https://github.com/scummvm/scummvm/commit/a083eb3d5ca682a0b465d383413f62ff38b8e814
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added bulk of game processor

Changed paths:
  A engines/gargoyle/frotz/glk_interface.cpp
  A engines/gargoyle/frotz/glk_interface.h
  A engines/gargoyle/frotz/processor.cpp
  A engines/gargoyle/frotz/processor.h
  A engines/gargoyle/frotz/processor_input.cpp
  A engines/gargoyle/frotz/processor_maths.cpp
  A engines/gargoyle/frotz/processor_objects.cpp
  A engines/gargoyle/frotz/processor_screen.cpp
  A engines/gargoyle/frotz/processor_streams.cpp
  A engines/gargoyle/frotz/processor_table.cpp
  A engines/gargoyle/frotz/processor_text.cpp
  A engines/gargoyle/frotz/processor_variables.cpp
    engines/gargoyle/frotz/buffer.cpp
    engines/gargoyle/frotz/err.cpp
    engines/gargoyle/frotz/err.h
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/frotz.h
    engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/frotz/mem.cpp
    engines/gargoyle/frotz/mem.h
    engines/gargoyle/glk.h
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/frotz/buffer.cpp b/engines/gargoyle/frotz/buffer.cpp
index 15ec771..7d9e644 100644
--- a/engines/gargoyle/frotz/buffer.cpp
+++ b/engines/gargoyle/frotz/buffer.cpp
@@ -61,7 +61,7 @@ void Buffer::flush() {
 void Buffer::printChar(zchar c) {
 	static bool flag = false;
 
-	if (g_vm->_message || g_vm->_ostream_memory || g_vm->_enableBuffering) {
+	if (g_vm->message || g_vm->ostream_memory || g_vm->enable_buffering) {
 		if (!flag) {
 			// Characters 0 and ZC_RETURN are special cases
 			if (c == ZC_RETURN) {
diff --git a/engines/gargoyle/frotz/err.cpp b/engines/gargoyle/frotz/err.cpp
index f9a3ce9..f75b764 100644
--- a/engines/gargoyle/frotz/err.cpp
+++ b/engines/gargoyle/frotz/err.cpp
@@ -67,14 +67,15 @@ Errors::Errors() {
 	Common::fill(&_count[0], &_count[ERR_NUM_ERRORS], 0);
 }
 
-void Errors::runtimeError(int errNum) {
-    int wasfirst;
+void Errors::runtimeError(ErrorCode errNum) {
+#ifdef TODO
+	int wasfirst;
     
     if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
 	return;
 
-    if (g_vm->_options._err_report_mode == ERR_REPORT_FATAL
-		|| (!g_vm->_options._ignore_errors && errNum <= ERR_MAX_FATAL)) {
+    if (g_vm->_err_report_mode == ERR_REPORT_FATAL
+		|| (!g_vm->_ignore_errors && errNum <= ERR_MAX_FATAL)) {
 		g_vm->_buffer.flush();
 		error(ERR_MESSAGES[errNum - 1]);
 		return;
@@ -83,9 +84,9 @@ void Errors::runtimeError(int errNum) {
     wasfirst = (_count[errNum - 1] == 0);
     _count[errNum - 1]++;
     
-    if ((g_vm->_options._err_report_mode == ERR_REPORT_ALWAYS)
-			|| (g_vm->_options._err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
-		long pc;
+    if ((g_vm->_err_report_mode == ERR_REPORT_ALWAYS)
+			|| (_err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
+		long pc = g_vm->_processor
 		GET_PC(pc);
 		printString("Warning: ");
 		printString(ERR_MESSAGES[errNum - 1]);
@@ -93,7 +94,7 @@ void Errors::runtimeError(int errNum) {
 		printLong(pc, 16);
 		printChar(')');
         
-		if (g_vm->_options._err_report_mode == ERR_REPORT_ONCE) {
+		if (_err_report_mode == ERR_REPORT_ONCE) {
 			printString(" (will ignore further occurrences)");
 		} else {
 			printString(" (occurence ");
@@ -103,6 +104,7 @@ void Errors::runtimeError(int errNum) {
 
 		newLine();
     }
+#endif
 }
 
 void Errors::printLong(uint value, int base) {
diff --git a/engines/gargoyle/frotz/err.h b/engines/gargoyle/frotz/err.h
index f61d726..e71c988 100644
--- a/engines/gargoyle/frotz/err.h
+++ b/engines/gargoyle/frotz/err.h
@@ -28,26 +28,51 @@
 namespace Gargoyle {
 namespace Frotz {
 
-#define ERR_NUM_ERRORS 33
-#define ERR_MAX_FATAL 19
+enum ErrorCode {
+	ERR_TEXT_BUF_OVF   = 1,		///< Text buffer overflow
+	ERR_STORE_RANGE    = 2,		///< Store out of dynamic memory
+	ERR_DIV_ZERO       = 3,		///< Division by zero
+	ERR_ILL_OBJ        = 4,		///< Illegal object
+	ERR_ILL_ATTR       = 5,		///< Illegal attribute
+	ERR_NO_PROP        = 6,		///< No such property
+	ERR_STK_OVF        = 7,		///< Stack overflow
+	ERR_ILL_CALL_ADDR  = 8,		///< Call to illegal address
+	ERR_CALL_NON_RTN   = 9,		///< Call to non-routine
+	ERR_STK_UNDF       = 10,	///< Stack underflow
+	ERR_ILL_OPCODE     = 11,	///< Illegal opcode
+	ERR_BAD_FRAME      = 12,	///< Bad stack frame
+	ERR_ILL_JUMP_ADDR  = 13,	///< Jump to illegal address
+	ERR_SAVE_IN_INTER  = 14,	///< Can't save while in interrupt
+	ERR_STR3_NESTING   = 15,	///< Nesting stream #3 too deep
+	ERR_ILL_WIN        = 16,	///< Illegal window
+	ERR_ILL_WIN_PROP   = 17,	///< Illegal window property
+	ERR_ILL_PRINT_ADDR = 18,	///< Print at illegal address
+	ERR_DICT_LEN       = 19,	///< Illegal dictionary word length
+	ERR_MAX_FATAL      = 19,
+
+	// Less serious errors
+	ERR_JIN_0            = 20,	///< @jin called with object 0
+	ERR_GET_CHILD_0      = 21,	///< @get_child called with object 0
+	ERR_GET_PARENT_0     = 22,	///< @get_parent called with object 0
+	ERR_GET_SIBLING_0    = 23,	///< @get_sibling called with object 0
+	ERR_GET_PROP_ADDR_0  = 24,	///< @get_prop_addr called with object 0
+	ERR_GET_PROP_0       = 25,	///< @get_prop called with object 0
+	ERR_PUT_PROP_0       = 26,	///< @put_prop called with object 0
+	ERR_CLEAR_ATTR_0     = 27,	///< @clear_attr called with object 0
+	ERR_SET_ATTR_0       = 28,	///< @set_attr called with object 0
+	ERR_TEST_ATTR_0      = 29,	///< @test_attr called with object 0
+	ERR_MOVE_OBJECT_0    = 30,	///< @move_object called moving object 0
+	ERR_MOVE_OBJECT_TO_0 = 31,	///< @move_object called moving into object 0
+	ERR_REMOVE_OBJECT_0  = 32,	///< @remove_object called with object 0
+	ERR_GET_NEXT_PROP_0  = 33,	///< @get_next_prop called with object 0
+	ERR_NUM_ERRORS       = 33
+};
 
 class Errors {
 private:
 	static const char *const ERR_MESSAGES[ERR_NUM_ERRORS];
 	int _count[ERR_NUM_ERRORS];
-public:
-	/**
-	 * Constructor
-	 */
-	Errors();
-
-	/**
-	 * An error has occurred. Ignore it, pass it to os_fatal or report
-	 * it according to err_report_mode.
-	 * @param errNum		Numeric code for error (1 to ERR_NUM_ERRORS)
-	 */
-	void runtimeError(int errNum);
-
+private:
 	/**
 	 * Print an unsigned 32bit number in decimal or hex.
 	 */
@@ -67,6 +92,23 @@ public:
 	 * Add a newline
 	 */
 	void newLine();
+protected:
+	/**
+	 * Get the PC. Is implemented by the Processor class, which derives from Errors
+	 */
+	virtual zword getPC() const = 0;
+public:
+	/**
+	 * Constructor
+	 */
+	Errors();
+
+	/**
+	 * An error has occurred. Ignore it, pass it to os_fatal or report
+	 * it according to err_report_mode.
+	 * @param errNum		Numeric code for error (1 to ERR_NUM_ERRORS)
+	 */
+	void runtimeError(ErrorCode errNum);
 };
 
 } // End of namespace Frotz
diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index 64b9f54..c0a9442 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -28,27 +28,18 @@ namespace Frotz {
 
 Frotz *g_vm;
 
-Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
-		_storyId(UNKNOWN), _storySize(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
-		_ostream_screen(true), _ostream_script(false), _ostream_memory(false),
-		_ostream_record(false), _istream_replay(false), _message(false),
-		_cwin(0), _mwin(0), _mouse_x(0), _mouse_y(0), _menu_selected(0),
-		_enableWrapping(false), _enableScripting(true), _enableScrolling(false),
-		_enableBuffering(false), _reserveMem(0) {
+Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+		Processor(syst, gameDesc) {
 	g_vm = this;
-	Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
 }
 
 void Frotz::runGame(Common::SeekableReadStream *gameFile) {
+	story_fp = gameFile;
 	initialize();
 
 	// TODO
 }
 
-void Frotz::initialize() {
-	_mem.initialize();
-}
-
 Common::Error Frotz::loadGameState(int slot) {
 	return Common::kNoError;
 }
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index 4dd5834..c032265 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -23,11 +23,7 @@
 #ifndef GARGOYLE_FROTZ_FROTZ
 #define GARGOYLE_FROTZ_FROTZ
 
-#include "gargoyle/glk.h"
-#include "gargoyle/frotz/frotz_types.h"
-#include "gargoyle/frotz/buffer.h"
-#include "gargoyle/frotz/err.h"
-#include "gargoyle/frotz/mem.h"
+#include "gargoyle/frotz/processor.h"
 
 namespace Gargoyle {
 namespace Frotz {
@@ -35,52 +31,7 @@ namespace Frotz {
 /**
  * Frotz interpreter for Z-code games
  */
-class Frotz : public Glk {
-private:
-	/**
-	 * Perform any initialization
-	 */
-	void initialize();
-public:
-	UserOptions _options;
-	Header _header;
-	Buffer _buffer;
-	Mem _mem;
-
-	// Story file name, id number and size
-	Common::SeekableReadStream *_gameFile;
-	Story _storyId;
-	size_t _storySize;
-
-	// Stack data
-	zword _stack[STACK_SIZE];
-	zword *_sp;
-	zword *_fp;
-	zword _frameCount;
-
-	// IO streams
-	bool _ostream_screen;
-	bool _ostream_script;
-	bool _ostream_memory;
-	bool _ostream_record;
-	bool _istream_replay;
-	bool _message;
-
-	// Current window and mouse data
-	int _cwin;
-	int _mwin;
-	int _mouse_y;
-	int _mouse_x;
-	int _menu_selected;
-
-	// Window attributes
-	bool _enableWrapping;
-	bool _enableScripting;
-	bool _enableScrolling;
-	bool _enableBuffering;
-
-	// Size of memory to reserve (in bytes)
-	size_t _reserveMem;
+class Frotz : public Processor {
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index 3c7312c..c4f4298 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -35,15 +35,6 @@ namespace Frotz {
 #define lo(v)	(v & 0xff)
 #define hi(v)	(v >> 8)
 
-#define SET_WORD(addr,v)  WRITE_BE_UINT16(addr,v)
-#define LOW_WORD(addr,v)  READ_BE_UINT16(addr,v)
-#define HIGH_WORD(addr,v) READ_BE_UINT16(addr,v)
-#define HIGH_LONG(addr,v) READ_BE_UINT32(addr,v)
-#define CODE_WORD(v)       v = g_vm->_mem.readWord()
-#define CODE_IDX_WORD(v,i) v = g_vm->_mem.readWord(i)
-#define GET_PC(v)          v = g_vm->_mem.getPC()
-#define SET_PC(v)          g_vm->_mem.setPC(v);
-
 /* There are four error reporting modes: never report errors;
  * report only the first time a given error type occurs;
  * report every time an error occurs;
@@ -113,6 +104,113 @@ enum Story {
 	UNKNOWN
 };
 
+enum Version {
+	V1 = 1,
+	V2 = 2,
+	V3 = 3,
+	V4 = 4,
+	V5 = 5,
+	V6 = 6,
+	V7 = 7,
+	V8 = 8,
+	V9 = 9
+};
+
+enum ConfigFlag {
+	CONFIG_BYTE_SWAPPED = 0x01, ///< Story file is byte swapped         - V3 
+	CONFIG_TIME         = 0x02, ///< Status line displays time          - V3 
+	CONFIG_TWODISKS     = 0x04, ///< Story file occupied two disks      - V3 
+	CONFIG_TANDY        = 0x08, ///< Tandy licensed game                - V3 
+	CONFIG_NOSTATUSLINE = 0x10, ///< Interpr can't support status lines - V3 
+	CONFIG_SPLITSCREEN  = 0x20, ///< Interpr supports split screen mode - V3 
+	CONFIG_PROPORTIONAL = 0x40, ///< Interpr uses proportional font     - V3 
+	
+	CONFIG_COLOUR       = 0x01, ///< Interpr supports colour            - V5+
+	CONFIG_PICTURES	    = 0x02, ///< Interpr supports pictures	        - V6 
+	CONFIG_BOLDFACE     = 0x04, ///< Interpr supports boldface style    - V4+
+	CONFIG_EMPHASIS     = 0x08, ///< Interpr supports emphasis style    - V4+
+	CONFIG_FIXED        = 0x10, ///< Interpr supports fixed width style - V4+
+	CONFIG_SOUND	    = 0x20, ///< Interpr supports sound             - V6 
+	CONFIG_TIMEDINPUT   = 0x80, ///< Interpr supports timed input       - V4+
+	
+	SCRIPTING_FLAG	  = 0x0001, ///< Outputting to transscription file  - V1+
+	FIXED_FONT_FLAG   = 0x0002, ///< Use fixed width font               - V3+
+	REFRESH_FLAG 	  = 0x0004, ///< Refresh the screen                 - V6 
+	GRAPHICS_FLAG	  = 0x0008, ///< Game wants to use graphics         - V5+
+	OLD_SOUND_FLAG	  = 0x0010, ///< Game wants to use sound effects    - V3 
+	UNDO_FLAG	  = 0x0010, ///< Game wants to use UNDO feature     - V5+
+	MOUSE_FLAG	  = 0x0020, ///< Game wants to use a mouse          - V5+
+	COLOUR_FLAG	  = 0x0040, ///< Game wants to use colours          - V5+
+	SOUND_FLAG	  = 0x0080, ///< Game wants to use sound effects    - V5+
+	MENU_FLAG	  = 0x0100  ///< Game wants to use menus            - V6 
+};
+
+enum {
+	TRANSPARENT_FLAG = 0x0001 ///< Game wants to use transparency     - V6
+};
+
+enum FrotzInterp {
+#define INTERP_DEFAULT 0
+#define INTERP_DEC_20 1
+#define INTERP_APPLE_IIE 2
+#define INTERP_MACINTOSH 3
+#define INTERP_AMIGA 4
+#define INTERP_ATARI_ST 5
+#define INTERP_MSDOS 6
+#define INTERP_CBM_128 7
+#define INTERP_CBM_64 8
+#define INTERP_APPLE_IIC 9
+#define INTERP_APPLE_IIGS 10
+#define INTERP_TANDY 11
+};
+
+enum Colour {
+	BLACK_COLOUR       = 2,
+	RED_COLOUR         = 3,
+	GREEN_COLOUR       = 4,
+	YELLOW_COLOUR      = 5,
+	BLUE_COLOUR        = 6,
+	MAGENTA_COLOUR     = 7,
+	CYAN_COLOUR        = 8,
+	WHITE_COLOUR       = 9,
+	GREY_COLOUR        = 10,	///< INTERP_MSDOS only
+	LIGHTGREY_COLOUR   = 10, 	///< INTERP_AMIGA only
+	MEDIUMGREY_COLOUR  = 11, 	///< INTERP_AMIGA only
+	DARKGREY_COLOUR    = 12,	///< INTERP_AMIGA only
+	TRANSPARENT_COLOUR = 15		///< ZSpec 1.1
+};
+
+enum Style {
+	REVERSE_STYLE     = 1,
+	BOLDFACE_STYLE    = 2,
+	EMPHASIS_STYLE    = 4,
+	FIXED_WIDTH_STYLE = 8
+};
+
+enum FontStyle {
+	TEXT_FONT        = 1,
+	PICTURE_FONT     = 2,
+	GRAPHICS_FONT    = 3,
+	FIXED_WIDTH_FONT = 4
+};
+
+/*** Constants for os_beep */
+
+#define BEEP_HIGH       1
+#define BEEP_LOW        2
+
+/*** Constants for os_restart_game */
+
+#define RESTART_BEGIN 0
+#define RESTART_WPROP_SET 1
+#define RESTART_END 2
+
+/*** Constants for os_menu */
+
+#define MENU_NEW 0
+#define MENU_ADD 1
+#define MENU_REMOVE 2
+
 typedef byte zbyte;
 typedef uint zchar;
 typedef uint16 zword;
diff --git a/engines/gargoyle/frotz/glk_interface.cpp b/engines/gargoyle/frotz/glk_interface.cpp
new file mode 100644
index 0000000..e1311fb
--- /dev/null
+++ b/engines/gargoyle/frotz/glk_interface.cpp
@@ -0,0 +1,272 @@
+/* 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 "gargoyle/frotz/glk_interface.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+GlkInterface::GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+		Glk(syst, gameDesc),
+		oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
+		curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
+		curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
+		gos_lower(nullptr), gos_curwin(nullptr), gos_linepending(0), gos_linebuf(nullptr),
+		gos_linewin(nullptr), gos_channel(nullptr), cwin(0), mwin(0), mouse_x(0), mouse_y(0),
+		menu_selected(0), ostream_screen(false), ostream_script(false), ostream_memory(false),
+		ostream_record(false), istream_replay(false), message(false),
+		enable_wrapping(false), enable_scripting(false), enable_scrolling(false),
+		enable_buffering(false), next_sample(0), next_volume(0),
+		_soundLocked(false), _soundPlaying(false) {
+	Common::fill(&statusline[0], &statusline[256], '\0');
+}
+
+int GlkInterface::os_char_width(zchar z) {
+	return 1;
+}
+
+int GlkInterface::os_string_width(const zchar *s) {
+	int width = 0;
+	zchar c;
+	while ((c = *s++) != 0)
+		if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
+			s++;
+		else
+			width += os_char_width(c);
+	return width;
+}
+
+int GlkInterface::os_string_length(zchar *s) {
+	int length = 0;
+	while (*s++) length++;
+	return length;
+}
+
+void GlkInterface::os_prepare_sample(int a) {
+	glk_sound_load_hint(a, 1);
+}
+
+void GlkInterface::os_finish_with_sample(int a) {
+	glk_sound_load_hint(a, 0);
+}
+
+void GlkInterface::os_start_sample(int number, int volume, int repeats, zword eos) {
+	int vol;
+
+	if (!gos_channel) {
+		gos_channel = glk_schannel_create(0);
+		if (!gos_channel)
+			return;
+	}
+
+	switch (volume) {
+	case   1: vol = 0x02000; break;
+	case   2: vol = 0x04000; break;
+	case   3: vol = 0x06000; break;
+	case   4: vol = 0x08000; break;
+	case   5: vol = 0x0a000; break;
+	case   6: vol = 0x0c000; break;
+	case   7: vol = 0x0e000; break;
+	case   8: vol = 0x10000; break;
+	default:  vol = 0x20000; break;
+	}
+
+	// we dont do repeating or eos-callback for now...
+	glk_schannel_play_ext(gos_channel, number, 1, 0);
+	glk_schannel_set_volume(gos_channel, vol);
+}
+
+void GlkInterface::os_stop_sample(int a) {
+	if (!gos_channel)
+		return;
+	glk_schannel_stop(gos_channel);
+}
+
+void GlkInterface::os_beep(int volume) {
+}
+
+void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) {
+	// TODO
+}
+
+void GlkInterface::start_next_sample() {
+	// TODO
+}
+
+void GlkInterface::gos_update_width() {
+	glui32 width;
+	if (gos_upper) {
+		glk_window_get_size(gos_upper, &width, nullptr);
+		h_screen_cols = width;
+		SET_BYTE(H_SCREEN_COLS, width);
+		if (curx > width) {
+			glk_window_move_cursor(gos_upper, 0, cury - 1);
+			curx = 1;
+		}
+	}
+}
+
+void GlkInterface::gos_update_height() {
+	glui32 height_upper;
+	glui32 height_lower;
+	if (gos_curwin) {
+		glk_window_get_size(gos_upper, nullptr, &height_upper);
+		glk_window_get_size(gos_lower, nullptr, &height_lower);
+		h_screen_rows = height_upper + height_lower + 1;
+		SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
+	}
+}
+
+void GlkInterface::reset_status_ht() {
+	glui32 height;
+	if (gos_upper) {
+		glk_window_get_size(gos_upper, nullptr, &height);
+		if (mach_status_ht != height) {
+			glk_window_set_arrangement(
+				glk_window_get_parent(gos_upper),
+				winmethod_Above | winmethod_Fixed,
+				mach_status_ht, nullptr);
+		}
+	}
+}
+
+void GlkInterface::erase_window(zword w) {
+	if (w == 0)
+		glk_window_clear(gos_lower);
+	else if (gos_upper) {
+#ifdef GARGLK
+		garglk_set_reversevideo_stream(
+			glk_window_get_stream(gos_upper),
+			true);
+#endif /* GARGLK */
+		
+		memset(statusline, ' ', sizeof statusline);
+		glk_window_clear(gos_upper);
+		reset_status_ht();
+		curr_status_ht = 0;
+	}
+}
+
+void GlkInterface::split_window(zword lines) {
+	if (!gos_upper)
+		return;
+
+	// The top line is always set for V1 to V3 games
+	if (h_version < V4)
+		lines++;
+
+	if (!lines || lines > curr_status_ht) {
+		glui32 height;
+
+		glk_window_get_size(gos_upper, nullptr, &height);
+		if (lines != height)
+			glk_window_set_arrangement(
+				glk_window_get_parent(gos_upper),
+				winmethod_Above | winmethod_Fixed,
+				lines, nullptr);
+		curr_status_ht = lines;
+	}
+	mach_status_ht = lines;
+	if (cury > lines)
+	{
+		glk_window_move_cursor(gos_upper, 0, 0);
+		curx = cury = 1;
+	}
+	gos_update_width();
+
+	if (h_version == V3)
+		glk_window_clear(gos_upper);
+}
+
+void GlkInterface::restart_screen() {
+	erase_window(0);
+	erase_window(1);
+	split_window(0);
+}
+
+void GlkInterface::packspaces(zchar *src, zchar *dst) {
+	int killing = 0;
+	while (*src) {
+		if (*src == 0x20202020)
+			*src = ' ';
+		if (*src == ' ')
+			killing++;
+		else
+			killing = 0;
+		if (killing > 2)
+			src++;
+		else
+			*dst++ = *src++;
+	}
+
+	*dst = 0;
+}
+
+void GlkInterface::smartstatusline() {
+	zchar packed[256];
+	zchar buf[256];
+	zchar *a, *b, *c, *d;
+	int roomlen, scorelen, scoreofs;
+	int len, tmp;
+
+	packspaces(statusline, packed);
+	len = os_string_length(packed);
+
+	a = packed;
+	while (a[0] == ' ')
+		a++;
+
+	b = a;
+	while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' '))
+		b++;
+
+	c = b;
+	while (c[0] == ' ')
+		c++;
+
+	d = packed + len - 1;
+	while (d[0] == ' ' && d > c)
+		d--;
+	if (d[0] != ' ' && d[0] != 0)
+		d++;
+	if (d < c)
+		d = c;
+
+	roomlen = b - a;
+	scorelen = d - c;
+	scoreofs = h_screen_cols - scorelen - 2;
+	if (scoreofs <= roomlen)
+		scoreofs = roomlen + 2;
+
+	for (tmp = 0; tmp < h_screen_cols; tmp++)
+		buf[tmp] = ' ';
+
+	memcpy(buf + 1 + scoreofs, c, scorelen * sizeof(zchar));
+	memcpy(buf + 1, a, roomlen * sizeof(zchar));
+
+	glk_window_move_cursor(gos_upper, 0, 0);
+	glk_put_buffer_uni(buf, h_screen_cols);
+	glk_window_move_cursor(gos_upper, cury - 1, curx - 1);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/glk_interface.h b/engines/gargoyle/frotz/glk_interface.h
new file mode 100644
index 0000000..ea3f60c
--- /dev/null
+++ b/engines/gargoyle/frotz/glk_interface.h
@@ -0,0 +1,147 @@
+/* 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 GARGOYLE_FROTZ_GLK_INTERFACE
+#define GARGOYLE_FROTZ_GLK_INTERFACE
+
+#include "gargoyle/glk.h"
+#include "gargoyle/frotz/mem.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+enum SoundEffect {
+	EFFECT_PREPARE     = 1,
+	EFFECT_PLAY        = 2,
+	EFFECT_STOP        = 3,
+	EFFECT_FINISH_WITH = 4
+};
+
+/**
+ * Implements an intermediate interface on top of the GLK layer, providing screen
+ * and sound effect handling
+ */
+class GlkInterface : public Glk, public virtual Mem {
+public:
+	zchar statusline[256];
+	int oldstyle;
+	int curstyle;
+	int cury;
+	int curx;
+	int fixforced;
+
+	int curr_fg;
+	int curr_bg;
+	int curr_font;
+	int prev_font;
+	int temp_font;
+
+	int curr_status_ht;
+	int mach_status_ht;
+
+	winid_t gos_status;
+	winid_t gos_upper;
+	winid_t gos_lower;
+	winid_t gos_curwin;
+	int gos_linepending;
+	zchar *gos_linebuf;
+	winid_t gos_linewin;
+	schanid_t gos_channel;
+
+	// Current window and mouse data
+	int cwin;
+	int mwin;
+	int mouse_y;
+	int mouse_x;
+	int menu_selected;
+
+	// IO streams
+	bool ostream_screen;
+	bool ostream_script;
+	bool ostream_memory;
+	bool ostream_record;
+	bool istream_replay;
+	bool message;
+
+	// Window attributes
+	bool enable_wrapping;
+	bool enable_scripting;
+	bool enable_scrolling;
+	bool enable_buffering;
+
+	// Sound fields
+	int next_sample;
+	int next_volume;
+
+	bool _soundLocked;
+	bool _soundPlaying;
+protected:
+	int os_char_width(zchar z);
+	int os_string_width(const zchar *s);
+	int os_string_length(zchar *s);
+	void os_prepare_sample(int a);
+	void os_finish_with_sample(int a);
+
+	/**
+	 * Play the given sample at the given volume (ranging from 1 to 8 and
+	 * 255 meaning a default volume). The sound is played once or several
+	 * times in the background (255 meaning forever). In Z-code 3 the
+	 * repeats value is always 0 and the number of repeats is taken from
+	 * the sound file itself. The end_of_sound function is called as soon
+	 * as the sound finishes.
+	 */
+	void os_start_sample(int number, int volume, int repeats, zword eos);
+
+	void os_stop_sample(int a);
+	void os_beep(int volume);
+
+	/**
+	 * Call the IO interface to play a sample.
+	 */
+	void start_sample(int number, int volume, int repeats, zword eos);
+
+	void start_next_sample();
+	void gos_update_width();
+	void gos_update_height();
+	void reset_status_ht();
+	void erase_window(zword w);
+	void split_window(zword lines);
+	void restart_screen();
+
+	/**
+	 * statusline overflowed the window size ... bad game!
+	 * so ... split status text into regions, reformat and print anew.
+	 */
+	void packspaces(zchar *src, zchar *dst);
+
+	void smartstatusline();
+public:
+	/**
+	 * Constructor
+	 */
+	GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc);
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
index 1b43e93..cb5a73b 100644
--- a/engines/gargoyle/frotz/mem.cpp
+++ b/engines/gargoyle/frotz/mem.cpp
@@ -22,12 +22,13 @@
 
 #include "gargoyle/frotz/mem.h"
 #include "gargoyle/frotz/frotz.h"
+#include "common/memstream.h"
 #include "common/textconsole.h"
 
 namespace Gargoyle {
 namespace Frotz {
 
-const Mem::StoryEntry Mem::RECORDS[25] = {
+const Header::StoryEntry Header::RECORDS[25] = {
 	{       SHERLOCK,  21, "871214" },
 	{       SHERLOCK,  26, "880127" },
 	{    BEYOND_ZORK,  47, "870915" },
@@ -55,28 +56,99 @@ const Mem::StoryEntry Mem::RECORDS[25] = {
 	{        UNKNOWN,   0, "------" }
 };
 
-Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0) {
+void Header::loadHeader(Common::SeekableReadStream &f) {
+	h_version = f.readByte();
+	h_config = f.readByte();
+
+	if (h_version < V1 || h_version > V8)
+		error("Unknown Z-code version");
+
+	if (h_version == V6)
+		error("Cannot play Z-code version 6");
+
+	if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
+		error("Byte swapped story file");
+
+	h_release = f.readUint16BE();
+	h_resident_size = f.readUint16BE();
+	h_start_pc = f.readUint16BE();
+	h_dictionary = f.readUint16BE();
+	h_objects = f.readUint16BE();
+	h_globals = f.readUint16BE();
+	h_dynamic_size = f.readUint16BE();
+	h_flags = f.readUint16BE();
+	f.read(h_serial, 6);
+	
+	/* Auto-detect buggy story files that need special fixes */
+	_storyId = UNKNOWN;
+
+	for (int i = 0; RECORDS[i]._storyId != UNKNOWN; ++i) {
+		if (h_release == RECORDS[i]._release) {
+			if (!strncmp((const char *)h_serial, RECORDS[i]._serial, 6)) {
+				_storyId = RECORDS[i]._storyId;
+				break;
+			}
+		}
+	}
+
+	h_abbreviations = f.readUint16BE();
+	h_file_size = f.readUint16BE();
+	h_checksum = f.readUint16BE();
+	
+	f.seek(H_FUNCTIONS_OFFSET);
+	h_functions_offset = f.readUint16BE();
+	h_strings_offset = f.readUint16BE();
+	f.seek(H_TERMINATING_KEYS);
+	h_terminating_keys = f.readUint16BE();
+	f.seek(H_ALPHABET);
+	h_alphabet = f.readUint16BE();
+	h_extension_table = f.readUint16BE();
+
+
+	// Zork Zero Macintosh doesn't have the graphics flag set
+	if (_storyId == ZORK_ZERO && h_release == 296)
+		h_flags |= GRAPHICS_FLAG;
+}
+
+/*--------------------------------------------------------------------------*/
+
+Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0), story_size(0) {
 }
 
 void Mem::initialize() {
-/*
-	long size;
-	zword addr;
-	unsigned n;
-	int i, j;
-	*/
 	initializeStoryFile();
+	loadGameHeader();
+
+	// Allocate memory for story data
+	if ((zmp = (zbyte *)realloc(zmp, story_size)) == nullptr)
+		error("Out of memory");
+
+	// Load story file in chunks of 32KB
+	uint n = 0x8000;
+
+	for (uint size = 64; size < story_size; size += n) {
+		if (story_size - size < 0x8000)
+			n = story_size - size;
+
+		setPC(size);
 
-	// TODO: More stuff
+		if (story_fp->read(pcp, n) != n)
+			error("Story file read error");
+
+	}
+
+	// Read header extension table
+	hx_table_size = get_header_extension(HX_TABLE_SIZE);
+	hx_unicode_table = get_header_extension(HX_UNICODE_TABLE);
+	hx_flags = get_header_extension(HX_FLAGS);
 }
 
 void Mem::initializeStoryFile() {
-	Common::SeekableReadStream *f = g_vm->_gameFile;
+	Common::SeekableReadStream *f = story_fp;
 	giblorb_map_t *map;
 	giblorb_result_t res;
 	uint32 magic;
 
-	story_fp = f;
 	magic = f->readUint32BE();
 
 	if (magic == MKTAG('F', 'O', 'R', 'M')) {
@@ -101,5 +173,41 @@ void Mem::initializeStoryFile() {
 		error("This file is too small to be a Z-code file.");
 }
 
+void Mem::loadGameHeader() {
+	// Load header
+	zmp = new byte[64];
+	story_fp->seek(blorb_ofs);
+	story_fp->read(zmp, 64);
+
+	Common::MemoryReadStream h(zmp, 64);
+	loadHeader(h);
+
+	// Calculate story file size in bytes
+	if (h_file_size != 0) {
+		story_size = (long)2 * h_file_size;
+
+		if (h_version >= V4)
+			story_size *= 2;
+		if (h_version >= V6)
+			story_size *= 2;
+	} else {
+		// Some old games lack the file size entry
+		story_size = blorb_len;
+	}
+}
+
+zword Mem::get_header_extension(int entry) {
+	zword addr;
+	zword val;
+
+	if (h_extension_table == 0 || entry > hx_table_size)
+		return 0;
+
+	addr = h_extension_table + 2 * entry;
+	LOW_WORD(addr, val);
+
+	return val;   
+}
+
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index 68c7b85..d37cdd3 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -28,11 +28,71 @@
 namespace Gargoyle {
 namespace Frotz {
 
+#define SET_WORD(addr,v)  zmp[addr] = hi(v); zmp[addr+1] = lo(v)
+#define LOW_WORD(addr,v)  v = READ_BE_UINT16(&zmp[addr])
+#define HIGH_WORD(addr,v) v = READ_BE_UINT16(&zmp[addr])
+#define HIGH_LONG(addr,v) v = READ_BE_UINT32(&zmp[addr])
+#define SET_BYTE(addr,v)   zmp[addr] = v
+#define LOW_BYTE(addr,v)   v = zmp[addr]
+
+enum HeaderByte {
+	H_VERSION             = 0,
+	H_CONFIG              = 1,
+	H_RELEASE             = 2,
+	H_RESIDENT_SIZE       = 4,
+	H_START_PC            = 6,
+	H_DICTIONARY          = 8,
+	H_OBJECTS             = 10,
+	H_GLOBALS             = 12,
+	H_DYNAMIC_SIZE        = 14,
+	H_FLAGS               = 16,
+	H_SERIAL              = 18,
+	H_ABBREVIATIONS       = 24,
+	H_FILE_SIZE           = 26,
+	H_CHECKSUM            = 28,
+	H_INTERPRETER_NUMBER  = 30,
+	H_INTERPRETER_VERSION = 31,
+	H_SCREEN_ROWS         = 32,
+	H_SCREEN_COLS         = 33,
+	H_SCREEN_WIDTH        = 34,
+	H_SCREEN_HEIGHT       = 36,
+	H_FONT_HEIGHT         = 38,		///< this is the font width in V5
+	H_FONT_WIDTH          = 39,		///< this is the font height in V5
+	H_FUNCTIONS_OFFSET    = 40,
+	H_STRINGS_OFFSET      = 42,
+	H_DEFAULT_BACKGROUND  = 44,
+	H_DEFAULT_FOREGROUND  = 45,
+	H_TERMINATING_KEYS    = 46,
+	H_LINE_WIDTH          = 48,
+	H_STANDARD_HIGH       = 50,
+	H_STANDARD_LOW        = 51,
+	H_ALPHABET            = 52,
+	H_EXTENSION_TABLE     = 54,
+	H_USER_NAME           = 56
+};
+
+enum {
+	HX_TABLE_SIZE    = 0,
+	HX_MOUSE_X       = 1,
+	HX_MOUSE_Y       = 2,
+	HX_UNICODE_TABLE = 3,
+	HX_FLAGS         = 4,
+	HX_FORE_COLOUR   = 5,
+	HX_BACK_COLOUR   = 6
+};
 
 /**
  * Story file header data
  */
 struct Header {
+private:
+	struct StoryEntry {
+		Story _storyId;
+		zword _release;
+		char _serial[7];
+	};
+	static const StoryEntry RECORDS[25];
+public:
 	zbyte h_version;
 	zbyte h_config;
 	zword h_release;
@@ -75,6 +135,11 @@ struct Header {
 	zword hx_fore_colour;
 	zword hx_back_colour;
 
+	Story _storyId;
+
+	/**
+	 * Constructor
+	 */
 	Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
 			h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
 			h_abbreviations(0), h_file_size(0), h_checksum(0), h_interpreter_number(0),
@@ -84,22 +149,22 @@ struct Header {
 			h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
 			h_alphabet(0), h_extension_table(0),
 			hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
-			hx_flags(0), hx_fore_colour(0), hx_back_colour(0) {
+			hx_flags(0), hx_fore_colour(0), hx_back_colour(0), _storyId(UNKNOWN) {
 		Common::fill(&h_serial[0], &h_serial[6], '\0');
 		Common::fill(&h_user_name[0], &h_user_name[8], '\0');
 	}
+
+	/**
+	 * Load the header
+	 */
+	void loadHeader(Common::SeekableReadStream &f);
 };
 
 class Mem : public Header {
-	struct StoryEntry {
-		Story _storyId;
-		zword _release;
-		char _serial[7];
-	};
-	static const StoryEntry RECORDS[25];
-private:
+protected:
 	Common::SeekableReadStream *story_fp;
 	uint blorb_ofs, blorb_len;
+	uint story_size;
 	byte *pcp;
 	byte *zmp;
 private:
@@ -107,6 +172,11 @@ private:
 	 * Handles setting the story file, parsing it if it's a Blorb file
 	 */
 	void initializeStoryFile();
+
+	/**
+	 * Handles loading the game header
+	 */
+	void loadGameHeader();
 public:
 	/**
 	 * Constructor
@@ -116,7 +186,7 @@ public:
 	/**
 	 * Initialize
 	 */
-	void initialize();
+	virtual void initialize();
 
 	/**
 	 * Read a word
@@ -142,6 +212,11 @@ public:
 	 * Set the PC
 	 */
 	void setPC(uint ofs) { pcp = zmp + ofs; }
+
+	/**
+	 * Read a value from the header extension (former mouse table).
+	 */
+	zword get_header_extension(int entry);
 };
 
 } // End of namespace Frotz
diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
new file mode 100644
index 0000000..1a31898
--- /dev/null
+++ b/engines/gargoyle/frotz/processor.cpp
@@ -0,0 +1,659 @@
+/* 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 "gargoyle/frotz/processor.h"
+#include "gargoyle/frotz/frotz.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Stubs to replace with actual code
+zword save_undo() { return 0; }
+zword restore_undo() { return 0; }
+
+
+Opcode Processor::var_opcodes[64] = {
+	&Processor::__illegal__,
+	&Processor::z_je,
+	&Processor::z_jl,
+	&Processor::z_jg,
+	&Processor::z_dec_chk,
+	&Processor::z_inc_chk,
+	&Processor::z_jin,
+	&Processor::z_test,
+	&Processor::z_or,
+	&Processor::z_and,
+	&Processor::z_test_attr,
+	&Processor::z_set_attr,
+	&Processor::z_clear_attr,
+	&Processor::z_store,
+	&Processor::z_insert_obj,
+	&Processor::z_loadw,
+	&Processor::z_loadb,
+	&Processor::z_get_prop,
+	&Processor::z_get_prop_addr,
+	&Processor::z_get_next_prop,
+	&Processor::z_add,
+	&Processor::z_sub,
+	&Processor::z_mul,
+	&Processor::z_div,
+	&Processor::z_mod,
+	&Processor::z_call_s,
+	&Processor::z_call_n,
+	&Processor::z_set_colour,
+	&Processor::z_throw,
+	&Processor::__illegal__,
+	&Processor::__illegal__,
+	&Processor::__illegal__,
+	&Processor::z_call_s,
+	&Processor::z_storew,
+	&Processor::z_storeb,
+	&Processor::z_put_prop,
+	&Processor::z_read,
+	&Processor::z_print_char,
+	&Processor::z_print_num,
+	&Processor::z_random,
+	&Processor::z_push,
+	&Processor::z_pull,
+	&Processor::z_split_window,
+	&Processor::z_set_window,
+	&Processor::z_call_s,
+	&Processor::z_erase_window,
+	&Processor::z_erase_line,
+	&Processor::z_set_cursor,
+	&Processor::z_get_cursor,
+	&Processor::z_set_text_style,
+	&Processor::z_buffer_mode,
+	&Processor::z_output_stream,
+	&Processor::z_input_stream,
+	&Processor::z_sound_effect,
+	&Processor::z_read_char,
+	&Processor::z_scan_table,
+	&Processor::z_not,
+	&Processor::z_call_n,
+	&Processor::z_call_n,
+	&Processor::z_tokenise,
+	&Processor::z_encode_text,
+	&Processor::z_copy_table,
+	&Processor::z_print_table,
+	&Processor::z_check_arg_count
+};
+
+Opcode Processor::ext_opcodes[64] = {
+	&Processor::z_save,
+	&Processor::z_restore,
+	&Processor::z_log_shift,
+	&Processor::z_art_shift,
+	&Processor::z_set_font,
+	&Processor::__illegal__,		// glkify - Processor::z_draw_picture,
+	&Processor::__illegal__,		// glkify - Processor::z_picture_data,
+	&Processor::__illegal__,		// glkify - Processor::z_erase_picture,
+	&Processor::__illegal__,		// glkify - Processor::z_set_margins,
+	&Processor::z_save_undo,
+	&Processor::z_restore_undo,
+	&Processor::z_print_unicode,
+	&Processor::z_check_unicode,
+	&Processor::z_set_true_colour,	// spec 1.1
+	&Processor::__illegal__,
+	&Processor::__illegal__,
+	&Processor::__illegal__,		// glkify - Processor::z_move_window,
+	&Processor::__illegal__,		// glkify - Processor::z_window_size,
+	&Processor::__illegal__,		// glkify - Processor::z_window_style,
+	&Processor::__illegal__,		// glkify - Processor::z_get_wind_prop,
+	&Processor::__illegal__,		// glkify - Processor::z_scroll_window,
+	&Processor::z_pop_stack,
+	&Processor::__illegal__,		// glkify - Processor::z_read_mouse,
+	&Processor::__illegal__,		// glkify - Processor::z_mouse_window,
+	&Processor::z_push_stack,
+	&Processor::__illegal__,		// glkify - Processor::z_put_wind_prop,
+	&Processor::z_print_form,
+	&Processor::z_make_menu,
+	&Processor::__illegal__,		// glkify - Processor::z_picture_table
+	&Processor::z_buffer_screen,	// spec 1.1
+};
+
+Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
+		GlkInterface(syst, gameDesc), Mem(), Errors(), UserOptions(),
+		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
+		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
+		_randomInterval(0), _randomCtr(0), first_restart(true) {
+	static const Opcode OP0_OPCODES[16] = {
+		&Processor::z_rtrue,
+		&Processor::z_rfalse,
+		&Processor::z_print,
+		&Processor::z_print_ret,
+		&Processor::z_nop,
+		&Processor::z_save,
+		&Processor::z_restore,
+		&Processor::z_restart,
+		&Processor::z_ret_popped,
+		&Processor::z_catch,
+		&Processor::z_quit,
+		&Processor::z_new_line,
+		&Processor::z_show_status,
+		&Processor::z_verify,
+		&Processor::__extended__,
+		&Processor::z_piracy
+	};
+	static const Opcode OP1_OPCODES[16] = {
+		&Processor::z_jz,
+		&Processor::z_get_sibling,
+		&Processor::z_get_child,
+		&Processor::z_get_parent,
+		&Processor::z_get_prop_len,
+		&Processor::z_inc,
+		&Processor::z_dec,
+		&Processor::z_print_addr,
+		&Processor::z_call_s,
+		&Processor::z_remove_obj,
+		&Processor::z_print_obj,
+		&Processor::z_ret,
+		&Processor::z_jump,
+		&Processor::z_print_paddr,
+		&Processor::z_load,
+		&Processor::z_call_n
+	};
+
+	Common::copy(&OP0_OPCODES[0], &OP0_OPCODES[16], op0_opcodes);
+	Common::copy(&OP1_OPCODES[0], &OP1_OPCODES[16], op1_opcodes);
+	Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
+	Common::fill(&zargs[0], &zargs[8], 0);
+}
+
+void Processor::initialize() {
+	Mem::initialize();
+
+	if (h_version <= V4) {
+		op0_opcodes[9] = &Processor::z_pop;
+		op1_opcodes[15] = &Processor::z_not;
+	} else {
+		op0_opcodes[9] = &Processor::z_catch;
+		op1_opcodes[15] = &Processor::z_call_n;
+	}
+}
+
+void Processor::load_operand(zbyte type) {
+	zword value;
+
+	if (type & 2) {
+		// variable
+		zbyte variable;
+
+		CODE_BYTE(variable);
+
+		if (variable == 0)
+			value = *_sp++;
+		else if (variable < 16)
+			value = *(_fp - variable);
+		else {
+			zword addr = h_globals + 2 * (variable - 16);
+			LOW_WORD(addr, value);
+		}
+	} else if (type & 1) {
+		// small constant
+		zbyte bvalue;
+
+		CODE_BYTE(bvalue);
+		value = bvalue;
+
+	} else {
+		// large constant
+		CODE_WORD(value);
+	}
+
+	zargs[zargc++] = value;
+}
+
+void Processor::load_all_operands(zbyte specifier) {
+	for (int i = 6; i >= 0; i -= 2) {
+		zbyte type = (specifier >> i) & 0x03;
+
+		if (type == 3)
+			break;
+
+		load_operand(type);
+	}
+}
+
+void Processor::interpret() {
+	do {
+		zbyte opcode;
+		CODE_BYTE(opcode);
+		zargc = 0;
+
+		if (opcode < 0x80) {
+			// 2OP opcodes
+			load_operand((zbyte)(opcode & 0x40) ? 2 : 1);
+			load_operand((zbyte)(opcode & 0x20) ? 2 : 1);
+
+			(*this.*var_opcodes[opcode & 0x1f])();
+
+		} else if (opcode < 0xb0) {
+			// 1OP opcodes
+			load_operand((zbyte)(opcode >> 4));
+
+			(*this.*op1_opcodes[opcode & 0x0f])();
+
+		} else if (opcode < 0xc0) {
+			// 0OP opcodes
+			(*this.*op0_opcodes[opcode - 0xb0])();
+
+		} else {
+			// VAR opcodes
+			zbyte specifier1;
+			zbyte specifier2;
+
+			if (opcode == 0xec || opcode == 0xfa) {	// opcodes 0xec
+				CODE_BYTE(specifier1);			// and 0xfa are
+				CODE_BYTE(specifier2);          // call opcodes
+				load_all_operands(specifier1);	// with up to 8
+				load_all_operands(specifier2);	// arguments
+			} else {
+				CODE_BYTE(specifier1);
+				load_all_operands(specifier1);
+			}
+
+			(*this.*var_opcodes[opcode - 0xc0])();
+		}
+
+#if defined(DJGPP) && defined(SOUND_SUPPORT)
+		if (end_of_sound_flag)
+			end_of_sound();
+#endif
+	} while (!_finished);
+
+	_finished--;
+}
+
+void Processor::call(zword routine, int argc, zword *args, int ct) {
+	long pc;
+	zword value;
+	zbyte count;
+	int i;
+
+	if (_sp - _stack < 4)
+		runtimeError(ERR_STK_OVF);
+
+	GET_PC(pc);
+
+	*--_sp = (zword)(pc >> 9);
+	*--_sp = (zword)(pc & 0x1ff);
+	*--_sp = (zword)(_fp - _stack - 1);
+	*--_sp = (zword)(argc | (ct << (_save_quetzal ? 12 : 8)));
+
+	_fp = _sp;
+	_frameCount++;
+
+	// Calculate byte address of routine
+	if (h_version <= V3)
+		pc = (long)routine << 1;
+	else if (h_version <= V5)
+		pc = (long)routine << 2;
+	else if (h_version <= V7)
+		pc = ((long)routine << 2) + ((long)h_functions_offset << 3);
+	else if (h_version <= V8)
+		pc = (long)routine << 3;
+	else {
+		// h_version == V9
+		long indirect = (long)routine << 2;
+		HIGH_LONG(indirect, pc);
+	}
+
+	if ((uint)pc >= story_size)
+		runtimeError(ERR_ILL_CALL_ADDR);
+
+	SET_PC(pc);
+
+	// Initialise local variables
+	CODE_BYTE(count);
+
+	if (count > 15)
+		runtimeError(ERR_CALL_NON_RTN);
+	if (_sp - _stack < count)
+		runtimeError(ERR_STK_OVF);
+
+	if (_save_quetzal)
+		_fp[0] |= (zword)count << 8;	// Save local var count for Quetzal.
+
+	value = 0;
+
+	for (i = 0; i < count; i++) {
+		if (h_version <= V4)		// V1 to V4 games provide default
+			CODE_WORD(value);		// values for all local variables
+
+			*--_sp = (zword)((argc-- > 0) ? args[i] : value);
+	}
+
+	// Start main loop for direct calls
+	if (ct == 2)
+		interpret();
+}
+
+void Processor::ret(zword value) {
+	long pc;
+	int ct;
+
+	if (_sp > _fp)
+		runtimeError(ERR_STK_UNDF);
+
+	_sp = _fp;
+
+	ct = *_sp++ >> (_save_quetzal ? 12 : 8);
+	_frameCount--;
+	_fp = _stack + 1 + *_sp++;
+	pc = *_sp++;
+	pc = ((long)*_sp++ << 9) | pc;
+
+	SET_PC(pc);
+
+	// Handle resulting value
+	if (ct == 0)
+		store(value);
+	if (ct == 2)
+		*--_sp = value;
+
+	// Stop main loop for direct calls
+	if (ct == 2)
+		_finished++;
+}
+
+void Processor::branch(bool flag) {
+	long pc;
+	zword offset;
+	zbyte specifier;
+	zbyte off1;
+	zbyte off2;
+
+	CODE_BYTE(specifier);
+	off1 = specifier & 0x3f;
+
+	if (!flag)
+		specifier ^= 0x80;
+
+	if (!(specifier & 0x40)) {
+		// it's a long branch
+		if (off1 & 0x20)		// propagate sign bit
+			off1 |= 0xc0;
+
+		CODE_BYTE(off2);
+		offset = (off1 << 8) | off2;
+	} else {
+		// It's a short branch
+		offset = off1;
+	}
+
+	if (specifier & 0x80) {
+		if (offset > 1) {
+			// normal branch
+			GET_PC(pc);
+			pc += (short)offset - 2;
+			SET_PC(pc);
+		} else {
+			// special case, return 0 or 1
+			ret(offset);
+		}
+	}
+}
+
+void Processor::store(zword value) {
+	zbyte variable;
+
+	CODE_BYTE(variable);
+
+	if (variable == 0)
+		*--_sp = value;
+	else if (variable < 16)
+		*(_fp - variable) = value;
+	else {
+		zword addr = h_globals + 2 * (variable - 16);
+		SET_WORD(addr, value);
+	}
+}
+
+int Processor::direct_call(zword addr) {
+	zword saved_zargs[8];
+	int saved_zargc;
+	int i;
+
+	// Calls to address 0 return false
+	if (addr == 0)
+		return 0;
+
+	// Save operands and operand count
+	for (i = 0; i < 8; i++)
+		saved_zargs[i] = zargs[i];
+
+	saved_zargc = zargc;
+
+	// Call routine directly
+	call(addr, 0, 0, 2);
+
+	// Restore operands and operand count
+	for (i = 0; i < 8; i++)
+		zargs[i] = saved_zargs[i];
+
+	zargc = saved_zargc;
+
+	// Resulting value lies on top of the stack
+	return (short)*_sp++;
+}
+
+void Processor::seed_random(int value) {
+	if (value == 0) {
+		// Now using random values
+		_randomInterval = 0;
+	} else if (value < 1000) {
+		// special seed value
+		_randomCtr = 0;
+		_randomInterval = value;
+	} else {
+		// standard seed value
+		_random.setSeed(value);
+		_randomInterval = 0;
+	}
+}
+
+void Processor::__extended__() {
+	zbyte opcode;
+	zbyte specifier;
+
+	CODE_BYTE(opcode);
+	CODE_BYTE(specifier);
+
+	load_all_operands(specifier);
+
+	if (opcode < 0x1e)					// extended opcodes from 0x1e on
+		(*this.*ext_opcodes[opcode])();	// are reserved for future spec'
+}
+
+void Processor::__illegal__() {
+	runtimeError(ERR_ILL_OPCODE);
+}
+
+void Processor::z_catch() {
+	store(_save_quetzal ? _frameCount : (zword)(_fp - _stack));
+}
+
+void Processor::z_throw() {
+	if (_save_quetzal) {
+		if (zargs[1] > _frameCount)
+			runtimeError(ERR_BAD_FRAME);
+
+		// Unwind the stack a frame at a time.
+		for (; _frameCount > zargs[1]; --_frameCount)
+			_fp = _stack + 1 + _fp[1];
+	} else {
+		if (zargs[1] > STACK_SIZE)
+			runtimeError(ERR_BAD_FRAME);
+
+		_fp = _stack + zargs[1];
+	}
+
+	ret(zargs[0]);
+}
+
+void Processor::z_call_n() {
+	if (zargs[0] != 0)
+		call(zargs[0], zargc - 1, zargs + 1, 1);
+}
+
+void Processor::z_call_s() {
+	if (zargs[0] != 0)
+		call(zargs[0], zargc - 1, zargs + 1, 0);
+	else
+		store(0);
+}
+
+void Processor::z_check_arg_count() {
+	if (_fp == _stack + STACK_SIZE)
+		branch(zargs[0] == 0);
+	else
+		branch(zargs[0] <= (*_fp & 0xff));
+}
+
+void Processor::z_jump() {
+	long pc;
+	GET_PC(pc);
+
+	pc += (short)zargs[0] - 2;
+
+	if ((uint)pc >= story_size)
+		runtimeError(ERR_ILL_JUMP_ADDR);
+
+	SET_PC(pc);
+}
+
+void Processor::z_nop() {
+	// Do nothing
+}
+
+void Processor::z_quit() {
+	_finished = 9999;
+}
+
+void Processor::z_ret() {
+	ret(zargs[0]);
+}
+
+void Processor::z_ret_popped() {
+	ret(*_sp++);
+}
+
+void Processor::z_rfalse() {
+	ret(0);
+}
+
+void Processor::z_rtrue() {
+	ret(1);
+}
+
+void Processor::z_random() {
+    if ((short) zargs[0] <= 0) {
+		// set random seed
+		seed_random(- (short) zargs[0]);
+		store(0);
+
+    } else {
+		// generate random number
+		zword result;
+		if (_randomInterval != 0) {
+			// ...in special mode
+			result = _randomCtr++;
+			if (_randomCtr == _randomInterval)
+				_randomCtr = 0;
+		} else {
+			// ...in standard mode
+			result = _random.getRandomNumber(0xffff);
+		}
+
+		store((zword)(result % zargs[0] + 1));
+    }
+}
+
+void Processor::z_sound_effect() {
+    zword number = zargs[0];
+    zword effect = zargs[1];
+    zword volume = zargs[2];
+
+    if (zargc < 1)
+		number = 0;
+    if (zargc < 2)
+		effect = EFFECT_PLAY;
+    if (zargc < 3)
+		volume = 8;
+
+    if (number >= 3 || number == 0) {
+		_soundLocked = true;
+
+		if (_storyId == LURKING_HORROR && (number == 9 || number == 16)) {
+			if (effect == EFFECT_PLAY) {
+				next_sample = number;
+				next_volume = volume;
+
+				_soundLocked = false;
+
+				if (!_soundPlaying)
+					start_next_sample();
+			} else {
+				_soundLocked = false;
+			}
+			return;
+		}
+
+		_soundPlaying = false;
+
+		switch (effect) {
+
+		case EFFECT_PREPARE:
+			os_prepare_sample (number);
+			break;
+		case EFFECT_PLAY:
+			start_sample(number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0);
+			break;
+		case EFFECT_STOP:
+			os_stop_sample (number);
+			break;
+		case EFFECT_FINISH_WITH:
+			os_finish_with_sample (number);
+			break;
+		}
+
+		_soundLocked = false;
+	} else {
+		os_beep(number);
+	}
+}
+
+void Processor::z_piracy() {
+	branch(!_piracy);
+}
+
+void Processor::z_save_undo(void) {
+	store((zword)save_undo());
+}
+
+void Processor::z_restore_undo(void) {
+	store((zword)restore_undo());
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
new file mode 100644
index 0000000..074100a
--- /dev/null
+++ b/engines/gargoyle/frotz/processor.h
@@ -0,0 +1,1322 @@
+/* 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 GARGOYLE_FROTZ_PROCESSOR
+#define GARGOYLE_FROTZ_PROCESSOR
+
+#include "gargoyle/frotz/buffer.h"
+#include "gargoyle/frotz/err.h"
+#include "gargoyle/frotz/mem.h"
+#include "gargoyle/frotz/glk_interface.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+#define CODE_BYTE(v)	   v = *pcp++
+#define CODE_WORD(v)       v = READ_BE_UINT16(pcp += 2)
+#define CODE_IDX_WORD(v,i) v = READ_BE_UINT16(pcp + i)
+#define GET_PC(v)          v = pcp - zmp
+#define SET_PC(v)          pcp = zmp + v
+
+
+enum string_type {
+	LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY
+};
+
+class Processor;
+typedef void (Processor::*Opcode)();
+
+/**
+ * Zcode processor
+ */
+class Processor : public virtual Mem, public Errors, public GlkInterface, public UserOptions {
+private:
+	Opcode op0_opcodes[16];
+	Opcode op1_opcodes[16];
+	static Opcode var_opcodes[64];
+	static Opcode ext_opcodes[64];
+	int _finished;
+	zword zargs[8];
+	int zargc;
+	uint _randomInterval;
+	uint _randomCtr;
+	bool first_restart;
+
+	// Stack data
+	zword _stack[STACK_SIZE];
+	zword *_sp;
+	zword *_fp;
+	zword _frameCount;
+
+	// Text related fields
+	static zchar ZSCII_TO_LATIN1[];
+	zchar *_decoded, *_encoded;
+	int _resolution;
+private:
+	/**
+	 * \defgroup General support methods
+	 * @{
+	 */
+
+	/**
+	 * Load an operand, either a variable or a constant.
+	 */
+	void load_operand(zbyte type);
+
+	/**
+	 * Given the operand specifier byte, load all (up to four) operands
+	 * for a VAR or EXT opcode.
+	 */
+	void load_all_operands(zbyte specifier);
+
+	/**
+	 * Call a subroutine. Save PC and FP then load new PC and initialise
+	 * new stack frame. Note that the caller may legally provide less or
+	 * more arguments than the function actually has. The call type "ct"
+	 * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
+	 */
+	void call(zword routine, int argc, zword *args, int ct);
+
+	/**
+	 * Return from the current subroutine and restore the previous _stack
+	 * frame. The result may be stored (0), thrown away (1) or pushed on
+	 * the stack (2). In the latter case a direct call has been finished
+	 * and we must exit the interpreter loop.
+	 */
+	void ret(zword value);
+
+	/**
+	 * Take a jump after an instruction based on the flag, either true or
+	 * false. The branch can be short or long; it is encoded in one or two
+	 * bytes respectively. When bit 7 of the first byte is set, the jump
+	 * takes place if the flag is true; otherwise it is taken if the flag
+	 * is false. When bit 6 of the first byte is set, the branch is short;
+	 * otherwise it is long. The offset occupies the bottom 6 bits of the
+	 * first byte plus all the bits in the second byte for long branches.
+	 * Uniquely, an offset of 0 means return false, and an offset of 1 is
+	 * return true.
+	 */
+	void branch(bool flag);
+
+	/**
+	 * Store an operand, either as a variable or pushed on the stack.
+	 */
+	void store(zword value);
+
+	/*
+	 * Call the interpreter loop directly. This is necessary when
+	 *
+	 * - a sound effect has been finished
+	 * - a read instruction has timed out
+	 * - a newline countdown has hit zero
+	 *
+	 * The interpreter returns the result value on the stack.
+	 */
+	int direct_call(zword addr);
+
+	/**
+	 * Set the seed value for the random number generator.
+	 */
+	void seed_random(int value);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Input support methods
+	 * @{
+	 */
+
+	/**
+	 * Check if the given key is an input terminator.
+	 */
+	bool is_terminator(zchar key);
+
+	/**
+	 * Ask the user a question; return true if the answer is yes.
+	 */
+	bool read_yes_or_no(const char *s);
+
+	/**
+	 * Read a string from the current input stream.
+	 */
+	void read_string(int max, zchar *buffer);
+
+	/**
+	 * Ask the user to type in a number and return it.
+	 */
+	int read_number();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Object support methods
+	 * @{
+	 */
+
+	/**
+	 * Calculate the address of an object.
+	 */
+	zword object_address(zword obj);
+
+	/**
+	 * Return the address of the given object's name.
+	 */
+	zword object_name(zword object);
+
+	/**
+	 * Calculate the start address of the property list associated with an object.
+	 */
+	zword first_property(zword obj);
+
+	/**
+	 * Calculate the address of the next property in a property list.
+	 */
+	zword next_property(zword prop_addr);
+
+	/**
+	 * Unlink an object from its parent and siblings.
+	 */
+	void unlink_object(zword object);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Screen support methods
+	 * @{
+	 */
+
+	void screen_char(zchar c);
+	void screen_new_line();
+	void screen_word(const zchar *s);
+	void screen_mssg_on();
+	void screen_mssg_off();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Stream support methods
+	 * @{
+	 */
+
+	/**
+	 * Write a single character to the scrollback buffer.
+	 *
+	 */
+	void scrollback_char(zchar c);
+
+	/**
+	 * Write a string to the scrollback buffer.
+	 */
+	void scrollback_word(const zchar *s);
+
+	/**
+	 * Send an input line to the scrollback buffer.
+	 */
+	void scrollback_write_input(const zchar *buf, zchar key);
+
+	/**
+	 * Remove an input line from the scrollback buffer.
+	 */
+	void scrollback_erase_input(const zchar *buf);
+
+	/**
+	 * Start printing a "debugging" message.
+	 */
+	void stream_mssg_on();
+
+	/**
+	 * Stop printing a "debugging" message.
+	 */
+	void stream_mssg_off();
+
+	/**
+	 * Send a single character to the output stream.
+	 */
+	void stream_char(zchar c);
+
+	/**
+	 * Send a string of characters to the output streams.
+	 */
+	void stream_word(const zchar *s);
+
+	/**
+	 * Send a newline to the output streams.
+	 */
+	void stream_new_line();
+
+	/**
+	 * Read a single keystroke from the current input stream.
+	 */
+	zchar stream_read_key(zword timeout, zword routine, bool hot_keys);
+
+	/**
+	 * Read a line of input from the current input stream.
+	 */
+	zchar stream_read_input(int max, zchar *buf, zword timeout, zword routine,
+		bool hot_keys, bool no_scripting);
+
+	 /**@}*/
+
+	/**
+	 * \defgroup Text support methods
+	 * @{
+	 */
+
+	/**
+	 * Map a ZSCII character into Unicode.
+	 */
+	zchar translate_from_zscii(zbyte c);
+
+	/**
+	 * Convert a Unicode character to ZSCII, returning 0 on failure.
+	 */
+	zbyte unicode_to_zscii(zchar c);
+
+	/**
+	 * Map a Unicode character onto the ZSCII alphabet.
+	 *
+	 */
+	zbyte translate_to_zscii(zchar c);
+
+	/**
+	 * Return a character from one of the three character sets.
+	 */
+	zchar alphabet(int set, int index);
+
+	/**
+	 * Find the number of bytes used for dictionary resolution.
+	 */
+	void find_resolution();
+
+	/**
+	 * Copy a ZSCII string from the memory to the global "decoded" string.
+	 */
+	void load_string(zword addr, zword length);
+
+	/**
+	 * Encode the Unicode text in the global "decoded" string then write
+	 * the result to the global "encoded" array. (This is used to look up
+	 * words in the dictionary.) Up to V3 the vocabulary resolution is
+	 * two, from V4 it is three, and from V9 it is any number of words.
+	 * Because each word contains three Z-characters, that makes six or
+	 * nine Z-characters respectively. Longer words are chopped to the
+	 * proper size, shorter words are are padded out with 5's. For word
+	 * completion we pad with 0s and 31s, the minimum and maximum
+	 * Z-characters.
+	 */
+	void encode_text(int padding);
+
+	/**
+	 * Convert _encoded text to Unicode. The _encoded text consists of 16bit
+	 * words. Every word holds 3 Z-characters (5 bits each) plus a spare
+	 * bit to mark the last word. The Z-characters translate to ZSCII by
+	 * looking at the current current character set. Some select another
+	 * character set, others refer to abbreviations.
+	 *
+	 * There are several different string types:
+	 *
+	 *    LOW_STRING - from the lower 64KB (byte address)
+	 *    ABBREVIATION - from the abbreviations table (word address)
+	 *    HIGH_STRING - from the end of the memory map (packed address)
+	 *    EMBEDDED_STRING - from the instruction stream (at PC)
+	 *    VOCABULARY - from the dictionary (byte address)
+	 *
+	 * The last type is only used for word completion.
+	 */
+	void decode_text(string_type st, zword addr);
+
+	/**
+	 * Print a signed 16bit number.
+	 */
+	void print_num(zword value);
+
+	/**
+	 * print_object
+	 *
+	 * Print an object description.
+	 *
+	 */
+	void print_object(zword object);
+
+	/**
+	 * Scan a dictionary searching for the given word. The first argument
+	 * can be
+	 *
+	 * 0x00 - find the first word which is >= the given one
+	 * 0x05 - find the word which exactly matches the given one
+	 * 0x1f - find the last word which is <= the given one
+	 *
+	 * The return value is 0 if the search fails.
+	 */
+	zword lookup_text(int padding, zword dct);
+
+	/**
+	 * tokenise_text
+	 *
+	 * Translate a single word to a token and append it to the token
+	 * buffer. Every token consists of the address of the dictionary
+	 * entry, the length of the word and the offset of the word from
+	 * the start of the text buffer. Unknown words cause empty slots
+	 * if the flag is set (such that the text can be scanned several
+	 * times with different dictionaries); otherwise they are zero.
+	 *
+	 */
+	void tokenise_text(zword text, zword length, zword from, zword parse, zword dct, bool flag);
+
+	/**
+	 * Split an input line into words and translate the words to tokens.
+	 */
+	void tokenise_line(zword text, zword token, zword dct, bool flag);
+
+	/**
+	 * Scan the vocabulary to complete the last word on the input line
+	 * (similar to "tcsh" under Unix). The return value is
+	 *
+	 *    2 ==> completion is impossible
+	 *    1 ==> completion is ambiguous
+	 *    0 ==> completion is successful
+	 *
+	 * The function also returns a string in its second argument. In case
+	 * of 2, the string is empty; in case of 1, the string is the longest
+	 * extension of the last word on the input line that is common to all
+	 * possible completions (for instance, if the last word on the input
+	 * is "fo" and its only possible completions are "follow" and "folly"
+	 * then the string is "ll"); in case of 0, the string is an extension
+	 * to the last word that results in the only possible completion.
+	 */
+	int completion(const zchar *buffer, zchar *result);
+
+	 /**
+	  * Convert a Unicode character to lowercase.
+	  * Taken from Zip2000 by Kevin Bracey.
+	  */
+	zchar unicode_tolower(zchar c);
+
+	/**@}*/
+private:
+	/**
+	 * \defgroup General Opcode methods
+	 * @{
+	 */
+
+	/*
+	 * Load and execute an extended opcode.
+	 */
+	void __extended__();
+
+	/*
+	 * Exit game because an unknown opcode has been hit.
+	 */
+	void __illegal__();
+
+	/*
+	 * Store the current _stack frame for later use with z_throw.
+	 *
+	 *	no zargs used
+	 */
+	void z_catch();
+
+	/**
+	 * Go back to the given _stack frame and return the given value.
+	 *
+	 *	zargs[0] = value to return
+	 *	zargs[1] = _stack frame
+	 */
+	void z_throw();
+
+	/*
+	 * Call a subroutine and discard its result.
+	 *
+	 * 	zargs[0] = packed address of subroutine
+	 *	zargs[1] = first argument (optional)
+	 *	...
+	 *	zargs[7] = seventh argument (optional)
+	 */
+	void z_call_n();
+
+	/**
+	 * Call a subroutine and store its result.
+	 *
+	 * 	zargs[0] = packed address of subroutine
+	 *	zargs[1] = first argument (optional)
+	 *	...
+	 *	zargs[7] = seventh argument (optional)
+	 */
+	void z_call_s();
+
+	/**
+	 * Branch if subroutine was called with >= n arg's.
+	 *
+	 * 	zargs[0] = number of arguments
+	 */
+	void z_check_arg_count();
+
+	/**
+	 * Jump unconditionally to the given address.
+	 *
+	 *	zargs[0] = PC relative address
+	 */
+	void z_jump();
+
+	/*
+	 * No operation.
+	 *
+	 *	no zargs used
+	 */
+	void z_nop();
+
+	/*
+	 * Stop game and exit interpreter.
+	 *
+	 *	no zargs used
+	 */
+	void z_quit();
+
+	/*
+	 * Return from a subroutine with the given value.
+	 *
+	 *	zargs[0] = value to return
+	 */
+	void z_ret();
+
+	/*
+	 * Return from a subroutine with a value popped off the stack.
+	 *
+	 *	no zargs used
+	 */
+	void z_ret_popped();
+
+	/*
+	 * Return from a subroutine with false (0).
+	 *
+	 * 	no zargs used
+	 */
+	void z_rfalse();
+
+	/*
+	 * Return from a subroutine with true (1).
+	 *
+	 * 	no zargs used
+	 */
+	void z_rtrue();
+
+	/**
+	 * Store a random number or set the random number seed.
+	 *
+	 *	zargs[0] = range (positive) or seed value (negative)
+	 */
+	void z_random();
+
+	/**
+	 * Load / play / stop / discard a sound effect.
+	 *
+	 *	zargs[0] = number of bleep (1 or 2) or sample
+	 *	zargs[1] = operation to perform (samples only)
+	 *	zargs[2] = repeats and volume (play sample only)
+	 *	zargs[3] = end-of-sound routine (play sample only, optional)
+	 *
+	 * Note: Volumes range from 1 to 8, volume 255 is the default volume.
+	 *	 Repeats are stored in the high byte, 255 is infinite loop.
+	 *
+	 */
+	void z_sound_effect();
+
+	/**
+	 * Branch if the story file is a legal copy
+	 */
+	void z_piracy();
+
+	/**
+	 * Save the current Z-machine state for a future undo.
+	 *
+	 *	no zargs used
+	 */
+	void z_save_undo();
+
+	/**
+	 * Restore a Z-machine state from memory.
+	 *
+	 *	no zargs used
+	 */
+	void z_restore_undo();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Input Opcode methods
+	 * @{
+	 */
+
+	 /**
+	  * Add or remove a menu and branch if successful.
+	  *
+	  * 	zargs[0] = number of menu
+	  *	zargs[1] = table of menu entries or 0 to remove menu
+	  */
+	void z_make_menu();
+
+	/**
+	 * Read a line of input and (in V5+) store the terminating key.
+	 *
+	 *	zargs[0] = address of text buffer
+	 *	zargs[1] = address of token buffer
+	 *	zargs[2] = timeout in tenths of a second (optional)
+	 *	zargs[3] = packed address of routine to be called on timeout
+	 */
+	void z_read();
+
+	/**
+	 * Read and store a key.
+	 *
+	 *	zargs[0] = input device (must be 1)
+	 *	zargs[1] = timeout in tenths of a second (optional)
+	 *	zargs[2] = packed address of routine to be called on timeout
+	 */
+	void z_read_char();
+
+	/**
+	 * z_read_mouse, write the current mouse status into a table.
+	 *
+	 *	zargs[0] = address of table
+	 */
+	void z_read_mouse();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Math Opcode methods
+	 * @{
+	 */
+
+	/**
+	 * 16 bit addition.
+	 *
+	 *	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_add();
+
+	/**
+	 * Bitwise AND operation.
+	 *
+	 *	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_and();
+
+	/**
+	 * Arithmetic SHIFT operation.
+	 *
+	 *	zargs[0] = value
+	 *	zargs[1] = #positions to shift left (positive) or right
+	 */
+	void z_art_shift();
+
+	/**
+	 * Signed 16bit division.
+	 *
+	 *	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_div();
+
+	/**
+	 * B ranch if the first value equals any of the following.
+	 *
+	 *	zargs[0] = first value
+	 *	zargs[1] = second value (optional)
+	 *	...
+	 *	zargs[3] = fourth value (optional)
+	 */
+	void z_je();
+
+	/**
+	 * Branch if the first value is greater than the second.
+	 *
+	 *	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_jg();
+
+	/**
+	 * Branch if the first value is less than the second.
+	 *
+	 *	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_jl();
+
+	/**
+	 * Branch if value is zero.
+	 *
+	 * 	zargs[0] = value
+	 */
+	void z_jz();
+
+	/**
+	 * Logical SHIFT operation.
+	 *
+	 * 	zargs[0] = value
+	 *	zargs[1] = #positions to shift left (positive) or right (negative)
+	 */
+	void z_log_shift();
+
+	/*
+	 * Remainder after signed 16bit division.
+	 *
+	 * 	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_mod();
+
+	/**
+	 * 16 bit multiplication.
+	 *
+	 * 	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_mul();
+
+	/**
+	 * Bitwise NOT operation.
+	 *
+	 * 	zargs[0] = value
+	 */
+	void z_not();
+
+	/**
+	 * Bitwise OR operation.
+	 *
+	 *	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_or();
+
+	/**
+	 * 16 bit substraction.
+	 *
+	 *	zargs[0] = first value
+	 *	zargs[1] = second value
+	 */
+	void z_sub();
+
+	/**
+	 * Branch if all the flags of a bit mask are set in a value.
+	 *
+	 *	zargs[0] = value to be examined
+	 *	zargs[1] = bit mask
+	 */
+	void z_test();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Object Opcode methods
+	 * @{
+	 */
+	
+	/**
+	 * Branch if the first object is inside the second.
+	 *
+	 *        zargs[0] = first object
+	 *        zargs[1] = second object
+	 */
+	void z_jin();
+
+	/**
+	 * Store the child of an object.
+	 *
+	 *        zargs[0] = object
+	 */
+	void z_get_child();
+
+	/**
+	 * Store the number of the first or next property.
+	 *
+	 *        zargs[0] = object
+	 *        zargs[1] = address of current property (0 gets the first property)
+	 */
+	void z_get_next_prop();
+
+	/**
+	 * Store the parent of an object.
+	 *
+	 *        zargs[0] = object
+	 */
+	void z_get_parent();
+
+	/**
+	 * Store the value of an object property.
+	 *
+	 *        zargs[0] = object
+	 *        zargs[1] = number of property to be examined
+	 */
+	void z_get_prop();
+
+	/**
+	 * Store the address of an object property.
+	 *
+	 *        zargs[0] = object
+	 *        zargs[1] = number of property to be examined
+	 */
+	void z_get_prop_addr();
+
+	/**
+	 * Store the length of an object property.
+	 *
+	 *         zargs[0] = address of property to be examined
+	 */
+	void z_get_prop_len();
+
+	/**
+	 * Store the sibling of an object.
+	 *
+	 *        zargs[0] = object
+	 */
+	void z_get_sibling();
+	
+	/**
+	 * Make an object the first child of another object.
+	 *
+	 *        zargs[0] = object to be moved
+	 *        zargs[1] = destination object
+	 */
+	void z_insert_obj();
+
+	/**
+	 * Set the value of an object property.
+	 *
+	 *        zargs[0] = object
+	 *        zargs[1] = number of property to set
+	 *        zargs[2] = value to set property to
+	 */
+	void z_put_prop();
+
+	/**
+	 * Unlink an object from its parent and siblings.
+	 *
+	 *        zargs[0] = object
+	 */
+	void z_remove_obj();
+
+	/**
+	 * Set an object attribute.
+	 *
+	 *        zargs[0] = object
+	 *        zargs[1] = number of attribute to set
+	 */
+	void z_set_attr();
+
+	/**
+	 * Branch if an object attribute is set.
+	 *
+	 *        zargs[0] = object
+	 *        zargs[1] = number of attribute to test
+	 */
+	void z_test_attr();
+
+	/**
+	 * Clear an object attribute.
+	 *
+	 *        zargs[0] = object
+	 *        zargs[1] = number of attribute to be cleared
+	 */
+	void z_clear_attr();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Screen Opcode methods
+	 * @{
+	 */
+
+	/**
+	 * Turn text buffering on/off.
+	 *
+	 *		zargs[0] = new text buffering flag (0 or 1)
+	 */
+	void z_buffer_mode();
+
+	/**
+	 * Set the screen buffering mode.
+	 *
+	 *	zargs[0] = mode
+	 */
+	void z_buffer_screen();
+
+	/**
+	 * Erase the line starting at the cursor position.
+	 *
+	 *		zargs[0] = 1 + #units to erase (1 clears to the end of the line)
+	 */
+	void z_erase_line();
+
+	/**
+	 * Erase a window or the screen to background colour.
+	 *
+	 *		zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
+	 */
+	void z_erase_window();
+
+	/**
+	 * Write the cursor coordinates into a table.
+	 *
+	 *		zargs[0] = address to write information to
+	 */
+	void z_get_cursor();
+
+	/**
+	 * Print ASCII text in a rectangular area.
+	 *
+	 *		zargs[0] = address of text to be printed
+	 *		zargs[1] = width of rectangular area
+	 *		zargs[2] = height of rectangular area (optional)
+	 *		zargs[3] = number of char's to skip between lines (optional)
+	 */
+	void z_print_table();
+
+	/**
+	 * Set the foreground and background colours
+	 * to specific RGB colour values.
+	 *
+	 *	zargs[0] = foreground colour
+	 *	zargs[1] = background colour
+	 *	zargs[2] = window (-3 is the current one, optional)
+	 */
+	void z_set_true_colour();
+
+	/**
+	 * Set the foreground and background colours.
+	 *
+	 *		zargs[0] = foreground colour
+	 *		zargs[1] = background colour
+	 *		zargs[2] = window (-3 is the current one, optional)
+	 */
+	void z_set_colour();
+
+	/**
+	 * Set the font for text output and store the previous font.
+	 *
+	 *		 zargs[0] = number of font or 0 to keep current font
+	 */
+	void z_set_font();
+
+	/**
+	 * Set the cursor position or turn the cursor on/off.
+	 *
+	 *		zargs[0] = y-coordinate or -2/-1 for cursor on/off
+	 *		zargs[1] = x-coordinate
+	 *		zargs[2] = window (-3 is the current one, optional)
+	 */
+	void z_set_cursor();
+
+	/**
+	 * z_set_text_style, set the style for text output.
+	 *
+	 *		 zargs[0] = style flags to set or 0 to reset text style
+	 */
+	void z_set_text_style();
+
+	/**
+	 * Select the current window.
+	 *
+	 *		zargs[0] = window to be selected (-3 is the current one)
+	 */
+	void z_set_window();
+
+	/**
+	 * Display the status line for V1 to V3 games.
+	 *
+	 *		no zargs used
+	 */
+	void pad_status_line(int column);
+
+	/**
+	 * Display the status line for V1 to V3 games.
+	 *
+	 *		no zargs used
+	 */
+	void z_show_status();
+
+	/**
+	 * Split the screen into an upper (1) and lower (0) window.
+	 *
+	 *		zargs[0] = height of upper window in screen units (V6) or #lines
+	 */
+	void z_split_window();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Stream Opcode methods
+	 * @{
+	 */
+
+	/**
+	 * Select an input stream.
+	 *
+	 *	zargs[0] = input stream to be selected
+	 */
+	void z_input_stream();
+
+	/**
+	 * Open or close an output stream.
+	 *
+	 *	zargs[0] = stream to open (positive) or close (negative)
+	 *	zargs[1] = address to redirect output to (stream 3 only)
+	 *	zargs[2] = width of redirected output (stream 3 only, optional)
+	 */
+	void z_output_stream();
+
+	/**
+	 * Re-load dynamic area, clear the stack and set the PC.
+	 *
+	 * 	no zargs used
+	 */
+	void z_restart();
+
+	/**
+	 * Save [a part of] the Z-machine state to disk.
+	 *
+	 *	zargs[0] = address of memory area to save (optional)
+	 *	zargs[1] = number of bytes to save
+	 *	zargs[2] = address of suggested file name
+	 */
+	void z_save();
+
+	/**
+	 * Restore [a part of] a Z-machine state from disk
+	 *
+	 *	zargs[0] = address of area to restore (optional)
+	 *	zargs[1] = number of bytes to restore
+	 *	zargs[2] = address of suggested file name
+	 */
+	void z_restore();
+
+	/**
+	 * Check the story file integrity.
+	 *
+	 *	no zargs used
+	 */
+	void z_verify();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Table Opcode methods
+	 * @{
+	 */
+
+	/**
+	 * Copy a table or fill it with zeroes.
+	 *
+	 *	zargs[0] = address of table
+	 * 	zargs[1] = destination address or 0 for fill
+	 *	zargs[2] = size of table
+	 *
+	 * Note: Copying is safe even when source and destination overlap; but
+	 *       if zargs[1] is negative the table _must_ be copied forwards.
+	 */
+	void z_copy_table();
+
+	/**
+	 * Store a value from a table of bytes.
+	 *
+	 *	zargs[0] = address of table
+	 *	zargs[1] = index of table entry to store
+	 */
+	void z_loadb();
+
+	/**
+	 * Store a value from a table of words.
+	 *
+	 *	zargs[0] = address of table
+	 *	zargs[1] = index of table entry to store
+	 */
+	void z_loadw();
+
+	/**
+	 * Find and store the address of a target within a table.
+	 *
+	 *	zargs[0] = target value to be searched for
+	 *	zargs[1] = address of table
+	 *	zargs[2] = number of table entries to check value against
+	 *	zargs[3] = type of table (optional, defaults to 0x82)
+	 *
+	 * Note: The table is a word array if bit 7 of zargs[3] is set; otherwise
+	 *       it's a byte array. The lower bits hold the address step.
+	 */
+	void z_scan_table();
+
+	/**
+	 * Write a byte into a table of bytes.
+	 *
+	 *	zargs[0] = address of table
+	 *	zargs[1] = index of table entry
+	 *	zargs[2] = value to be written
+	 */
+	void z_storeb();
+
+	/**
+	 * Write a word into a table of words.
+	 *
+	 *	zargs[0] = address of table
+	 *	zargs[1] = index of table entry
+	 *	zargs[2] = value to be written
+	 */
+	void z_storew();
+
+	/**@}*/
+
+	/**
+	 * \defgroup Text Opcode methods
+	 * @{
+	 */
+
+	/**
+	 * Test if a unicode character can be printed (bit 0) and read (bit 1).
+	 *
+	 * 	zargs[0] = Unicode
+	 */
+	void z_check_unicode();
+
+	/**
+	 * Encode a ZSCII string for use in a dictionary.
+	 *
+	 *	zargs[0] = address of text buffer
+	 *	zargs[1] = length of ASCII string
+	 *	zargs[2] = offset of ASCII string within the text buffer
+	 *	zargs[3] = address to store encoded text in
+	 *
+	 * This is a V5+ opcode and therefore the dictionary resolution must be
+	 * three 16bit words.
+	 */
+	void z_encode_text();
+
+	/**
+	 * Print a new line.
+	 *
+	 * 	no zargs used
+	 *
+	 */
+	void z_new_line();
+
+	/**
+	 * Print a string embedded in the instruction stream.
+	 *
+	 *	no zargs used
+	 */
+	void z_print();
+
+	/**
+	 * Print a string from the lower 64KB.
+	 *
+	 *	zargs[0] = address of string to print
+	 */
+	void z_print_addr();
+
+	/**
+	 * Print a single ZSCII character.
+	 *
+	 *	zargs[0] = ZSCII character to be printed
+	 */
+	void z_print_char();
+
+	/**
+	 * Print a formatted table.
+	 *
+	 *	zargs[0] = address of formatted table to be printed
+	 */
+	void z_print_form();
+
+	/**
+	 * Print a signed number.
+	 *
+	 * 	zargs[0] = number to print
+	 */
+	void z_print_num();
+
+	/**
+	 * Print an object description.
+	 *
+	 * 	zargs[0] = number of object to be printed
+	 */
+	void z_print_obj();
+
+	/**
+	 * Print the string at the given packed address.
+	 *
+	 * 	zargs[0] = packed address of string to be printed
+	 */
+	void z_print_paddr();
+
+	/*
+	 * Print the string at PC, print newline then return true.
+	 *
+	 * 	no zargs used
+	 */
+	void z_print_ret();
+
+	/**
+	 * Print a string of ASCII characters.
+	 */
+	void print_string(const char *s);
+
+	/**
+	 * Print unicode character
+	 *
+	 * 	zargs[0] = Unicode
+	 */
+	void z_print_unicode();
+
+	/**
+	 * Make a lexical analysis of a ZSCII string.
+	 *
+	 *	zargs[0] = address of string to analyze
+	 *	zargs[1] = address of token buffer
+	 *	zargs[2] = address of dictionary (optional)
+	 *	zargs[3] = set when unknown words cause empty slots (optional)
+	 */
+	void z_tokenise();
+
+	 /**@}*/
+
+	/**
+	 * \defgroup Variable Opcode methods
+	 * @{
+	 */
+
+	/**
+	 * Decrement a variable.
+	 *
+	 * 	zargs[0] = variable to decrement
+	 */
+	void z_dec();
+
+	/**
+	 * Decrement a variable and branch if now less than value.
+	 *
+	 * 	zargs[0] = variable to decrement
+	 * 	zargs[1] = value to check variable against
+	 */
+	void z_dec_chk();
+
+	/**
+	 * Increment a variable.
+	 *
+	 * 	zargs[0] = variable to increment
+	 */
+	void z_inc();
+
+	/**
+	 * Increment a variable and branch if now greater than value.
+	 *
+	 * 	zargs[0] = variable to increment
+	 * 	zargs[1] = value to check variable against
+	 */
+	void z_inc_chk();
+
+	/**
+	 * Store the value of a variable.
+	 *
+	 *	zargs[0] = variable to store
+	 */
+	void z_load();
+
+	/**
+	 * Pop a value off the game stack and discard it.
+	 *
+	 *	no zargs used
+	 */
+	void z_pop();
+
+	/**
+	 * Pop n values off the game or user stack and discard them.
+	 *
+	 *	zargs[0] = number of values to discard
+	 *	zargs[1] = address of user stack (optional)
+	 */
+	void z_pop_stack();
+
+	/**
+	 * Pop a value off...
+	 *
+	 * a) ...the game or a user stack and store it (V6)
+	 *
+	 *	zargs[0] = address of user stack (optional)
+	 *
+	 * b) ...the game stack and write it to a variable (other than V6)
+	 *
+	 *	zargs[0] = variable to write value to
+	 */
+	void z_pull();
+
+	/**
+	 * Push a value onto the game stack.
+	 *
+	 *	zargs[0] = value to push onto the stack
+	 */
+	void z_push();
+
+	/**
+	 * Push a value onto a user stack then branch if successful.
+	 *
+	 *	zargs[0] = value to push onto the stack
+	 *	zargs[1] = address of user stack
+	 */
+	void z_push_stack();
+
+	/**
+	 * Write a value to a variable.
+	 *
+	 * 	zargs[0] = variable to be written to
+	 *      zargs[1] = value to write
+	 */
+	void z_store();
+
+	/**@}*/
+protected:
+	/**
+	 * Get the PC. Is implemented by the Processor class, which derives from Errors
+	 */
+	virtual zword getPC() const { return pcp - zmp; }
+public:
+	/**
+	 * Constructor
+	 */
+	Processor(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+	/**
+	 * Initialization
+	 */
+	virtual void initialize() override;
+
+	/**
+	 * Z-code interpreter main loop
+	 */
+	void interpret();
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
new file mode 100644
index 0000000..e7f92ab
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_input.cpp
@@ -0,0 +1,207 @@
+/* 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 "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Implement method stubs
+static zchar stream_read_key(zword, zword, bool) { return 0; }
+static zchar stream_read_input(int, zchar *, zword, zword, bool, bool) { return 0;}
+static void storeb(zword, zchar) {}
+static void storew(zword, zword) {}
+static void save_undo() {}
+static zword os_read_mouse() { return 0; }
+
+
+#define INPUT_BUFFER_SIZE 200
+
+void Processor::z_make_menu() {
+    // This opcode was only used for the Macintosh version of Journey.
+	// It controls menus with numbers greater than 2 (menus 0, 1 and 2
+    // are system menus).
+    branch (false);
+}
+
+bool Processor::read_yes_or_no(const char *s) {
+    zchar key;
+
+    print_string(s);
+    print_string("? (y/n) >");
+
+    key = stream_read_key(0, 0, false);
+
+    if (key == 'y' || key == 'Y') {
+		print_string("y\n");
+		return true;
+    } else {
+		print_string("n\n");
+		return false;
+    }
+}
+
+void Processor::read_string(int max, zchar *buffer) {
+    zchar key;
+
+    buffer[0] = 0;
+
+    do {
+		key = stream_read_input(max, buffer, 0, 0, false, false);
+    } while (key != ZC_RETURN);
+}
+
+int Processor::read_number() {
+    zchar buffer[6];
+    int value = 0;
+    int i;
+
+    read_string(5, buffer);
+
+    for (i = 0; buffer[i] != 0; i++)
+	if (buffer[i] >= '0' && buffer[i] <= '9')
+	    value = 10 * value + buffer[i] - '0';
+
+    return value;
+
+}
+
+void Processor::z_read() {
+    zchar buffer[INPUT_BUFFER_SIZE];
+    zword addr;
+    zchar key;
+    zbyte max, size;
+    zbyte c;
+    int i;
+
+    // Supply default arguments
+    if (zargc < 3)
+		zargs[2] = 0;
+
+    // Get maximum input size
+    addr = zargs[0];
+
+	LOW_BYTE(addr, max);
+
+    if (h_version <= V4)
+		max--;
+
+    if (max >= INPUT_BUFFER_SIZE)
+		max = INPUT_BUFFER_SIZE - 1;
+
+    // Get initial input size
+    if (h_version >= V5) {
+		addr++;
+		LOW_BYTE(addr, size);
+	} else {
+		size = 0;
+	}
+
+    // Copy initial input to local buffer
+    for (i = 0; i < size; i++) {
+		addr++;
+		LOW_BYTE(addr, c);
+		buffer[i] = translate_from_zscii(c);
+    }
+    buffer[i] = 0;
+
+    // Draw status line for V1 to V3 games
+    if (h_version <= V3)
+		z_show_status();
+
+    // Read input from current input stream
+    key = stream_read_input(
+		max, buffer,		// buffer and size
+		zargs[2],			// timeout value
+		zargs[3],			// timeout routine
+		false,				// enable hot keys
+		h_version == V6		// no script in V6
+	);
+
+	if (key == ZC_BAD)
+		return;
+
+    // Perform save_undo for V1 to V4 games
+    if (h_version <= V4)
+		save_undo();
+
+    // Copy local buffer back to dynamic memory
+    for (i = 0; buffer[i] != 0; i++) {
+		if (key == ZC_RETURN) {
+			buffer[i] = unicode_tolower (buffer[i]);
+		}
+
+		storeb((zword) (zargs[0] + ((h_version <= V4) ? 1 : 2) + i), translate_to_zscii (buffer[i]));
+    }
+
+    // Add null character (V1-V4) or write input length into 2nd byte
+    if (h_version <= V4)
+		storeb((zword) (zargs[0] + 1 + i), 0);
+    else
+		storeb((zword) (zargs[0] + 1), i);
+
+    // Tokenise line if a token buffer is present
+    if (key == ZC_RETURN && zargs[1] != 0)
+		tokenise_line (zargs[0], zargs[1], 0, false);
+
+    // Store key
+    if (h_version >= V5)
+		store(translate_to_zscii(key));
+}
+
+void Processor::z_read_char() {
+    zchar key;
+
+    // Supply default arguments
+    if (zargc < 2)
+		zargs[1] = 0;
+
+    // Read input from the current input stream
+    key = stream_read_key(
+		zargs[1],	// timeout value
+		zargs[2],	// timeout routine
+		false  		// enable hot keys
+	);
+    
+	if (key == ZC_BAD)
+		return;
+
+    // Store key
+    store (translate_to_zscii (key));
+}
+
+void Processor::z_read_mouse(){
+    zword btn;
+
+    // Read the mouse position, the last menu click and which buttons are down
+    btn = os_read_mouse();
+    hx_mouse_y = mouse_y;
+    hx_mouse_x = mouse_x;
+
+    storew((zword) (zargs[0] + 0), hx_mouse_y);
+    storew((zword) (zargs[0] + 2), hx_mouse_x);
+    storew((zword) (zargs[0] + 4), btn);			// mouse button bits
+    storew((zword) (zargs[0] + 6), menu_selected);	// menu selection
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_maths.cpp b/engines/gargoyle/frotz/processor_maths.cpp
new file mode 100644
index 0000000..e64dd55
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_maths.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+void Processor::z_add() {
+    store((zword)((short)zargs[0] + (short)zargs[1]));
+}
+
+void Processor::z_and() {
+    store((zword)(zargs[0] & zargs[1]));
+}
+
+void Processor::z_art_shift() {
+    if ((short)zargs[1] > 0)
+		store((zword)((short)zargs[0] << (short)zargs[1]));
+    else
+		store((zword)((short)zargs[0] >> - (short)zargs[1]));
+}
+
+void Processor::z_div() {
+    if (zargs[1] == 0)
+		runtimeError(ERR_DIV_ZERO);
+
+    store((zword)((short)zargs[0] / (short)zargs[1]));
+}
+
+void Processor::z_je() {
+    branch(
+		zargc > 1 && (zargs[0] == zargs[1] || (
+		zargc > 2 && (zargs[0] == zargs[2] || (
+		zargc > 3 && (zargs[0] == zargs[3])))))
+	);
+}
+
+void Processor::z_jg() {
+    branch((short)zargs[0] > (short)zargs[1]);
+}
+
+void Processor::z_jl() {
+    branch((short)zargs[0] < (short)zargs[1]);
+}
+
+void Processor::z_jz() {
+    branch((short)zargs[0] == 0);
+}
+
+void Processor::z_log_shift() {
+    if ((short)zargs[1] > 0)
+		store((zword)(zargs[0] << (short)zargs[1]));
+    else
+		store((zword)(zargs[0] >> - (short)zargs[1]));
+}
+
+void Processor::z_mod() {
+    if (zargs[1] == 0)
+		runtimeError(ERR_DIV_ZERO);
+
+    store((zword)((short)zargs[0] % (short)zargs[1]));
+}
+
+void Processor::z_mul() {
+    store((zword)((short)zargs[0] * (short)zargs[1]));
+}
+
+void Processor::z_not() {
+    store((zword)~zargs[0]);
+}
+
+void Processor::z_or() {
+    store((zword)(zargs[0] | zargs[1]));
+}
+
+void Processor::z_sub() {
+    store((zword)((short)zargs[0] - (short)zargs[1]));
+}
+
+void Processor::z_test() {
+	branch((zargs[0] & zargs[1]) == zargs[1]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_objects.cpp b/engines/gargoyle/frotz/processor_objects.cpp
new file mode 100644
index 0000000..3d2b171
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_objects.cpp
@@ -0,0 +1,735 @@
+/* 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 "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Method stubs to implement
+static void new_line() {}
+
+#define MAX_OBJECT 2000
+
+enum O1 {
+	O1_PARENT          = 4,
+	O1_SIBLING         = 5,
+	O1_CHILD           = 6,
+	O1_PROPERTY_OFFSET = 7,
+	O1_SIZE            = 9
+};
+
+enum O4 {
+	O4_PARENT          = 6,
+	O4_SIBLING         = 8,
+	O4_CHILD           = 10,
+	O4_PROPERTY_OFFSET = 12,
+	O4_SIZE            = 14
+};
+
+zword Processor::object_address(zword obj) {
+    // Check object number
+    if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
+        print_string("@Attempt to address illegal object ");
+        print_num(obj);
+        print_string(".  This is normally fatal.");
+        new_line();
+        runtimeError(ERR_ILL_OBJ);
+    }
+
+    // Return object address
+    if (h_version <= V3)
+        return h_objects + ((obj - 1) * O1_SIZE + 62);
+    else
+        return h_objects + ((obj - 1) * O4_SIZE + 126);
+}
+
+zword Processor::object_name(zword object) {
+    zword obj_addr;
+    zword name_addr;
+
+    obj_addr = object_address(object);
+
+    // The object name address is found at the start of the properties
+    if (h_version <= V3)
+        obj_addr += O1_PROPERTY_OFFSET;
+    else
+        obj_addr += O4_PROPERTY_OFFSET;
+
+	LOW_WORD(obj_addr, name_addr);
+
+    return name_addr;
+}
+
+zword Processor::first_property(zword obj) {
+    zword prop_addr;
+    zbyte size;
+
+    // Fetch address of object name
+    prop_addr = object_name (obj);
+
+    // Get length of object name
+	LOW_BYTE(prop_addr, size);
+
+    // Add name length to pointer
+    return prop_addr + 1 + 2 * size;
+}
+
+zword Processor::next_property(zword prop_addr) {
+    zbyte value;
+
+    // Load the current property id
+	LOW_BYTE(prop_addr, value);
+    prop_addr++;
+
+    // Calculate the length of this property
+    if (h_version <= V3)
+        value >>= 5;
+    else if (!(value & 0x80))
+        value >>= 6;
+    else {
+		LOW_BYTE(prop_addr, value);
+        value &= 0x3f;
+
+        if (value == 0)
+			// demanded by Spec 1.0
+			value = 64;
+    }
+
+    // Add property length to current property pointer
+    return prop_addr + value + 1;
+}
+
+void Processor::unlink_object(zword object) {
+    zword obj_addr;
+    zword parent_addr;
+    zword sibling_addr;
+
+    if (object == 0) {
+        runtimeError(ERR_REMOVE_OBJECT_0);
+        return;
+    }
+
+    obj_addr = object_address(object);
+
+    if (h_version <= V3) {
+
+        zbyte parent;
+        zbyte younger_sibling;
+        zbyte older_sibling;
+        zbyte zero = 0;
+
+        // Get parent of object, and return if no parent
+        obj_addr += O1_PARENT;
+		LOW_BYTE(obj_addr, parent);
+        if (!parent)
+            return;
+
+        // Get (older) sibling of object and set both parent and sibling pointers to 0
+		SET_BYTE(obj_addr, zero);
+        obj_addr += O1_SIBLING - O1_PARENT;
+		LOW_BYTE(obj_addr, older_sibling);
+		SET_BYTE(obj_addr, zero);
+
+        // Get first child of parent (the youngest sibling of the object)
+        parent_addr = object_address(parent) + O1_CHILD;
+		LOW_BYTE(parent_addr, younger_sibling);
+
+        // Remove object from the list of siblings
+		if (younger_sibling == object)
+			SET_BYTE(parent_addr, older_sibling);
+        else {
+            do {
+                sibling_addr = object_address(younger_sibling) + O1_SIBLING;
+				LOW_BYTE(sibling_addr, younger_sibling);
+            } while (younger_sibling != object);
+			SET_BYTE(sibling_addr, older_sibling);
+        }
+    } else {
+        zword parent;
+        zword younger_sibling;
+        zword older_sibling;
+        zword zero = 0;
+
+        // Get parent of object, and return if no parent
+        obj_addr += O4_PARENT;
+		LOW_WORD(obj_addr, parent);
+        if (!parent)
+            return;
+
+        // Get (older) sibling of object and set both parent and sibling pointers to 0
+		SET_WORD(obj_addr, zero);
+        obj_addr += O4_SIBLING - O4_PARENT;
+		LOW_WORD(obj_addr, older_sibling);
+		SET_WORD(obj_addr, zero);
+
+        // Get first child of parent (the youngest sibling of the object)
+        parent_addr = object_address(parent) + O4_CHILD;
+		LOW_WORD(parent_addr, younger_sibling);
+
+        // Remove object from the list of siblings
+		if (younger_sibling == object) {
+			SET_WORD(parent_addr, older_sibling);
+		} else {
+            do {
+                sibling_addr = object_address(younger_sibling) + O4_SIBLING;
+				LOW_WORD(sibling_addr, younger_sibling);
+            } while (younger_sibling != object);
+			SET_WORD(sibling_addr, older_sibling);
+        }
+    }
+}
+
+void Processor::z_clear_attr() {
+    zword obj_addr;
+    zbyte value;
+
+    if (_storyId == SHERLOCK)
+        if (zargs[1] == 48)
+            return;
+
+    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+        runtimeError(ERR_ILL_ATTR);
+
+    // If we are monitoring attribute assignment display a short note
+    if (_attribute_assignment) {
+        stream_mssg_on();
+        print_string("@clear_attr ");
+        print_object(zargs[0]);
+        print_string(" ");
+        print_num(zargs[1]);
+        stream_mssg_off();
+    }
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_CLEAR_ATTR_0);
+        return;
+    }
+
+    // Get attribute address
+    obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+    // Clear attribute bit
+	LOW_BYTE(obj_addr, value);
+    value &= ~(0x80 >> (zargs[1] & 7));
+	SET_BYTE(obj_addr, value);
+}
+
+void Processor::z_jin() {
+    zword obj_addr;
+
+    // If we are monitoring object locating display a short note
+    if (_object_locating) {
+        stream_mssg_on();
+        print_string("@jin ");
+        print_object(zargs[0]);
+        print_string(" ");
+        print_object(zargs[1]);
+        stream_mssg_off();
+    }
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_JIN_0);
+        branch(0 == zargs[1]);
+        return;
+    }
+
+    obj_addr = object_address(zargs[0]);
+
+    if (h_version <= V3) {
+        zbyte parent;
+
+        // Get parent id from object
+        obj_addr += O1_PARENT;
+		LOW_BYTE(obj_addr, parent);
+
+        // Branch if the parent is obj2
+        branch(parent == zargs[1]);
+
+    } else {
+        zword parent;
+
+        // Get parent id from object
+        obj_addr += O4_PARENT;
+		LOW_WORD(obj_addr, parent);
+
+        // Branch if the parent is obj2
+        branch(parent == zargs[1]);
+    }
+}
+
+void Processor::z_get_child() {
+    zword obj_addr;
+
+    // If we are monitoring object locating display a short note
+    if (_object_locating) {
+        stream_mssg_on();
+        print_string("@get_child ");
+        print_object(zargs[0]);
+        stream_mssg_off();
+    }
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_GET_CHILD_0);
+        store(0);
+        branch(false);
+        return;
+    }
+
+    obj_addr = object_address(zargs[0]);
+
+    if (h_version <= V3) {
+        zbyte child;
+
+        // Get child id from object
+        obj_addr += O1_CHILD;
+		LOW_BYTE(obj_addr, child);
+
+        // Store child id and branch
+        store(child);
+        branch(child);
+    } else {
+        zword child;
+
+        // Get child id from object
+        obj_addr += O4_CHILD;
+		LOW_WORD(obj_addr, child);
+
+        // Store child id and branch
+        store(child);
+        branch(child);
+    }
+}
+
+void Processor::z_get_next_prop() {
+    zword prop_addr;
+    zbyte value;
+    zbyte mask;
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_GET_NEXT_PROP_0);
+        store(0);
+        return;
+    }
+
+    // Property id is in bottom five (six) bits
+    mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+    // Load address of first property
+    prop_addr = first_property(zargs[0]);
+
+    if (zargs[1] != 0) {
+        // Scan down the property list
+        do {
+			LOW_BYTE(prop_addr, value);
+            prop_addr = next_property(prop_addr);
+        } while ((value & mask) > zargs[1]);
+
+        // Exit if the property does not exist
+        if ((value & mask) != zargs[1])
+            runtimeError(ERR_NO_PROP);
+    }
+
+    // Return the property id
+	LOW_BYTE(prop_addr, value);
+    store((zword) (value & mask));
+}
+
+void Processor::z_get_parent() {
+    zword obj_addr;
+
+    // If we are monitoring object locating display a short note
+    if (_object_locating) {
+        stream_mssg_on();
+        print_string("@get_parent ");
+        print_object(zargs[0]);
+        stream_mssg_off();
+    }
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_GET_PARENT_0);
+        store(0);
+        return;
+    }
+
+    obj_addr = object_address(zargs[0]);
+
+    if (h_version <= V3) {
+        zbyte parent;
+
+        // Get parent id from object
+        obj_addr += O1_PARENT;
+		LOW_BYTE(obj_addr, parent);
+
+        // Store parent
+        store(parent);
+
+	} else {
+        zword parent;
+
+        // Get parent id from object
+        obj_addr += O4_PARENT;
+		LOW_WORD(obj_addr, parent);
+
+        // Store parent
+        store(parent);
+    }
+}
+
+void Processor::z_get_prop() {
+    zword prop_addr;
+    zword wprop_val;
+    zbyte bprop_val;
+    zbyte value;
+    zbyte mask;
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_GET_PROP_0);
+        store(0);
+        return;
+    }
+
+    // Property id is in bottom five (six) bits
+    mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+    // Load address of first property
+    prop_addr = first_property(zargs[0]);
+
+    // Scan down the property list
+    for (;;) {
+		LOW_BYTE(prop_addr, value);
+        if ((value & mask) <= zargs[1])
+            break;
+        prop_addr = next_property(prop_addr);
+    }
+
+    if ((value & mask) == zargs[1]) {
+		// property found
+
+        // Load property(byte or word sized)
+        prop_addr++;
+
+        if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
+			LOW_BYTE(prop_addr, bprop_val);
+            wprop_val = bprop_val;
+		} else {
+			LOW_WORD(prop_addr, wprop_val);
+		}
+    } else {
+		// property not found
+
+        // Load default value
+        prop_addr = h_objects + 2 * (zargs[1] - 1);
+		LOW_WORD(prop_addr, wprop_val);
+    }
+
+    // Store the property value
+    store(wprop_val);
+}
+
+void Processor::z_get_prop_addr() {
+    zword prop_addr;
+    zbyte value;
+    zbyte mask;
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_GET_PROP_ADDR_0);
+        store(0);
+        return;
+    }
+
+    if (_storyId == BEYOND_ZORK)
+        if (zargs[0] > MAX_OBJECT)
+            { store(0); return; }
+
+    // Property id is in bottom five (six) bits
+    mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+    // Load address of first property
+    prop_addr = first_property(zargs[0]);
+
+    // Scan down the property list
+    for (;;) {
+		LOW_BYTE(prop_addr, value);
+        if ((value & mask) <= zargs[1])
+            break;
+        prop_addr = next_property(prop_addr);
+    }
+
+    // Calculate the property address or return zero
+    if ((value & mask) == zargs[1]) {
+
+        if (h_version >= V4 && (value & 0x80))
+            prop_addr++;
+        store((zword) (prop_addr + 1));
+
+    } else {
+		store(0);
+	}
+}
+
+void Processor::z_get_prop_len() {
+    zword addr;
+    zbyte value;
+
+    // Back up the property pointer to the property id
+    addr = zargs[0] - 1;
+	LOW_BYTE(addr, value);
+
+    // Calculate length of property
+    if (h_version <= V3)
+        value = (value >> 5) + 1;
+    else if (!(value & 0x80))
+        value = (value >> 6) + 1;
+    else {
+        value &= 0x3f;
+
+        if (value == 0)
+			value = 64;        // demanded by Spec 1.0
+    }
+
+    // Store length of property
+    store(value);
+}
+
+void Processor::z_get_sibling() {
+    zword obj_addr;
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_GET_SIBLING_0);
+        store(0);
+        branch(false);
+        return;
+    }
+
+    obj_addr = object_address(zargs[0]);
+
+    if (h_version <= V3) {
+        zbyte sibling;
+
+        // Get sibling id from object
+        obj_addr += O1_SIBLING;
+		LOW_BYTE(obj_addr, sibling);
+
+        // Store sibling and branch
+        store(sibling);
+        branch(sibling);
+
+	} else {
+        zword sibling;
+
+        // Get sibling id from object
+        obj_addr += O4_SIBLING;
+		LOW_WORD(obj_addr, sibling);
+
+        // Store sibling and branch
+        store(sibling);
+        branch(sibling);
+    }
+}
+
+void Processor::z_insert_obj() {
+    zword obj1 = zargs[0];
+    zword obj2 = zargs[1];
+    zword obj1_addr;
+    zword obj2_addr;
+
+    // If we are monitoring object movements display a short note
+    if (_object_movement) {
+        stream_mssg_on();
+        print_string("@move_obj ");
+        print_object(obj1);
+        print_string(" ");
+        print_object(obj2);
+        stream_mssg_off();
+    }
+
+    if (obj1 == 0) {
+        runtimeError(ERR_MOVE_OBJECT_0);
+        return;
+    }
+
+    if (obj2 == 0) {
+        runtimeError(ERR_MOVE_OBJECT_TO_0);
+        return;
+    }
+
+    // Get addresses of both objects
+    obj1_addr = object_address(obj1);
+    obj2_addr = object_address(obj2);
+
+    // Remove object 1 from current parent
+    unlink_object(obj1);
+
+    // Make object 1 first child of object 2
+    if (h_version <= V3) {
+        zbyte child;
+
+        obj1_addr += O1_PARENT;
+		SET_BYTE(obj1_addr, obj2);
+        obj2_addr += O1_CHILD;
+		LOW_BYTE(obj2_addr, child);
+		SET_BYTE(obj2_addr, obj1);
+        obj1_addr += O1_SIBLING - O1_PARENT;
+		SET_BYTE(obj1_addr, child);
+
+    } else {
+        zword child;
+
+        obj1_addr += O4_PARENT;
+		SET_WORD(obj1_addr, obj2);
+        obj2_addr += O4_CHILD;
+		LOW_WORD(obj2_addr, child);
+		SET_WORD(obj2_addr, obj1);
+        obj1_addr += O4_SIBLING - O4_PARENT;
+		SET_WORD(obj1_addr, child);
+    }
+}
+
+void Processor::z_put_prop() {
+    zword prop_addr;
+    zword value;
+    zbyte mask;
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_PUT_PROP_0);
+        return;
+    }
+
+    // Property id is in bottom five or six bits
+    mask = (h_version <= V3) ? 0x1f : 0x3f;
+
+    // Load address of first property
+    prop_addr = first_property(zargs[0]);
+
+    // Scan down the property list
+    for (;;) {
+		LOW_BYTE(prop_addr, value);
+        if ((value & mask) <= zargs[1])
+            break;
+
+        prop_addr = next_property(prop_addr);
+    }
+
+    // Exit if the property does not exist
+    if ((value & mask) != zargs[1])
+        runtimeError(ERR_NO_PROP);
+
+    // Store the new property value (byte or word sized)
+    prop_addr++;
+
+    if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
+        zbyte v = zargs[2];
+		SET_BYTE(prop_addr, v);
+    } else {
+        zword v = zargs[2];
+		SET_WORD(prop_addr, v);
+    }
+}
+
+void Processor::z_remove_obj() {
+    // If we are monitoring object movements display a short note
+    if (_object_movement) {
+        stream_mssg_on();
+        print_string("@remove_obj ");
+        print_object(zargs[0]);
+        stream_mssg_off();
+    }
+
+    // Call unlink_object to do the job
+    unlink_object(zargs[0]);
+}
+
+void Processor::z_set_attr() {
+    zword obj_addr;
+    zbyte value;
+
+    if (_storyId == SHERLOCK)
+        if (zargs[1] == 48)
+            return;
+
+    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+        runtimeError(ERR_ILL_ATTR);
+
+    // If we are monitoring attribute assignment display a short note
+    if (_attribute_assignment) {
+        stream_mssg_on();
+        print_string("@set_attr ");
+        print_object(zargs[0]);
+        print_string(" ");
+        print_num(zargs[1]);
+        stream_mssg_off();
+    }
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_SET_ATTR_0);
+        return;
+    }
+
+    // Get attribute address
+    obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+    // Load attribute byte
+	LOW_BYTE(obj_addr, value);
+
+    // Set attribute bit
+    value |= 0x80 >> (zargs[1] & 7);
+
+    // Store attribute byte
+	SET_BYTE(obj_addr, value);
+}
+
+void Processor::z_test_attr() {
+    zword obj_addr;
+    zbyte value;
+
+    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
+        runtimeError(ERR_ILL_ATTR);
+
+    // If we are monitoring attribute testing display a short note
+    if (_attribute_testing) {
+        stream_mssg_on();
+        print_string("@test_attr ");
+        print_object(zargs[0]);
+        print_string(" ");
+        print_num(zargs[1]);
+        stream_mssg_off();
+    }
+
+    if (zargs[0] == 0) {
+        runtimeError(ERR_TEST_ATTR_0);
+        branch(false);
+        return;
+    }
+
+    // Get attribute address
+    obj_addr = object_address(zargs[0]) + zargs[1] / 8;
+
+    // Load attribute byte
+	LOW_BYTE(obj_addr, value);
+
+    // Test attribute
+    branch(value & (0x80 >> (zargs[1] & 7)));
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_screen.cpp b/engines/gargoyle/frotz/processor_screen.cpp
new file mode 100644
index 0000000..5b3b8cb
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_screen.cpp
@@ -0,0 +1,455 @@
+/* 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 "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Method stubs to implement
+static void storew(zword, zword) {}
+static void print_char(zchar) {}
+
+
+void Processor::screen_char(zchar c) {
+	// TODO
+}
+
+void Processor::screen_new_line() {
+	// TODO
+}
+
+void Processor::screen_word(const zchar *s) {
+	// TODO
+}
+
+void Processor::screen_mssg_on() {
+	// TODO
+}
+
+void Processor::screen_mssg_off() {
+	// TODO
+}
+
+
+void Processor::z_buffer_mode() {
+}
+
+void Processor::z_buffer_screen() {
+	store(0);
+}
+
+void Processor::z_erase_line() {
+	int i;
+
+	if (gos_upper && gos_curwin == gos_upper) {
+		for (i = 0; i < h_screen_cols + 1 - curx; i++)
+			glk_put_char(' ');
+		glk_window_move_cursor(gos_curwin, curx - 1, cury - 1);
+	}
+}
+
+void Processor::z_erase_window() {
+	short w = zargs[0];
+	if (w == -2)
+	{
+		if (gos_upper) {
+			glk_set_window(gos_upper);
+#ifdef GARGLK
+			garglk_set_zcolors(curr_fg, curr_bg);
+#endif /* GARGLK */
+			glk_window_clear(gos_upper);
+			glk_set_window(gos_curwin);
+		}
+		glk_window_clear(gos_lower);
+	}
+	if (w == -1)
+	{
+		if (gos_upper) {
+			glk_set_window(gos_upper);
+#ifdef GARGLK
+			garglk_set_zcolors(curr_fg, curr_bg);
+#endif /* GARGLK */
+			glk_window_clear(gos_upper);
+		}
+		glk_window_clear(gos_lower);
+		split_window(0);
+		glk_set_window(gos_lower);
+		gos_curwin = gos_lower;
+	}
+	if (w == 0)
+		glk_window_clear(gos_lower);
+	if (w == 1 && gos_upper)
+		glk_window_clear(gos_upper);
+}
+
+void Processor::z_get_cursor() {
+	storew((zword) (zargs[0] + 0), cury);
+	storew((zword) (zargs[0] + 2), curx);
+}
+
+void Processor::z_print_table() {
+	zword addr = zargs[0];
+	zword x;
+	int i, j;
+
+	// Supply default arguments
+	if (zargc < 3)
+		zargs[2] = 1;
+	if (zargc < 4)
+		zargs[3] = 0;
+
+	// Write text in width x height rectangle
+	x = curx;
+
+	for (i = 0; i < zargs[2]; i++) {
+		if (i != 0) {
+			cury += 1;
+			curx = x;
+		}
+
+		for (j = 0; j < zargs[1]; j++) {
+
+			zbyte c;
+
+			LOW_BYTE(addr, c);
+			addr++;
+
+			print_char(c);
+		}
+
+		addr += zargs[3];
+	}
+}
+
+#define zB(i) ((((i >> 10) & 0x1F) << 3) | (((i >> 10) & 0x1F) >> 2))
+#define zG(i) ((((i >>  5) & 0x1F) << 3) | (((i >>  5) & 0x1F) >> 2))
+#define zR(i) ((((i      ) & 0x1F) << 3) | (((i      ) & 0x1F) >> 2))
+
+#define zRGB(i) (zR(i) << 16 | zG(i) << 8 | zB(i))
+
+void Processor::z_set_true_colour() {
+	int zfore = zargs[0];
+	int zback = zargs[1];
+
+	if (!(zfore < 0))
+		zfore = zRGB(zargs[0]);
+
+	if (!(zback < 0))
+		zback = zRGB(zargs[1]);
+
+#ifdef GARGLK
+	garglk_set_zcolors(zfore, zback);
+#endif /* GARGLK */
+
+	curr_fg = zfore;
+	curr_bg = zback;
+}
+
+static const int zcolor_map[] = {
+	-2,						///<  0 = current
+	-1,						///<  1 = default
+	0x0000,					///<  2 = black
+	0x001D,					///<  3 = red
+	0x0340,					///<  4 = green
+	0x03BD,					///<  5 = yellow
+	0x59A0,					///<  6 = blue
+	0x7C1F,					///<  7 = magenta
+	0x77A0,					///<  8 = cyan
+	0x7FFF,					///<  9 = white
+	0x5AD6,					///< 10 = light grey
+	0x4631,					///< 11 = medium grey
+	0x2D6B,					///< 12 = dark grey
+};
+
+#define zcolor_NUMCOLORS    (13)
+
+void Processor::z_set_colour() {
+	int zfore = zargs[0];
+	int zback = zargs[1];
+
+	switch (zfore) {
+	case -1:
+		zfore = -3;
+		break;
+
+	case 0:
+	case 1:
+		zfore = zcolor_map[zfore];
+		break;
+
+	default:
+		if (zfore < zcolor_NUMCOLORS)
+			zfore = zRGB(zcolor_map[zfore]);
+		break;
+	}
+
+	switch (zback) {
+	case -1:
+		zback = -3;
+
+	case 0:
+	case 1:
+		zback = zcolor_map[zback];
+		break;
+
+	default:
+		if (zback < zcolor_NUMCOLORS)
+			zback = zRGB(zcolor_map[zback]);
+		break;
+	}
+
+#ifdef GARGLK
+	garglk_set_zcolors(zfore, zback);
+#endif /* GARGLK */
+
+	curr_fg = zfore;
+	curr_bg = zback;
+}
+
+void Processor::z_set_font() {
+	zword font = zargs[0];
+
+	switch (font) {
+		case 0:
+			// previous font
+			temp_font = curr_font;
+			curr_font = prev_font;
+			prev_font = temp_font;
+			zargs[0] = 0xf000;	// tickle tickle!
+			z_set_text_style();
+			store (curr_font);
+			break;
+
+		case 1: /* normal font */
+			prev_font = curr_font;
+			curr_font = 1;
+			zargs[0] = 0xf000;	// tickle tickle!
+			z_set_text_style();
+			store (prev_font);
+			break; 
+
+		case 4: /* fixed-pitch font*/
+			prev_font = curr_font;
+			curr_font = 4;
+			zargs[0] = 0xf000;	// tickle tickle!
+			z_set_text_style();
+			store (prev_font);
+			break;
+
+		case 2: // picture font, undefined per 1.1
+		case 3: // character graphics font
+		default: // unavailable
+			store (0);
+			break;
+	}
+}
+
+void Processor::z_set_cursor() {
+	cury = zargs[0];
+	curx = zargs[1];
+
+	if (gos_upper) {
+		if (cury > mach_status_ht) {
+			mach_status_ht = cury;
+			reset_status_ht();
+		}
+
+		glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
+	}
+}
+
+void Processor::z_set_text_style() {
+	int style;
+
+	if (zargs[0] == 0)
+		curstyle = 0;
+	else if (zargs[0] != 0xf000) /* not tickle time */
+		curstyle |= zargs[0];
+
+	if (h_flags & FIXED_FONT_FLAG || curr_font == 4)
+		style = curstyle | FIXED_WIDTH_STYLE;
+	else
+		style = curstyle;
+
+	if (gos_linepending && gos_curwin == gos_linewin)
+		return;
+
+	if (style & REVERSE_STYLE) {
+#ifdef GARGLK
+		garglk_set_reversevideo(true);
+#endif /* GARGLK */
+	}
+
+	if (style & FIXED_WIDTH_STYLE) {
+		if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
+			glk_set_style(style_BlockQuote);	// monoz
+		else if (style & EMPHASIS_STYLE)
+			glk_set_style(style_Alert);			// monoi
+		else if (style & BOLDFACE_STYLE)
+			glk_set_style(style_Subheader);		// monob
+		else
+			glk_set_style(style_Preformatted);	// monor
+	} else {
+		if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
+			glk_set_style(style_Note);			// propz
+		else if (style & EMPHASIS_STYLE)
+			glk_set_style(style_Emphasized);	// propi
+		else if (style & BOLDFACE_STYLE)
+			glk_set_style(style_Header);		// propb
+		else
+			glk_set_style(style_Normal);		// propr
+	}
+
+	if (curstyle == 0) {
+#ifdef GARGLK
+		garglk_set_reversevideo(false);
+#endif /* GARGLK */
+	}
+}
+
+void Processor::z_set_window() {
+	int win = zargs[0];
+
+	if (win == 0) {
+		glk_set_window(gos_lower);
+		gos_curwin = gos_lower;
+	} else {
+		if (gos_upper)
+			glk_set_window(gos_upper);
+		gos_curwin = gos_upper;
+	}
+
+	if (win == 0)
+		enable_scripting = true;
+	else
+		enable_scripting = false;
+
+	zargs[0] = 0xf000;	// tickle tickle!
+	z_set_text_style();
+}
+
+void Processor::pad_status_line(int column) {
+	int spaces;
+	spaces = (h_screen_cols + 1 - curx) - column;
+	while (spaces-- > 0)
+		print_char(' ');
+}
+
+void Processor::z_show_status() {
+	zword global0;
+	zword global1;
+	zword global2;
+	zword addr;
+
+	bool brief = false;
+
+	if (!gos_upper)
+		return;
+
+	// One V5 game (Wishbringer Solid Gold) contains this opcode by accident,
+	// so just return if the version number does not fit
+	if (h_version >= V4)
+		return;
+
+	// Read all relevant global variables from the memory of the Z-machine
+	// into local variables
+
+	addr = h_globals;
+	LOW_WORD(addr, global0);
+	addr += 2;
+	LOW_WORD(addr, global1);
+	addr += 2;
+	LOW_WORD(addr, global2);
+
+	// Move to top of the status window, and print in reverse style.
+	glk_set_window(gos_upper);
+	gos_curwin = gos_upper;
+
+#ifdef GARGLK
+	garglk_set_reversevideo(true);
+#endif /* GARGLK */
+
+	curx = cury = 1;
+	glk_window_move_cursor(gos_upper, 0, 0);
+
+	// If the screen width is below 55 characters then we have to use
+	// the brief status line format
+	if (h_screen_cols < 55)
+		brief = true;
+
+	// Print the object description for the global variable 0
+	print_char (' ');
+	print_object (global0);
+
+	// A header flag tells us whether we have to display the current
+	// time or the score/moves information
+	if (h_config & CONFIG_TIME) {
+		// print hours and minutes
+		zword hours = (global1 + 11) % 12 + 1;
+
+		pad_status_line (brief ? 15 : 20);
+
+		print_string ("Time: ");
+
+		if (hours < 10)
+			print_char (' ');
+		print_num (hours);
+
+		print_char (':');
+
+		if (global2 < 10)
+			print_char ('0');
+		print_num (global2);
+
+		print_char (' ');
+
+		print_char ((global1 >= 12) ? 'p' : 'a');
+		print_char ('m');
+
+	} else {
+		// print score and moves
+		pad_status_line (brief ? 15 : 30);
+
+		print_string (brief ? "S: " : "Score: ");
+		print_num (global1);
+
+		pad_status_line (brief ? 8 : 14);
+
+		print_string (brief ? "M: " : "Moves: ");
+		print_num (global2);
+	}
+
+	// Pad the end of the status line with spaces
+	pad_status_line (0);
+
+	// Return to the lower window
+	glk_set_window(gos_lower);
+	gos_curwin = gos_lower;
+}
+
+void Processor::z_split_window() {
+	split_window(zargs[0]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
new file mode 100644
index 0000000..5fe526f
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -0,0 +1,533 @@
+/* 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 "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Implement method stubs
+static void os_scrollback_char(zchar) {}
+static void os_scrollback_erase(zword) {}
+static void flush_buffer() {}
+static void script_open() {}
+static void script_close() {}
+static void script_mssg_on() {}
+static void script_mssg_off() {}
+static void script_char(zchar) {}
+static void script_word(const zchar *) {}
+static void script_new_line() {}
+static void script_erase_input(const zchar *) {}
+static void script_write_input(zchar *, char) {}
+static void memory_open(zword, zword, bool) {}
+static void memory_close() {}
+static void memory_word(const zchar *) {}
+static void memory_new_line() {}
+static void replay_open() {}
+static void replay_close() {}
+static zchar replay_read_key() { return 0; }
+static zchar replay_read_input(zchar *) { return 0; }
+static zchar console_read_key(zword) { return 0; }
+static zchar console_read_input(uint, zchar *, uint, bool) { return 0; }
+static void record_open() {}
+static void record_close() {}
+static void record_write_key(zchar) {}
+static void record_write_input(zchar *, zchar) {}
+static void os_restart_game(zword) {}
+static void restart_header() {}
+
+
+void Processor::scrollback_char (zchar c) {
+    if (c == ZC_INDENT)
+        { scrollback_char (' '); scrollback_char (' '); scrollback_char (' '); return; }
+    if (c == ZC_GAP)
+        { scrollback_char (' '); scrollback_char (' '); return; }
+
+    os_scrollback_char(c);
+}
+
+void Processor::scrollback_word(const zchar *s) {
+    int i;
+
+	for (i = 0; s[i] != 0; i++) {
+		if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
+			i++;
+		else
+			scrollback_char(s[i]);
+	}
+}
+
+void Processor::scrollback_write_input(const zchar *buf, zchar key) {
+    int i;
+
+    for (i = 0; buf[i] != 0; i++)
+        scrollback_char (buf[i]);
+
+    if (key == ZC_RETURN)
+        scrollback_char ('\n');
+}
+
+void Processor::scrollback_erase_input(const zchar *buf) {
+    int width;
+    int i;
+
+    for (i = 0, width = 0; buf[i] != 0; i++)
+        width++;
+
+    os_scrollback_erase(width);
+
+}
+
+void Processor::stream_mssg_on() {
+    flush_buffer();
+
+    if (ostream_screen)
+	    screen_mssg_on();
+    if (ostream_script && enable_scripting)
+	script_mssg_on();
+
+    message = true;
+}
+
+void Processor::stream_mssg_off() {
+    flush_buffer();
+
+    if (ostream_screen)
+		screen_mssg_off();
+    if (ostream_script && enable_scripting)
+		script_mssg_off();
+
+    message = false;
+}
+
+void Processor::stream_char(zchar c) {
+    if (ostream_screen)
+		screen_char(c);
+    if (ostream_script && enable_scripting)
+		script_char(c);
+    if (enable_scripting)
+		scrollback_char(c);
+}
+
+void Processor::stream_word(const zchar *s) {
+    if (ostream_memory && !message)
+		memory_word(s);
+    else {
+		if (ostream_screen)
+			screen_word(s);
+		if (ostream_script && enable_scripting)
+			script_word(s);
+		if (enable_scripting)
+			scrollback_word(s);
+    }
+}
+
+void Processor::stream_new_line() {
+    if (ostream_memory && !message)
+		memory_new_line();
+    else {
+		if (ostream_screen)
+			screen_new_line();
+		if (ostream_script && enable_scripting)
+			script_new_line();
+		if (enable_scripting)
+			os_scrollback_char ('\n');
+    }
+}
+
+zchar Processor::stream_read_key(zword timeout, zword routine, bool hot_keys) {
+    zchar key = ZC_BAD;
+
+    flush_buffer();
+
+    // Read key from current input stream
+continue_input:
+
+    do {
+		if (istream_replay)
+			key = replay_read_key();
+		else
+			key = console_read_key(timeout);
+    } while (key == ZC_BAD);
+
+    // Copy key to the command file
+    if (ostream_record && !istream_replay)
+		record_write_key(key);
+
+    // Handle timeouts
+    if (key == ZC_TIME_OUT)
+		if (direct_call (routine) == 0)
+			goto continue_input;
+
+    // Return key
+    return key;
+}
+
+zchar Processor::stream_read_input(int max, zchar *buf, zword timeout, zword routine,
+			  bool hot_keys, bool no_scripting) {
+    zchar key = ZC_BAD;
+    bool no_scrollback = no_scripting;
+
+    if (h_version == V6 && _storyId == UNKNOWN && !ostream_script)
+		no_scrollback = false;
+
+    flush_buffer();
+
+    // Remove initial input from the transscript file or from the screen
+    if (ostream_script && enable_scripting && !no_scripting)
+	script_erase_input(buf);
+
+    // Read input line from current input stream
+continue_input:
+
+    do {
+		if (istream_replay)
+			key = replay_read_input(buf);
+		else
+			key = console_read_input(max, buf, timeout, key != ZC_BAD);
+    } while (key == ZC_BAD);
+
+    // Copy input line to the command file
+    if (ostream_record && !istream_replay)
+		record_write_input(buf, key);
+
+    // Handle timeouts
+    if (key == ZC_TIME_OUT)
+	if (direct_call(routine) == 0)
+	    goto continue_input;
+
+    // Copy input line to transscript file or to the screen
+    if (ostream_script && enable_scripting && !no_scripting)
+		script_write_input(buf, key);
+
+    // Return terminating key
+    return key;
+}
+
+void Processor::z_input_stream() {
+	flush_buffer();
+
+	if (zargs[0] == 0 && istream_replay)
+		replay_close();
+	if (zargs[0] == 1 && !istream_replay)
+		replay_open();
+}
+
+void Processor::z_output_stream() {
+    flush_buffer();
+
+    switch ((short) zargs[0]) {
+    case  1: ostream_screen = true;
+	     break;
+    case -1: ostream_screen = false;
+	     break;
+    case  2: if (!ostream_script) script_open();
+	     break;
+    case -2: if (ostream_script) script_close();
+	     break;
+    case  3: memory_open(zargs[1], zargs[2], zargc >= 3);
+	     break;
+    case -3: memory_close();
+	     break;
+    case  4: if (!ostream_record) record_open();
+	     break;
+    case -4: if (ostream_record) record_close();
+	     break;
+	default:
+		break;
+    }
+}
+
+void Processor::z_restart(void) {
+	flush_buffer();
+
+	os_restart_game(RESTART_BEGIN);
+
+	seed_random(0);
+
+	if (!first_restart) {
+		story_fp->seek(blorb_ofs);
+
+		if (story_fp->read(zmp, h_dynamic_size) != h_dynamic_size)
+			error("Story file read error");
+
+	} else {
+		first_restart = false;
+	}
+
+	restart_header();
+	restart_screen();
+
+	_sp = _fp = _stack + STACK_SIZE;
+	_frameCount = 0;
+
+	if (h_version != V6 && h_version != V9) {
+		long pc = (long)h_start_pc;
+		SET_PC(pc);
+	} else {
+		call(h_start_pc, 0, nullptr, 0);
+	}
+
+	os_restart_game(RESTART_END);
+}
+
+
+void Processor::z_save(void) {
+#ifdef TODO
+	bool success = false;
+
+	if (zargc != 0) {
+		// Open auxilary file
+		frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
+			filemode_Write, 0);
+		if (ref == nullptr)
+			goto finished;
+
+		// Write data
+		strid_t f = glk_stream_open_file(ref, filemode_Write);
+
+		glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]);
+
+		stream_result_t result;
+		glk_stream_close(f, &result);
+
+	} else {
+		long pc;
+		zword addr;
+		zword nsp, nfp;
+		int skip;
+		int i;
+
+		/* Open game file */
+
+		if ((gfp = frotzopenprompt (FILE_SAVE)) == nullptr)
+			goto finished;
+
+		if (option_save_quetzal) {
+			success = save_quetzal (gfp, story_fp, blorb_ofs);
+		} else {
+			/* Write game file */
+
+			fputc ((int) hi (h_release), gfp);
+			fputc ((int) lo (h_release), gfp);
+			fputc ((int) hi (h_checksum), gfp);
+			fputc ((int) lo (h_checksum), gfp);
+
+			GET_PC (pc)
+
+				fputc ((int) (pc >> 16) & 0xff, gfp);
+			fputc ((int) (pc >> 8) & 0xff, gfp);
+			fputc ((int) (pc) & 0xff, gfp);
+
+			nsp = (int) (_sp - _stack);
+			nfp = (int) (_fp - _stack);
+
+			fputc ((int) hi (nsp), gfp);
+			fputc ((int) lo (nsp), gfp);
+			fputc ((int) hi (nfp), gfp);
+			fputc ((int) lo (nfp), gfp);
+
+			for (i = nsp; i < STACK_SIZE; i++) {
+				fputc ((int) hi (_stack[i]), gfp);
+				fputc ((int) lo (_stack[i]), gfp);
+			}
+
+			fseek (story_fp, blorb_ofs, SEEK_SET);
+
+			for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
+				if (zmp[addr] != fgetc (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
+					fputc (skip, gfp);
+					fputc (zmp[addr], gfp);
+					skip = 0;
+				} else skip++;
+		}
+
+		/* Close game file and check for errors */
+
+		if (fclose (gfp) == EOF || ferror (story_fp)) {
+			print_string ("Error writing save file\n");
+			goto finished;
+		}
+
+		/* Success */
+
+		success = 1;
+
+	}
+
+finished:
+
+	if (h_version <= V3)
+		branch (success);
+	else
+		store (success);
+#endif
+}
+
+void Processor::z_restore() {
+#ifdef TODO
+	FILE *gfp;
+
+	zword success = 0;
+
+	if (zargc != 0) {
+
+		/* Get the file name */
+
+		/* Open auxilary file */
+
+		if ((gfp = frotzopenprompt(FILE_LOAD_AUX)) == nullptr)
+			goto finished;
+
+		/* Load auxilary file */
+
+		success = fread (zmp + zargs[0], 1, zargs[1], gfp);
+
+		/* Close auxilary file */
+
+		fclose (gfp);
+
+	} else {
+
+		long pc;
+		zword release;
+		zword addr;
+		int i;
+
+		/* Open game file */
+
+		if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
+			goto finished;
+
+		if (option_save_quetzal) {
+			success = restore_quetzal (gfp, story_fp, blorb_ofs);
+
+		} else {
+			/* Load game file */
+
+			release = (unsigned) fgetc (gfp) << 8;
+			release |= fgetc (gfp);
+
+			(void) fgetc (gfp);
+			(void) fgetc (gfp);
+
+			/* Check the release number */
+
+			if (release == h_release) {
+
+				pc = (long) fgetc (gfp) << 16;
+				pc |= (unsigned) fgetc (gfp) << 8;
+				pc |= fgetc (gfp);
+
+				SET_PC (pc);
+
+				_sp = _stack + (fgetc (gfp) << 8);
+				_sp += fgetc (gfp);
+				_fp = _stack + (fgetc (gfp) << 8);
+				_fp += fgetc (gfp);
+
+				for (i = (int) (_sp - _stack); i < STACK_SIZE; i++) {
+					_stack[i] = (unsigned) fgetc (gfp) << 8;
+					_stack[i] |= fgetc (gfp);
+				}
+
+				fseek (story_fp, blorb_ofs, SEEK_SET);
+
+				for (addr = 0; addr < h_dynamic_size; addr++) {
+					int skip = fgetc (gfp);
+					for (i = 0; i < skip; i++)
+						zmp[addr++] = fgetc (story_fp);
+					zmp[addr] = fgetc (gfp);
+					(void) fgetc (story_fp);
+				}
+
+				/* Check for errors */
+
+				if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
+					success = -1;
+				else
+
+					/* Success */
+
+					success = 2;
+
+			} else print_string ("Invalid save file\n");
+		}
+
+		if ((short) success >= 0) {
+
+			/* Close game file */
+
+			fclose (gfp);
+
+			if ((short) success > 0) {
+				zbyte old_screen_rows;
+				zbyte old_screen_cols;
+
+				/* In V3, reset the upper window. */
+				if (h_version == V3)
+					split_window (0);
+
+				LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
+				LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
+
+				/* Reload cached header fields. */
+				restart_header ();
+
+				/*
+				 * Since QUETZAL files may be saved on many different machines,
+				 * the screen sizes may vary a lot. Erasing the status window
+				 * seems to cover up most of the resulting badness.
+				 */
+				if (h_version > V3 && h_version != V6
+						&& (h_screen_rows != old_screen_rows
+							|| h_screen_cols != old_screen_cols))
+					erase_window (1);
+			}
+		} else
+			os_fatal ("Error reading save file");
+	}
+
+finished:
+
+	if (h_version <= V3)
+		branch (success);
+	else
+		store (success);
+#endif
+}
+
+void Processor::z_verify(void) {
+	zword checksum = 0;
+
+	// Sum all bytes in story file except header bytes
+	story_fp->seek(blorb_ofs + 64);
+
+	for (uint i = 64; i < story_size; i++)
+		checksum += story_fp->readByte();
+
+	// Branch if the checksums are equal
+	branch(checksum == h_checksum);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_table.cpp b/engines/gargoyle/frotz/processor_table.cpp
new file mode 100644
index 0000000..4e660ca
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_table.cpp
@@ -0,0 +1,125 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Method stubs to implement
+static void storeb(zword, zbyte) {}
+static void storew(zword, zword) {}
+
+
+void Processor::z_copy_table() {
+    zword addr;
+    zword size = zargs[2];
+    zbyte value;
+    int i;
+
+    if (zargs[1] == 0)      				/* zero table */
+
+	for (i = 0; i < size; i++)
+	    storeb((zword) (zargs[0] + i), 0);
+
+    else if ((short) size < 0 || zargs[0] > zargs[1])	/* copy forwards */
+
+	for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) {
+	    addr = zargs[0] + i;
+		LOW_BYTE(addr, value);
+	    storeb((zword) (zargs[1] + i), value);
+	} else {
+		// copy backwards
+		for (i = size - 1; i >= 0; i--) {
+			addr = zargs[0] + i;
+			LOW_BYTE(addr, value);
+			storeb((zword) (zargs[1] + i), value);
+		}
+	}
+}
+
+void Processor::z_loadb() {
+    zword addr = zargs[0] + zargs[1];
+    zbyte value;
+
+	LOW_BYTE(addr, value);
+
+    store(value);
+}
+
+void Processor::z_loadw() {
+    zword addr = zargs[0] + 2 * zargs[1];
+    zword value;
+
+	LOW_WORD(addr, value);
+
+    store(value);
+}
+
+void Processor::z_scan_table() {
+    zword addr = zargs[1];
+    int i;
+
+    // Supply default arguments
+    if (zargc < 4)
+	zargs[3] = 0x82;
+
+    // Scan byte or word array
+    for (i = 0; i < zargs[2]; i++) {
+		if (zargs[3] & 0x80) {
+			// scan word array
+			zword wvalue;
+
+			LOW_WORD(addr, wvalue);
+
+			if (wvalue == zargs[0])
+				goto finished;
+		} else {
+			// scan byte array
+			zbyte bvalue;
+
+			LOW_BYTE(addr, bvalue);
+
+			if (bvalue == zargs[0])
+				goto finished;
+		}
+
+		addr += zargs[3] & 0x7f;
+    }
+
+    addr = 0;
+
+finished:
+    store(addr);
+    branch(addr);
+}
+
+void Processor::z_storeb() {
+    storeb((zword) (zargs[0] + zargs[1]), zargs[2]);
+}
+
+void Processor::z_storew() {
+    storew((zword)(zargs[0] + 2 * zargs[1]), zargs[2]);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_text.cpp b/engines/gargoyle/frotz/processor_text.cpp
new file mode 100644
index 0000000..e660ac1
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_text.cpp
@@ -0,0 +1,904 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Replace method stubs
+static void new_line() {}
+static void print_char(zchar) {}
+static void storeb(zword, zchar) {}
+static void storew(zword, zword) {}
+
+
+zchar Processor::ZSCII_TO_LATIN1[] = {
+    0x0e4, 0x0f6, 0x0fc, 0x0c4, 0x0d6, 0x0dc, 0x0df, 0x0bb,
+    0x0ab, 0x0eb, 0x0ef, 0x0ff, 0x0cb, 0x0cf, 0x0e1, 0x0e9,
+    0x0ed, 0x0f3, 0x0fa, 0x0fd, 0x0c1, 0x0c9, 0x0cd, 0x0d3,
+    0x0da, 0x0dd, 0x0e0, 0x0e8, 0x0ec, 0x0f2, 0x0f9, 0x0c0,
+    0x0c8, 0x0cc, 0x0d2, 0x0d9, 0x0e2, 0x0ea, 0x0ee, 0x0f4,
+    0x0fb, 0x0c2, 0x0ca, 0x0ce, 0x0d4, 0x0db, 0x0e5, 0x0c5,
+    0x0f8, 0x0d8, 0x0e3, 0x0f1, 0x0f5, 0x0c3, 0x0d1, 0x0d5,
+    0x0e6, 0x0c6, 0x0e7, 0x0c7, 0x0fe, 0x0f0, 0x0de, 0x0d0,
+    0x0a3, 0x153, 0x152, 0x0a1, 0x0bf
+};
+
+zchar Processor::translate_from_zscii(zbyte c) {
+    if (c == 0xfc)
+		return ZC_MENU_CLICK;
+    if (c == 0xfd)
+		return ZC_DOUBLE_CLICK;
+    if (c == 0xfe)
+		return ZC_SINGLE_CLICK;
+
+    if (c >= 0x9b && _storyId != BEYOND_ZORK) {
+		if (hx_unicode_table != 0) {
+			// game has its own Unicode table
+			zbyte N;
+			LOW_BYTE(hx_unicode_table, N);
+
+			if (c - 0x9b < N) {
+				zword addr = hx_unicode_table + 1 + 2 * (c - 0x9b);
+				zword unicode;
+
+				LOW_WORD(addr, unicode);
+
+				if (unicode < 0x20)
+					return '?';
+
+				return unicode;
+			} else {
+				return '?';
+			}
+		} else {
+			// game uses standard set
+			if (c <= 0xdf) {
+				return ZSCII_TO_LATIN1[c - 0x9b];
+			} else {
+				return '?';
+			}
+		}
+    }
+
+    return (zchar)c;
+}
+
+zbyte Processor::unicode_to_zscii(zchar c) {
+    int i;
+
+    if (c >= ZC_LATIN1_MIN) {
+		if (hx_unicode_table != 0) {
+			// game has its own Unicode table
+			zbyte N;
+			LOW_BYTE(hx_unicode_table, N);
+
+			for (i = 0x9b; i < 0x9b + N; i++) {
+				zword addr = hx_unicode_table + 1 + 2 * (i - 0x9b);
+				zword unicode;
+
+				LOW_WORD(addr, unicode);
+
+				if (c == unicode)
+					return (zbyte) i;
+			}
+
+			return 0;
+		} else {
+			// game uses standard set
+			for (i = 0x9b; i <= 0xdf; i++)
+			if (c == ZSCII_TO_LATIN1[i - 0x9b])
+				return (zbyte) i;
+
+			return 0;
+		}
+    }
+
+    return (zbyte)c;
+}
+
+zbyte Processor::translate_to_zscii(zchar c) {
+    if (c == ZC_SINGLE_CLICK)
+		return 0xfe;
+    if (c == ZC_DOUBLE_CLICK)
+		return 0xfd;
+    if (c == ZC_MENU_CLICK)
+		return 0xfc;
+    if (c == 0)
+		return 0;
+
+    c = unicode_to_zscii (c);
+    if (c == 0)
+	c = '?';
+
+    return (zbyte)c;
+}
+
+zchar Processor::alphabet(int set, int index) {
+    if (h_version > V1 && set == 2 && index == 1)
+		// always newline
+		return '\r';
+
+    if (h_alphabet != 0) {
+		// game uses its own alphabet
+		zbyte c;
+
+		zword addr = h_alphabet + 26 * set + index;
+		LOW_BYTE(addr, c);
+
+		return translate_from_zscii(c);
+    } else {
+		// game uses default alphabet
+		if (set == 0)
+			return 'a' + index;
+		else if (set == 1)
+			return 'A' + index;
+		else if (h_version == V1)
+			return " 0123456789.,!?_#'\"/\\<-:()"[index];
+		else
+			return " ^0123456789.,!?_#'\"/\\-:()"[index];
+	}
+}
+
+void Processor::find_resolution() {
+    zword dct = h_dictionary;
+    zword entry_count;
+    zbyte sep_count;
+    zbyte entry_len;
+
+	LOW_BYTE(dct, sep_count);
+    dct += 1 + sep_count;  // skip word separators
+	LOW_BYTE(dct, entry_len);
+    dct += 1;              // skip entry length
+	LOW_WORD(dct, entry_count);
+    dct += 2;              // get number of entries
+
+    if (h_version < V9) {
+		_resolution = (h_version <= V3) ? 2 : 3;
+    } else {
+		zword addr = dct;
+		zword code;
+
+		if (entry_count == 0)
+			runtimeError(ERR_DICT_LEN);
+
+		// check the first word in the dictionary
+		do {
+			LOW_WORD(addr, code);
+			addr += 2;
+		} while (!(code & 0x8000) && (addr - dct < entry_len + 1));
+
+		_resolution = (addr - dct) / 2;
+	}
+
+	if (2 * _resolution > entry_len) {
+		runtimeError(ERR_DICT_LEN);
+    }
+
+    _decoded = (zchar *)malloc (sizeof (zchar) * (3 * _resolution) + 1);
+    _encoded = (zchar *)malloc (sizeof (zchar) * _resolution);
+}
+
+void Processor::load_string (zword addr, zword length) {
+    int i = 0;
+
+    if (_resolution == 0)
+		find_resolution();
+
+	while (i < 3 * _resolution) {
+		if (i < length) {
+			zbyte c;
+			LOW_BYTE(addr, c);
+			addr++;
+
+			_decoded[i++] = translate_from_zscii(c);
+		} else {
+			_decoded[i++] = 0;
+		}
+	}
+}
+
+void Processor::encode_text(int padding) {
+    static const zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0, 0, 0, 0 };
+    static const zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0, 0 };
+    static const zchar wait[] = { 'w', 'a', 'i', 't', 0, 0, 0, 0, 0 };
+
+    zbyte *zchars;
+    const zchar *ptr;
+    zchar c;
+    int i = 0;
+
+    if (_resolution == 0) find_resolution();
+
+    zchars = new byte[3 * (_resolution + 1)];
+    ptr = _decoded;
+
+    // Expand abbreviations that some old Infocom games lack
+	if (_expand_abbreviations && (h_version <= V8)) {
+		if (padding == 0x05 && _decoded[1] == 0) {
+			switch (_decoded[0]) {
+			case 'g': ptr = again; break;
+			case 'x': ptr = examine; break;
+			case 'z': ptr = wait; break;
+			default: break;
+			}
+		}
+	}
+
+    // Translate string to a sequence of Z-characters
+    while (i < 3 * _resolution) {
+		if ((c = *ptr++) != 0) {
+			int index, set;
+			zbyte c2;
+
+			if (c == ' ') {
+				zchars[i++] = 0;
+				continue;
+			}
+
+			// Search character in the alphabet
+			for (set = 0; set < 3; set++)
+				for (index = 0; index < 26; index++)
+					if (c == alphabet (set, index))
+						goto letter_found;
+
+			// Character not found, store its ZSCII value
+			c2 = translate_to_zscii (c);
+
+			zchars[i++] = 5;
+			zchars[i++] = 6;
+			zchars[i++] = c2 >> 5;
+			zchars[i++] = c2 & 0x1f;
+			continue;
+
+	letter_found:
+			// Character found, store its index
+			if (set != 0)
+				zchars[i++] = ((h_version <= V2) ? 1 : 3) + set;
+
+			zchars[i++] = index + 6;
+		} else {
+			zchars[i++] = padding;
+		}
+	}
+
+	// Three Z-characters make a 16bit word
+    for (i = 0; i < _resolution; i++)
+		_encoded[i] =
+			(zchars[3 * i + 0] << 10) |
+			(zchars[3 * i + 1] << 5) |
+			(zchars[3 * i + 2]);
+
+    _encoded[_resolution - 1] |= 0x8000;
+	delete[]  zchars;
+}
+
+#define outchar(c)	if (st == VOCABULARY) *ptr++=c; else print_char(c)
+
+void Processor::decode_text(enum string_type st, zword addr) {
+	zchar *ptr = nullptr;
+	long byte_addr = 0;
+	zchar c2;
+	zword code;
+	zbyte c, prev_c = 0;
+	int shift_state = 0;
+	int shift_lock = 0;
+	int status = 0;
+
+	if (_resolution == 0)
+		find_resolution();
+
+	// Calculate the byte address if necessary
+	if (st == ABBREVIATION)
+		byte_addr = (long)addr << 1;
+
+	else if (st == HIGH_STRING) {
+		if (h_version <= V3)
+			byte_addr = (long)addr << 1;
+		else if (h_version <= V5)
+			byte_addr = (long)addr << 2;
+		else if (h_version <= V7)
+			byte_addr = ((long)addr << 2) + ((long)h_strings_offset << 3);
+		else if (h_version <= V8)
+			byte_addr = (long)addr << 3;
+		else {
+			// h_version == V9
+			long indirect = (long)addr << 2;
+			HIGH_LONG(indirect, byte_addr);
+		}
+
+		if ((uint)byte_addr >= story_size)
+			runtimeError(ERR_ILL_PRINT_ADDR);
+	}
+
+	// Loop until a 16bit word has the highest bit set
+	if (st == VOCABULARY)
+		ptr = _decoded;
+
+	do {
+		int i;
+
+		// Fetch the next 16bit word
+		if (st == LOW_STRING || st == VOCABULARY) {
+			LOW_WORD(addr, code);
+			addr += 2;
+		} else if (st == HIGH_STRING || st == ABBREVIATION) {
+			HIGH_WORD(byte_addr, code);
+			byte_addr += 2;
+		} else {
+			CODE_WORD(code);
+		}
+
+		// Read its three Z-characters
+		for (i = 10; i >= 0; i -= 5) {
+			zword abbr_addr;
+			zword ptr_addr;
+			zchar zc;
+
+			c = (code >> i) & 0x1f;
+
+			switch (status) {
+			case 0:
+				// normal operation
+				if (shift_state == 2 && c == 6)
+					status = 2;
+
+				else if (h_version == V1 && c == 1)
+					new_line();
+
+				else if (h_version >= V2 && shift_state == 2 && c == 7)
+					new_line();
+
+				else if (c >= 6)
+					outchar(alphabet(shift_state, c - 6));
+
+				else if (c == 0)
+					outchar(' ');
+
+				else if (h_version >= V2 && c == 1)
+					status = 1;
+
+				else if (h_version >= V3 && c <= 3)
+					status = 1;
+
+				else {
+					shift_state = (shift_lock + (c & 1) + 1) % 3;
+
+					if (h_version <= V2 && c >= 4)
+						shift_lock = shift_state;
+
+					break;
+				}
+
+				shift_state = shift_lock;
+				break;
+
+			case 1:
+				// abbreviation
+				ptr_addr = h_abbreviations + 64 * (prev_c - 1) + 2 * c;
+
+				LOW_WORD(ptr_addr, abbr_addr);
+				decode_text(ABBREVIATION, abbr_addr);
+
+				status = 0;
+				break;
+
+			case 2:
+				// ZSCII character - first part
+				status = 3;
+				break;
+
+			case 3:
+				// ZSCII character - second part
+				zc = (prev_c << 5) | c;
+
+				if (zc > 767) {
+					// Unicode escape
+					while (zc-- > 767) {
+						if (st == LOW_STRING || st == VOCABULARY) {
+							LOW_WORD(addr, c2);
+							addr += 2;
+						} else if (st == HIGH_STRING || st == ABBREVIATION) {
+							HIGH_WORD(byte_addr, c2);
+							byte_addr += 2;
+						} else
+							CODE_WORD(c2);
+
+						outchar(c2 ^ 0xFFFF);
+					}
+				} else {
+					c2 = translate_from_zscii(zc);
+					outchar(c2);
+				}
+
+				status = 0;
+				break;
+
+			default:
+				break;
+			}
+
+			prev_c = c;
+		}
+	} while (!(code & 0x8000));
+
+	if (st == VOCABULARY)
+		*ptr = 0;
+}
+
+#undef outchar
+
+void Processor::print_num(zword value) {
+	int i;
+
+	/* Print sign */
+
+	if ((short)value < 0) {
+		print_char('-');
+		value = -(short)value;
+	}
+
+	/* Print absolute value */
+
+	for (i = 10000; i != 0; i /= 10)
+		if (value >= i || i == 1)
+			print_char('0' + (value / i) % 10);
+
+}
+
+void Processor::print_object(zword object) {
+	zword addr = object_name(object);
+	zword code = 0x94a5;
+	zbyte length;
+
+	LOW_BYTE(addr, length);
+	addr++;
+
+	if (length != 0)
+		LOW_WORD(addr, code);
+
+	if (code == 0x94a5) {
+		// _encoded text 0x94a5 == empty string
+		print_string("object#");	// supply a generic name
+		print_num(object);			// for anonymous objects
+	} else {
+		decode_text(LOW_STRING, addr);
+	}
+}
+
+void Processor::print_string(const char *s) {
+	char c;
+
+	while ((c = *s++) != 0) {
+		if (c == '\n')
+			new_line();
+		else
+			print_char(c);
+	}
+}
+
+zword Processor::lookup_text(int padding, zword dct) {
+	zword entry_addr;
+	zword entry_count;
+	zword entry;
+	zword addr;
+	zbyte entry_len;
+	zbyte sep_count;
+	int entry_number;
+	int lower, upper;
+	int i;
+	bool sorted;
+
+	if (_resolution == 0)
+		find_resolution();
+
+	encode_text(padding);
+
+	LOW_BYTE(dct, sep_count);		// skip word separators
+	dct += 1 + sep_count;
+	LOW_BYTE(dct, entry_len);		// get length of entries
+	dct += 1;
+	LOW_WORD(dct, entry_count);		// get number of entries
+	dct += 2;
+
+	if ((short)entry_count < 0) {
+		// bad luck, entries aren't sorted
+		entry_count = -(short)entry_count;
+		sorted = false;
+
+	} else {
+		sorted = true;				// entries are sorted
+	}
+
+	lower = 0;
+	upper = entry_count - 1;
+
+	while (lower <= upper) {
+		if (sorted)
+			// binary search
+			entry_number = (lower + upper) / 2;
+		else
+			// linear search
+			entry_number = lower;
+
+		entry_addr = dct + entry_number * entry_len;
+
+		// Compare word to dictionary entry
+		addr = entry_addr;
+
+		for (i = 0; i < _resolution; i++) {
+			LOW_WORD(addr, entry);
+			if (_encoded[i] != entry)
+				goto continuing;
+			addr += 2;
+		}
+
+		return entry_addr;		// exact match found, return now
+
+	continuing:
+		if (sorted) {
+			// binary search
+			if (_encoded[i] > entry)
+				lower = entry_number + 1;
+			else
+				upper = entry_number - 1;
+		} else {
+			// linear search
+			lower++;
+		}
+	}
+
+	// No exact match has been found
+	if (padding == 0x05)
+		return 0;
+
+	entry_number = (padding == 0x00) ? lower : upper;
+
+	if (entry_number == -1 || entry_number == entry_count)
+		return 0;
+
+	return dct + entry_number * entry_len;
+}
+
+void Processor::tokenise_text(zword text, zword length, zword from, zword parse, zword dct, bool flag) {
+	zword addr;
+	zbyte token_max, token_count;
+
+	LOW_BYTE(parse, token_max);
+	parse++;
+	LOW_BYTE(parse, token_count);
+
+	if (token_count < token_max) {
+		// sufficient space left for token?
+		storeb(parse++, token_count + 1);
+
+		load_string((zword)(text + from), length);
+
+		addr = lookup_text(0x05, dct);
+
+		if (addr != 0 || !flag) {
+
+			parse += 4 * token_count;
+
+			storew((zword)(parse + 0), addr);
+			storeb((zword)(parse + 2), length);
+			storeb((zword)(parse + 3), from);
+		}
+	}
+}
+
+void Processor::tokenise_line(zword text, zword token, zword dct, bool flag) {
+	zword addr1;
+	zword addr2;
+	zbyte length = 0;
+	zbyte c;
+
+	// Use standard dictionary if the given dictionary is zero
+	if (dct == 0)
+		dct = h_dictionary;
+
+	// Remove all tokens before inserting new ones
+	storeb((zword)(token + 1), 0);
+
+	//  Move the first pointer across the text buffer searching for the beginning
+	// of a word. If this succeeds, store the position in a second pointer.
+	// Move the first pointer searching for the end of the word. When it is found,
+	// "tokenise" the word. Continue until the end of the buffer is reached.
+	addr1 = text;
+	addr2 = 0;
+
+	if (h_version >= V5) {
+		addr1++;
+		LOW_BYTE(addr1, length);
+	}
+
+	do {
+		zword sep_addr;
+		zbyte sep_count;
+		zbyte separator;
+
+		// Fetch next ZSCII character
+		addr1++;
+
+		if (h_version >= V5 && addr1 == text + 2 + length)
+			c = 0;
+		else
+			LOW_BYTE(addr1, c);
+
+		// Check for separator 
+		sep_addr = dct;
+
+		LOW_BYTE(sep_addr, sep_count);
+		sep_addr++;
+
+		do {
+			LOW_BYTE(sep_addr, separator);
+			sep_addr++;
+		} while (c != separator && --sep_count != 0);
+
+		// This could be the start or the end of a word
+		if (sep_count == 0 && c != ' ' && c != 0) {
+			if (addr2 == 0)
+				addr2 = addr1;
+		} else if (addr2 != 0) {
+			tokenise_text(text, (zword)(addr1 - addr2), (zword)(addr2 - text),
+				token, dct, flag);
+
+			addr2 = 0;
+		}
+
+		// Translate separator (which is a word in its own right)
+		if (sep_count != 0)
+			tokenise_text(text, (zword)(1), (zword)(addr1 - text), token, dct, flag);
+
+	} while (c != 0);
+}
+
+int Processor::completion(const zchar *buffer, zchar *result) {
+	zword minaddr;
+	zword maxaddr;
+	zchar *ptr;
+	zchar c;
+	int len;
+	int i;
+
+	*result = 0;
+
+	if (_resolution == 0)
+		find_resolution();
+
+	// Copy last word to "_decoded" string
+	len = 0;
+
+	while ((c = *buffer++) != 0)
+		if (c != ' ') {
+			if (len < 3 * _resolution)
+				_decoded[len++] = c;
+		} else {
+			len = 0;
+		}
+
+	_decoded[len] = 0;
+
+	// Search the dictionary for first and last possible extensions
+	minaddr = lookup_text(0x00, h_dictionary);
+	maxaddr = lookup_text(0x1f, h_dictionary);
+
+	if (minaddr == 0 || maxaddr == 0 || minaddr > maxaddr)
+		return 2;
+
+	// Copy first extension to "result" string
+	decode_text(VOCABULARY, minaddr);
+
+	ptr = result;
+
+	for (i = len; (c = _decoded[i]) != 0; i++)
+		*ptr++ = c;
+	*ptr = 0;
+
+	// Merge second extension with "result" string
+	decode_text(VOCABULARY, maxaddr);
+
+	for (i = len, ptr = result; (c = _decoded[i]) != 0; i++, ptr++) {
+		if (*ptr != c)
+			break;
+	}
+	*ptr = 0;
+
+	// Search was ambiguous or successful
+	return (minaddr == maxaddr) ? 0 : 1;
+}
+
+zchar Processor::unicode_tolower(zchar c) {
+	static const byte tolower_basic_latin[0x100] = {
+		0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+		0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+		0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+		0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+		0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+		0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,
+		0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+		0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+		0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+		0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+		0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
+		0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+		0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+		0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xD7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xDF,
+		0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+		0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+	};
+	static const byte tolower_latin_extended_a[0x80] = {
+		0x01,0x01,0x03,0x03,0x05,0x05,0x07,0x07,0x09,0x09,0x0B,0x0B,0x0D,0x0D,0x0F,0x0F,
+		0x11,0x11,0x13,0x13,0x15,0x15,0x17,0x17,0x19,0x19,0x1B,0x1B,0x1D,0x1D,0x1F,0x1F,
+		0x21,0x21,0x23,0x23,0x25,0x25,0x27,0x27,0x29,0x29,0x2B,0x2B,0x2D,0x2D,0x2F,0x2F,
+		0x00,0x31,0x33,0x33,0x35,0x35,0x37,0x37,0x38,0x3A,0x3A,0x3C,0x3C,0x3E,0x3E,0x40,
+		0x40,0x42,0x42,0x44,0x44,0x46,0x46,0x48,0x48,0x49,0x4B,0x4B,0x4D,0x4D,0x4F,0x4F,
+		0x51,0x51,0x53,0x53,0x55,0x55,0x57,0x57,0x59,0x59,0x5B,0x5B,0x5D,0x5D,0x5F,0x5F,
+		0x61,0x61,0x63,0x63,0x65,0x65,0x67,0x67,0x69,0x69,0x6B,0x6B,0x6D,0x6D,0x6F,0x6F,
+		0x71,0x71,0x73,0x73,0x75,0x75,0x77,0x77,0x00,0x7A,0x7A,0x7C,0x7C,0x7E,0x7E,0x7F
+	};
+	static const byte tolower_greek[0x50] = {
+		0x80,0x81,0x82,0x83,0x84,0x85,0xAC,0x87,0xAD,0xAE,0xAF,0x8B,0xCC,0x8D,0xCD,0xCE,
+		0x90,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+		0xC0,0xC1,0xA2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xAC,0xAD,0xAE,0xAF,
+		0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF
+	};
+	static const byte tolower_cyrillic[0x60] = {
+		0x00,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
+		0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+		0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+		0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+		0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
+		0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F
+	};
+
+	if (c < 0x0100)
+		c = tolower_basic_latin[c];
+	else if (c == 0x0130)
+		c = 0x0069;		// Capital I with dot -> lower case i
+	else if (c == 0x0178)
+		c = 0x00FF;		// Capital Y diaeresis -> lower case y diaeresis
+	else if (c < 0x0180)
+		c = tolower_latin_extended_a[c - 0x100] + 0x100;
+	else if (c >= 0x380 && c < 0x3D0)
+		c = tolower_greek[c - 0x380] + 0x300;
+	else if (c >= 0x400 && c < 0x460)
+		c = tolower_cyrillic[c - 0x400] + 0x400;
+
+	return c;
+}
+
+
+void Processor::z_check_unicode() {
+    zword c = zargs[0];
+    zword result = 0;
+
+    if (c <= 0x1f) {
+		if ((c == 0x08) || (c == 0x0d) || (c == 0x1b))
+			result = 2;
+    } else if (c <= 0x7e) {
+		result = 3;
+	} else {
+		// we support unicode
+		result = 1;
+	}
+
+	store (result);
+}
+
+void Processor::z_encode_text() {
+    int i;
+
+    load_string((zword) (zargs[0] + zargs[2]), zargs[1]);
+
+    encode_text(0x05);
+
+    for (i = 0; i < _resolution; i++)
+	storew((zword) (zargs[3] + 2 * i), _encoded[i]);
+
+}
+
+void Processor::z_new_line() {
+    new_line ();
+}
+
+void Processor::z_print () {
+    decode_text(EMBEDDED_STRING, 0);
+}
+
+void Processor::z_print_addr() {
+    decode_text(LOW_STRING, zargs[0]);
+}
+
+void Processor::z_print_char() {
+    print_char (translate_from_zscii(zargs[0]));
+}
+
+void Processor::z_print_form() {
+    zword count;
+    zword addr = zargs[0];
+    bool first = true;
+
+    for (;;) {
+		LOW_WORD(addr, count);
+		addr += 2;
+
+		if (count == 0)
+			break;
+
+		if (!first)
+			new_line ();
+
+		while (count--) {
+			zbyte c;
+
+			LOW_BYTE(addr, c);
+			addr++;
+
+			print_char(translate_from_zscii (c));
+		}
+
+		first = false;
+    }
+}
+
+void Processor::z_print_num() {
+    print_num (zargs[0]);
+}
+
+void Processor::z_print_obj() {
+    print_object(zargs[0]);
+}
+
+void Processor::z_print_paddr() {
+    decode_text (HIGH_STRING, zargs[0]);
+}
+
+void Processor::z_print_ret() {
+    decode_text(EMBEDDED_STRING, 0);
+    new_line();
+    ret(1);
+}
+
+void Processor::z_print_unicode() {
+    if (zargs[0] < 0x20)
+		print_char('?');
+    else
+		print_char(zargs[0]);
+}
+
+void Processor::z_tokenise() {
+    // Supply default arguments
+    if (zargc < 3)
+		zargs[2] = 0;
+    if (zargc < 4)
+		zargs[3] = 0;
+
+    // Call tokenise_line to do the real work
+    tokenise_line(zargs[0], zargs[1], zargs[2], zargs[3] != 0);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_variables.cpp b/engines/gargoyle/frotz/processor_variables.cpp
new file mode 100644
index 0000000..24bbd28
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_variables.cpp
@@ -0,0 +1,203 @@
+/* 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 "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Replace method stubs
+static void storew(zword, zword) {}
+
+
+void Processor::z_dec() {
+	zword value;
+
+	if (zargs[0] == 0)
+		(*_sp)--;
+	else if (zargs[0] < 16)
+		(*(_fp - zargs[0]))--;
+	else {
+		zword addr = h_globals + 2 * (zargs[0] - 16);
+		LOW_WORD(addr, value);
+		value--;
+		SET_WORD(addr, value);
+	}
+}
+
+void Processor::z_dec_chk() {
+	zword value;
+
+	if (zargs[0] == 0)
+		value = --(*_sp);
+	else if (zargs[0] < 16)
+		value = --(*(_fp - zargs[0]));
+	else {
+		zword addr = h_globals + 2 * (zargs[0] - 16);
+		LOW_WORD(addr, value);
+		value--;
+		SET_WORD(addr, value);
+	}
+
+	branch((short)value < (short)zargs[1]);
+}
+
+void Processor::z_inc() {
+	zword value;
+
+	if (zargs[0] == 0)
+		(*_sp)++;
+	else if (zargs[0] < 16)
+		(*(_fp - zargs[0]))++;
+	else {
+		zword addr = h_globals + 2 * (zargs[0] - 16);
+		LOW_WORD(addr, value);
+		value++;
+		SET_WORD(addr, value);
+	}
+}
+
+void Processor::z_inc_chk() {
+	zword value;
+
+	if (zargs[0] == 0)
+		value = ++(*_sp);
+	else if (zargs[0] < 16)
+		value = ++(*(_fp - zargs[0]));
+	else {
+		zword addr = h_globals + 2 * (zargs[0] - 16);
+		LOW_WORD(addr, value);
+		value++;
+		SET_WORD(addr, value);
+	}
+
+	branch((short)value > (short)zargs[1]);
+}
+
+void Processor::z_load() {
+	zword value;
+
+	if (zargs[0] == 0)
+		value = *_sp;
+	else if (zargs[0] < 16)
+		value = *(_fp - zargs[0]);
+	else {
+		zword addr = h_globals + 2 * (zargs[0] - 16);
+		LOW_WORD(addr, value);
+	}
+
+	store(value);
+}
+
+void Processor::z_pop() {
+	_sp++;
+}
+
+void Processor::z_pop_stack() {
+	if (zargc == 2) {
+		// it's a user stack
+		zword size;
+		zword addr = zargs[1];
+
+		LOW_WORD(addr, size);
+
+		size += zargs[0];
+		storew(addr, size);
+	} else {
+		// it's the game stack
+		_sp += zargs[0];
+	}
+}
+
+void Processor::z_pull() {
+	zword value;
+
+	if (h_version != V6) {
+		// not a V6 game, pop stack and write
+		value = *_sp++;
+
+		if (zargs[0] == 0)
+			*_sp = value;
+		else if (zargs[0] < 16)
+			*(_fp - zargs[0]) = value;
+		else {
+			zword addr = h_globals + 2 * (zargs[0] - 16);
+			SET_WORD(addr, value);
+		}
+	} else {
+		// it's V6, but is there a user stack?
+		if (zargc == 1) {
+			// it's a user stack
+			zword size;
+			zword addr = zargs[0];
+
+			LOW_WORD(addr, size);
+
+			size++;
+			storew(addr, size);
+
+			addr += 2 * size;
+			LOW_WORD(addr, value);
+		} else {
+			// it's the game stack
+			value = *_sp++;
+		}
+
+		store(value);
+	}
+}
+
+void Processor::z_push() {
+	*--_sp = zargs[0];
+}
+
+void Processor::z_push_stack() {
+	zword size;
+	zword addr = zargs[1];
+
+	LOW_WORD(addr, size);
+
+	if (size != 0) {
+		storew((zword)(addr + 2 * size), zargs[0]);
+
+		size--;
+		storew(addr, size);
+	}
+
+	branch(size);
+}
+
+void Processor::z_store() {
+	zword value = zargs[1];
+
+	if (zargs[0] == 0)
+		*_sp = value;
+	else if (zargs[0] < 16)
+		*(_fp - zargs[0]) = value;
+	else {
+		zword addr = h_globals + 2 * (zargs[0] - 16);
+		SET_WORD(addr, value);
+	}
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index 9dc7f97..a7a1198 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -72,7 +72,7 @@ public:
 	                                glui32 size, winid_t keyWin);
 	void glk_window_get_arrangement(winid_t win, glui32 *method,
 	                                glui32 *size, winid_t *keyWin);
-	winid_t glk_window_iterate(winid_t win, glui32 *rock);
+	winid_t glk_window_iterate(winid_t win, glui32 *rock = 0);
 	glui32 glk_window_get_rock(winid_t win);
 	glui32 glk_window_get_type(winid_t win);
 	winid_t glk_window_get_parent(winid_t win);
@@ -85,8 +85,8 @@ public:
 	strid_t glk_window_get_echo_stream(winid_t win);
 	void glk_set_window(winid_t win);
 
-	strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock);
-	strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock);
+	strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock = 0);
+	strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
 	void glk_stream_close(strid_t str, stream_result_t *result);
 	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const;
 	glui32 glk_stream_get_rock(strid_t str) const;
@@ -114,10 +114,10 @@ public:
 	glui32 glk_style_distinguish(winid_t win, glui32 style1, glui32 style2);
 	bool glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result);
 
-	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock);
-	frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock);
-	frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock);
-	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock);
+	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock = 0);
+	frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock = 0);
+	frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock = 0);
+	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock = 0);
 	void glk_fileref_destroy(frefid_t fref);
 	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
 	glui32 glk_fileref_get_rock(frefid_t fref);
@@ -169,8 +169,8 @@ public:
 	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
 	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
 
-	strid_t glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock);
-	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock);
+	strid_t glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock = 0);
+	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
 
 	void glk_request_char_event_uni(winid_t win);
 	void glk_request_line_event_uni(winid_t win, glui32 *buf,
@@ -206,7 +206,7 @@ public:
 
 #ifdef GLK_MODULE_SOUND
 
-	schanid_t glk_schannel_create(glui32 rock);
+	schanid_t glk_schannel_create(glui32 rock = 0);
 	void glk_schannel_destroy(schanid_t chan);
 	schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
 	glui32 glk_schannel_get_rock(schanid_t chan);
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index d359429..9a1b4c4 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -26,7 +26,17 @@ MODULE_OBJS := \
 	frotz/detection_tables.o \
 	frotz/err.o \
 	frotz/frotz.o \
+	frotz/glk_interface.o \
 	frotz/mem.o \
+	frotz/processor.o \
+	frotz/processor_input.o \
+	frotz/processor_maths.o \
+	frotz/processor_objects.o \
+	frotz/processor_screen.o \
+	frotz/processor_streams.o \
+	frotz/processor_table.o \
+	frotz/processor_text.o \
+	frotz/processor_variables.o \
 	scott/detection.o \
 	scott/scott.o
 


Commit: 9fadd84b37ff4e9fa76cb2efc3464d5b958a7ed3
    https://github.com/scummvm/scummvm/commit/9fadd84b37ff4e9fa76cb2efc3464d5b958a7ed3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Add GlkInterface initialize method

Changed paths:
    engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/frotz/glk_interface.cpp
    engines/gargoyle/frotz/glk_interface.h
    engines/gargoyle/frotz/mem.h
    engines/gargoyle/frotz/processor.cpp
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_input.cpp


diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index c4f4298..302cc43 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -234,13 +234,14 @@ struct UserOptions {
 	bool _save_quetzal;
 	int _err_report_mode;
 	bool _sound;
+	bool _user_tandy_bit;
 
 	UserOptions() : _attribute_assignment(0), _attribute_testing(0),
 		_context_lines(0), _object_locating(0), _object_movement(0),
 		_left_margin(0), _right_margin(0), _ignore_errors(false), _piracy(false),
 		_undo_slots(MAX_UNDO_SLOTS), _expand_abbreviations(0), _script_cols(80),
-		_save_quetzal(true),
-		_err_report_mode(ERR_DEFAULT_REPORT_MODE), _sound(true) {
+		_save_quetzal(true), _err_report_mode(ERR_DEFAULT_REPORT_MODE), _sound(true),
+		_user_tandy_bit(false) {
 	}
 };
 
diff --git a/engines/gargoyle/frotz/glk_interface.cpp b/engines/gargoyle/frotz/glk_interface.cpp
index e1311fb..f60ecc5 100644
--- a/engines/gargoyle/frotz/glk_interface.cpp
+++ b/engines/gargoyle/frotz/glk_interface.cpp
@@ -26,7 +26,7 @@ namespace Gargoyle {
 namespace Frotz {
 
 GlkInterface::GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-		Glk(syst, gameDesc),
+		Glk(syst, gameDesc), UserOptions(),
 		oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
 		curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
 		curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
@@ -40,6 +40,135 @@ GlkInterface::GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDes
 	Common::fill(&statusline[0], &statusline[256], '\0');
 }
 
+void GlkInterface::initialize() {
+	uint width, height;
+
+	/*
+	 * Init glk stuff
+	 */
+
+	// monor
+	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Weight, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Oblique, 0);
+
+	// monob
+	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Weight, 1);
+	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Oblique, 0);
+
+	// monoi
+	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Weight, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Oblique, 1);
+
+	// monoz
+	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Weight, 1);
+	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Oblique, 1);
+
+	// propr
+	glk_stylehint_set(wintype_TextBuffer, style_Normal,       stylehint_Proportional, 1);
+	glk_stylehint_set(wintype_TextGrid,   style_Normal,       stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Normal,       stylehint_Weight, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Normal,       stylehint_Oblique, 0);
+
+	// propb
+	glk_stylehint_set(wintype_TextBuffer, style_Header,       stylehint_Proportional, 1);
+	glk_stylehint_set(wintype_TextGrid,   style_Header,       stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Header,       stylehint_Weight, 1);
+	glk_stylehint_set(wintype_AllTypes,   style_Header,       stylehint_Oblique, 0);
+
+	// propi
+	glk_stylehint_set(wintype_TextBuffer, style_Emphasized,   stylehint_Proportional, 1);
+	glk_stylehint_set(wintype_TextGrid,   style_Emphasized,   stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Emphasized,   stylehint_Weight, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Emphasized,   stylehint_Oblique, 1);
+
+	// propi
+	glk_stylehint_set(wintype_TextBuffer, style_Note,         stylehint_Proportional, 1);
+	glk_stylehint_set(wintype_TextGrid,   style_Note,         stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Weight, 1);
+	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Oblique, 1);
+
+	gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
+	if (!gos_lower)
+		gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+	glk_window_get_size(gos_lower, &width, &height);
+	glk_window_close(gos_lower, NULL);
+
+	gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+	gos_upper = glk_window_open(gos_lower,
+			winmethod_Above | winmethod_Fixed,
+			0,
+			wintype_TextGrid, 0);
+
+	gos_channel = NULL;
+
+	glk_set_window(gos_lower);
+	gos_curwin = gos_lower;
+
+	/*
+	 * Icky magic bit setting
+	 */
+
+	if (h_version == V3 && _user_tandy_bit)
+		h_config |= CONFIG_TANDY;
+
+	if (h_version == V3 && gos_upper)
+		h_config |= CONFIG_SPLITSCREEN;
+
+	if (h_version == V3 && !gos_upper)
+		h_config |= CONFIG_NOSTATUSLINE;
+
+	if (h_version >= V4)
+		h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
+			CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
+
+	if (h_version >= V5)
+		h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
+
+	if ((h_version >= 5) && (h_flags & SOUND_FLAG))
+		h_flags |= SOUND_FLAG;
+
+	if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
+		h_flags |= OLD_SOUND_FLAG;
+
+	if ((h_version == 6) && (_sound != 0)) 
+		h_config |= CONFIG_SOUND;
+
+	if (h_version >= V5 && (h_flags & UNDO_FLAG))
+		if (_undo_slots == 0)
+			h_flags &= ~UNDO_FLAG;
+
+	h_screen_cols = width;
+	h_screen_rows = height;
+
+	h_screen_height = h_screen_rows;
+	h_screen_width = h_screen_cols;
+
+	h_font_width = 1;
+	h_font_height = 1;
+
+	/* Must be after screen dimensions are computed.  */
+	if (h_version == V6) {
+		h_flags &= ~GRAPHICS_FLAG;
+	}
+
+	// Use the ms-dos interpreter number for v6, because that's the
+	// kind of graphics files we understand.  Otherwise, use DEC.
+	h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
+	h_interpreter_version = 'F';
+
+	{
+		// Set these per spec 8.3.2.
+		h_default_foreground = WHITE_COLOUR;
+		h_default_background = BLACK_COLOUR;
+		if (h_flags & COLOUR_FLAG)
+			h_flags &= ~COLOUR_FLAG;
+	}
+}
+
 int GlkInterface::os_char_width(zchar z) {
 	return 1;
 }
@@ -118,7 +247,7 @@ void GlkInterface::gos_update_width() {
 		glk_window_get_size(gos_upper, &width, nullptr);
 		h_screen_cols = width;
 		SET_BYTE(H_SCREEN_COLS, width);
-		if (curx > width) {
+		if ((uint)curx > width) {
 			glk_window_move_cursor(gos_upper, 0, cury - 1);
 			curx = 1;
 		}
@@ -140,7 +269,7 @@ void GlkInterface::reset_status_ht() {
 	glui32 height;
 	if (gos_upper) {
 		glk_window_get_size(gos_upper, nullptr, &height);
-		if (mach_status_ht != height) {
+		if ((uint)mach_status_ht != height) {
 			glk_window_set_arrangement(
 				glk_window_get_parent(gos_upper),
 				winmethod_Above | winmethod_Fixed,
diff --git a/engines/gargoyle/frotz/glk_interface.h b/engines/gargoyle/frotz/glk_interface.h
index ea3f60c..1d30364 100644
--- a/engines/gargoyle/frotz/glk_interface.h
+++ b/engines/gargoyle/frotz/glk_interface.h
@@ -40,7 +40,7 @@ enum SoundEffect {
  * Implements an intermediate interface on top of the GLK layer, providing screen
  * and sound effect handling
  */
-class GlkInterface : public Glk, public virtual Mem {
+class GlkInterface : public Glk, public UserOptions, public virtual Mem {
 public:
 	zchar statusline[256];
 	int oldstyle;
@@ -139,6 +139,11 @@ public:
 	 * Constructor
 	 */
 	GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc);
+
+	/**
+	 * Initialization
+	 */
+	void initialize();
 };
 
 } // End of namespace Frotz
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index d37cdd3..55cc4dc 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -186,7 +186,7 @@ public:
 	/**
 	 * Initialize
 	 */
-	virtual void initialize();
+	void initialize();
 
 	/**
 	 * Read a word
diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
index 1a31898..20fb387 100644
--- a/engines/gargoyle/frotz/processor.cpp
+++ b/engines/gargoyle/frotz/processor.cpp
@@ -132,7 +132,7 @@ Opcode Processor::ext_opcodes[64] = {
 };
 
 Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
-		GlkInterface(syst, gameDesc), Mem(), Errors(), UserOptions(),
+		GlkInterface(syst, gameDesc), Mem(), Errors(),
 		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
 		_randomInterval(0), _randomCtr(0), first_restart(true) {
@@ -181,6 +181,7 @@ Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 
 void Processor::initialize() {
 	Mem::initialize();
+	GlkInterface::initialize();
 
 	if (h_version <= V4) {
 		op0_opcodes[9] = &Processor::z_pop;
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index 074100a..a11fa1b 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -48,7 +48,7 @@ typedef void (Processor::*Opcode)();
 /**
  * Zcode processor
  */
-class Processor : public virtual Mem, public Errors, public GlkInterface, public UserOptions {
+class Processor : public virtual Mem, public Errors, public GlkInterface {
 private:
 	Opcode op0_opcodes[16];
 	Opcode op1_opcodes[16];
@@ -1308,7 +1308,7 @@ public:
 	/**
 	 * Initialization
 	 */
-	virtual void initialize() override;
+	void initialize();
 
 	/**
 	 * Z-code interpreter main loop
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
index e7f92ab..86a8abc 100644
--- a/engines/gargoyle/frotz/processor_input.cpp
+++ b/engines/gargoyle/frotz/processor_input.cpp
@@ -26,8 +26,6 @@ namespace Gargoyle {
 namespace Frotz {
 
 // TODO: Implement method stubs
-static zchar stream_read_key(zword, zword, bool) { return 0; }
-static zchar stream_read_input(int, zchar *, zword, zword, bool, bool) { return 0;}
 static void storeb(zword, zchar) {}
 static void storew(zword, zword) {}
 static void save_undo() {}


Commit: ad95130e4f3ad9b0eb0f1ab976dd157a0e7a2dbf
    https://github.com/scummvm/scummvm/commit/ad95130e4f3ad9b0eb0f1ab976dd157a0e7a2dbf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added user options initialization from configuration

Changed paths:
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/frotz.h


diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index c0a9442..df6d736 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -22,6 +22,7 @@
 
 #include "gargoyle/frotz/frotz.h"
 #include "gargoyle/frotz/frotz_types.h"
+#include "common/config-manager.h"
 
 namespace Gargoyle {
 namespace Frotz {
@@ -40,11 +41,48 @@ void Frotz::runGame(Common::SeekableReadStream *gameFile) {
 	// TODO
 }
 
+void Frotz::initialize() {
+	if (ConfMan.hasKey("attribute_assignment") && ConfMan.getBool("attribute_assignment"))
+		_attribute_assignment = true;
+	if (ConfMan.hasKey("attribute_testing") && ConfMan.getBool("attribute_testing"))
+		_attribute_testing = true;
+	if (ConfMan.hasKey("ignore_errors") && ConfMan.getBool("ignore_errors"))
+		_ignore_errors = true;
+	if (ConfMan.hasKey("object_movement") && ConfMan.getBool("object_movement"))
+		_object_movement = true;
+	if (ConfMan.hasKey("object_locating") && ConfMan.getBool("object_locating"))
+		_object_locating = true;
+	if (ConfMan.hasKey("piracy") && ConfMan.getBool("piracy"))
+		_piracy = true;
+	if (ConfMan.hasKey("save_quetzal") && ConfMan.getBool("save_quetzal"))
+		_save_quetzal = true;
+	if (ConfMan.hasKey("random_seed"))
+		_random.setSeed(ConfMan.getInt("random_seed"));
+	if (ConfMan.hasKey("script_cols"))
+		_script_cols = ConfMan.getInt("script_cols");
+	if (ConfMan.hasKey("tandy_bit") && ConfMan.getBool("tandy_bit"))
+		_user_tandy_bit = true;
+	if (ConfMan.hasKey("undo_slots"))
+		_undo_slots = ConfMan.getInt("undo_slots");
+	if (ConfMan.hasKey("expand_abbreviations") && ConfMan.getBool("expand_abbreviations"))
+		_expand_abbreviations = true;
+	if (ConfMan.hasKey("err_report_mode")) {
+		_err_report_mode = ConfMan.getInt("err_report_mode");
+		if ((_err_report_mode < ERR_REPORT_NEVER) || (_err_report_mode > ERR_REPORT_FATAL))
+			_err_report_mode = ERR_DEFAULT_REPORT_MODE;
+	}
+
+	// Call process initialization
+	Processor::initialize();
+}
+
 Common::Error Frotz::loadGameState(int slot) {
+	// TODO
 	return Common::kNoError;
 }
 
 Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
+	// TODO
 	return Common::kNoError;
 }
 
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index c032265..78272f0 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -39,6 +39,11 @@ public:
 	Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc);
 
 	/**
+	 * Initialization
+	 */
+	void initialize();
+
+	/**
 	 * Execute the game
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) override;


Commit: 7a3dd94de1e5343f00316d69674e46d129f56914
    https://github.com/scummvm/scummvm/commit/7a3dd94de1e5343f00316d69674e46d129f56914
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Add undo data initialization

Changed paths:
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/glk_interface.cpp
    engines/gargoyle/frotz/glk_interface.h
    engines/gargoyle/frotz/mem.cpp
    engines/gargoyle/frotz/mem.h
    engines/gargoyle/frotz/processor.cpp
    engines/gargoyle/frotz/processor.h


diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index df6d736..5c86189 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -74,6 +74,9 @@ void Frotz::initialize() {
 
 	// Call process initialization
 	Processor::initialize();
+
+	// Restart the game
+	z_restart();
 }
 
 Common::Error Frotz::loadGameState(int slot) {
diff --git a/engines/gargoyle/frotz/glk_interface.cpp b/engines/gargoyle/frotz/glk_interface.cpp
index f60ecc5..13d0a56 100644
--- a/engines/gargoyle/frotz/glk_interface.cpp
+++ b/engines/gargoyle/frotz/glk_interface.cpp
@@ -26,7 +26,7 @@ namespace Gargoyle {
 namespace Frotz {
 
 GlkInterface::GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-		Glk(syst, gameDesc), UserOptions(),
+		Glk(syst, gameDesc),
 		oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
 		curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
 		curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
diff --git a/engines/gargoyle/frotz/glk_interface.h b/engines/gargoyle/frotz/glk_interface.h
index 1d30364..f02fa0d 100644
--- a/engines/gargoyle/frotz/glk_interface.h
+++ b/engines/gargoyle/frotz/glk_interface.h
@@ -40,7 +40,7 @@ enum SoundEffect {
  * Implements an intermediate interface on top of the GLK layer, providing screen
  * and sound effect handling
  */
-class GlkInterface : public Glk, public UserOptions, public virtual Mem {
+class GlkInterface : public Glk, public virtual UserOptions, public virtual Mem {
 public:
 	zchar statusline[256];
 	int oldstyle;
diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
index cb5a73b..d9ff53d 100644
--- a/engines/gargoyle/frotz/mem.cpp
+++ b/engines/gargoyle/frotz/mem.cpp
@@ -112,11 +112,15 @@ void Header::loadHeader(Common::SeekableReadStream &f) {
 
 /*--------------------------------------------------------------------------*/
 
-Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0), story_size(0) {
+Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0), story_size(0),
+		first_undo(nullptr), last_undo(nullptr), curr_undo(nullptr),
+		undo_mem(nullptr), prev_zmp(nullptr), undo_diff(nullptr),
+		undo_count(0), reserve_mem(0) {
 }
 
 void Mem::initialize() {
 	initializeStoryFile();
+	initializeUndo();
 	loadGameHeader();
 
 	// Allocate memory for story data
@@ -173,6 +177,29 @@ void Mem::initializeStoryFile() {
 		error("This file is too small to be a Z-code file.");
 }
 
+void Mem::initializeUndo() {
+	void *reserved = nullptr;
+
+	if (reserve_mem != 0) {
+		if ((reserved = malloc(reserve_mem)) == NULL)
+			return;
+	}
+
+	// Allocate h_dynamic_size bytes for previous dynamic zmp state
+	// + 1.5 h_dynamic_size for Quetzal diff + 2.
+	undo_mem = new zbyte[(h_dynamic_size * 5) / 2 + 2];
+	if (undo_mem != nullptr) {
+		prev_zmp = undo_mem;
+		undo_diff = undo_mem + h_dynamic_size;
+		memcpy(prev_zmp, zmp, h_dynamic_size);
+	} else {
+		_undo_slots = 0;
+	}
+
+	if (reserve_mem != 0)
+		delete reserved;
+}
+
 void Mem::loadGameHeader() {
 	// Load header
 	zmp = new byte[64];
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index 55cc4dc..e12c062 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -82,6 +82,21 @@ enum {
 };
 
 /**
+ * Stores undo information
+ */
+struct undo_struct {
+	undo_struct *next;
+	undo_struct *prev;
+	long pc;
+	long diff_size;
+	zword frame_count;
+	zword stack_size;
+	zword frame_offset;
+	// undo diff and stack data follow
+};
+typedef undo_struct undo_t;
+
+/**
  * Story file header data
  */
 struct Header {
@@ -160,13 +175,18 @@ public:
 	void loadHeader(Common::SeekableReadStream &f);
 };
 
-class Mem : public Header {
+class Mem : public Header, public virtual UserOptions {
 protected:
 	Common::SeekableReadStream *story_fp;
 	uint blorb_ofs, blorb_len;
 	uint story_size;
 	byte *pcp;
 	byte *zmp;
+
+	undo_t *first_undo, *last_undo, *curr_undo;
+	zbyte *undo_mem, *prev_zmp, *undo_diff;
+	int undo_count;
+	int reserve_mem;
 private:
 	/**
 	 * Handles setting the story file, parsing it if it's a Blorb file
@@ -174,6 +194,11 @@ private:
 	void initializeStoryFile();
 
 	/**
+	 * Setup undo data
+	 */
+	void initializeUndo();
+
+	/**
 	 * Handles loading the game header
 	 */
 	void loadGameHeader();
diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
index 20fb387..721f64b 100644
--- a/engines/gargoyle/frotz/processor.cpp
+++ b/engines/gargoyle/frotz/processor.cpp
@@ -132,7 +132,7 @@ Opcode Processor::ext_opcodes[64] = {
 };
 
 Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
-		GlkInterface(syst, gameDesc), Mem(), Errors(),
+		GlkInterface(syst, gameDesc), Errors(),
 		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
 		_randomInterval(0), _randomCtr(0), first_restart(true) {
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index a11fa1b..77192b4 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -412,7 +412,7 @@ private:
 	zchar unicode_tolower(zchar c);
 
 	/**@}*/
-private:
+protected:
 	/**
 	 * \defgroup General Opcode methods
 	 * @{


Commit: fbe7fb7e1a6223070bd0d0c9929b372391756c8c
    https://github.com/scummvm/scummvm/commit/fbe7fb7e1a6223070bd0d0c9929b372391756c8c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Merge Buffer class into Processor

Changed paths:
  A engines/gargoyle/frotz/processor_buffer.cpp
  R engines/gargoyle/frotz/buffer.cpp
  R engines/gargoyle/frotz/buffer.h
    engines/gargoyle/frotz/processor.cpp
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_streams.cpp
    engines/gargoyle/frotz/processor_text.cpp
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/frotz/buffer.cpp b/engines/gargoyle/frotz/buffer.cpp
deleted file mode 100644
index 7d9e644..0000000
--- a/engines/gargoyle/frotz/buffer.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/buffer.h"
-#include "gargoyle/frotz/frotz.h"
-#include "common/algorithm.h"
-#include "common/textconsole.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-// TODO: Replace these method stubs with correct calls
-void stream_char(zchar) {}
-void stream_word(const zchar *) {}
-void stream_new_line(void) {}
-
-Buffer::Buffer() : _bufPos(0), _locked(false), _prevC('\0') {
-	Common::fill(&_buffer[0], &_buffer[TEXT_BUFFER_SIZE], '\0');
-}
-
-void Buffer::flush() {
-	/* Make sure we stop when flush_buffer is called from flush_buffer.
-	 * Note that this is difficult to avoid as we might print a newline
-	 * during flush_buffer, which might cause a newline interrupt, that
-	 * might execute any arbitrary opcode, which might flush the buffer.
-	 */
-	if (_locked || empty())
-		return;
-
-	// Send the buffer to the output streams
-	_buffer[_bufPos] = '\0';
-	
-	_locked = true;
-	stream_word(_buffer);
-	_locked = false;
-
-	// Reset the buffer
-	_bufPos = 0;
-	_prevC = '\0';
-}
-
-void Buffer::printChar(zchar c) {
-	static bool flag = false;
-
-	if (g_vm->message || g_vm->ostream_memory || g_vm->enable_buffering) {
-		if (!flag) {
-			// Characters 0 and ZC_RETURN are special cases
-			if (c == ZC_RETURN) {
-				newLine();
-				return;
-			}
-			if (c == 0)
-				return;
-
-			// Flush the buffer before a whitespace or after a hyphen
-			if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (_prevC == '-' && c != '-'))
-				flush();
-
-			// Set the flag if this is part one of a style or font change
-			if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
-				flag = true;
-
-			// Remember the current character code
-			_prevC = c;
-		} else {
-			flag = false;
-		}
-
-		// Insert the character into the buffer
-		_buffer[_bufPos++] = c;
-
-		if (_bufPos == TEXT_BUFFER_SIZE)
-			error("Text buffer overflow");
-	} else {
-		stream_char(c);
-	}
-}
-
-void Buffer::newLine()  {
-	flush();
-	stream_new_line();
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/buffer.h b/engines/gargoyle/frotz/buffer.h
deleted file mode 100644
index 608e087..0000000
--- a/engines/gargoyle/frotz/buffer.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FROTZ_BUFFER
-#define GARGOYLE_FROTZ_BUFFER
-
-#include "gargoyle/frotz/frotz_types.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-#define TEXT_BUFFER_SIZE 200
-
-/**
- * Text buffer class
- */
-class Buffer {
-public:
-	zchar _buffer[TEXT_BUFFER_SIZE];
-	size_t _bufPos;
-	bool _locked;
-	zchar _prevC;
-public:
-	/**
-	 * Constructor
-	 */
-	Buffer();
-
-	/**
-	 * Copy the contents of the text buffer to the output streams.
-	 */
-	void flush();
-
-	 /**
-	  * High level output function.
-	  */
-	void printChar(zchar c);
-
-	 /**
-	  * High level newline function.
-	  */
-	void newLine();
-
-	/**
-	 * Returns true if the buffer is empty
-	 */
-	bool empty() const { return !_bufPos; }
-};
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
index 721f64b..85a539b 100644
--- a/engines/gargoyle/frotz/processor.cpp
+++ b/engines/gargoyle/frotz/processor.cpp
@@ -135,7 +135,8 @@ Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 		GlkInterface(syst, gameDesc), Errors(),
 		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
-		_randomInterval(0), _randomCtr(0), first_restart(true) {
+		_randomInterval(0), _randomCtr(0), first_restart(true), _bufPos(0),
+		_locked(false), _prevC('\0') {
 	static const Opcode OP0_OPCODES[16] = {
 		&Processor::z_rtrue,
 		&Processor::z_rfalse,
@@ -177,6 +178,7 @@ Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 	Common::copy(&OP1_OPCODES[0], &OP1_OPCODES[16], op1_opcodes);
 	Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
 	Common::fill(&zargs[0], &zargs[8], 0);
+	Common::fill(&_buffer[0], &_buffer[TEXT_BUFFER_SIZE], '\0');
 }
 
 void Processor::initialize() {
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index 77192b4..d754b1f 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -23,7 +23,6 @@
 #ifndef GARGOYLE_FROTZ_PROCESSOR
 #define GARGOYLE_FROTZ_PROCESSOR
 
-#include "gargoyle/frotz/buffer.h"
 #include "gargoyle/frotz/err.h"
 #include "gargoyle/frotz/mem.h"
 #include "gargoyle/frotz/glk_interface.h"
@@ -37,6 +36,7 @@ namespace Frotz {
 #define GET_PC(v)          v = pcp - zmp
 #define SET_PC(v)          pcp = zmp + v
 
+#define TEXT_BUFFER_SIZE 200
 
 enum string_type {
 	LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY
@@ -48,12 +48,8 @@ typedef void (Processor::*Opcode)();
 /**
  * Zcode processor
  */
-class Processor : public virtual Mem, public Errors, public GlkInterface {
+class Processor : public Errors, public GlkInterface, public virtual Mem {
 private:
-	Opcode op0_opcodes[16];
-	Opcode op1_opcodes[16];
-	static Opcode var_opcodes[64];
-	static Opcode ext_opcodes[64];
 	int _finished;
 	zword zargs[8];
 	int zargc;
@@ -71,6 +67,17 @@ private:
 	static zchar ZSCII_TO_LATIN1[];
 	zchar *_decoded, *_encoded;
 	int _resolution;
+
+	// Buffer related fields
+	zchar _buffer[TEXT_BUFFER_SIZE];
+	size_t _bufPos;
+	bool _locked;
+	zchar _prevC;
+
+	Opcode op0_opcodes[16];
+	Opcode op1_opcodes[16];
+	static Opcode var_opcodes[64];
+	static Opcode ext_opcodes[64];
 private:
 	/**
 	 * \defgroup General support methods
@@ -146,6 +153,33 @@ private:
 	 */
 
 	/**
+	 * Copy the contents of the text buffer to the output streams.
+	 */
+	void flush_buffer();
+
+	 /**
+	  * High level output function.
+	  */
+	void print_char(zchar c);
+
+	 /**
+	  * High level newline function.
+	  */
+	void new_line();
+
+	/**
+	 * Returns true if the buffer is empty
+	 */
+	bool bufferEmpty() const { return !_bufPos; }
+
+	/**@}*/
+
+	/**
+	 * \defgroup Input support methods
+	 * @{
+	 */
+
+	/**
 	 * Check if the given key is an input terminator.
 	 */
 	bool is_terminator(zchar key);
diff --git a/engines/gargoyle/frotz/processor_buffer.cpp b/engines/gargoyle/frotz/processor_buffer.cpp
new file mode 100644
index 0000000..be30bfe
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_buffer.cpp
@@ -0,0 +1,99 @@
+/* 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 "gargoyle/frotz/processor.h"
+#include "common/algorithm.h"
+#include "common/textconsole.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+// TODO: Replace these method stubs with correct calls
+void stream_char(zchar) {}
+void stream_word(const zchar *) {}
+void stream_new_line(void) {}
+
+void Processor::flush_buffer() {
+	/* Make sure we stop when flush_buffer is called from flush_buffer.
+	 * Note that this is difficult to avoid as we might print a newline
+	 * during flush_buffer, which might cause a newline interrupt, that
+	 * might execute any arbitrary opcode, which might flush the buffer.
+	 */
+	if (_locked || bufferEmpty())
+		return;
+
+	// Send the buffer to the output streams
+	_buffer[_bufPos] = '\0';
+	
+	_locked = true;
+	stream_word(_buffer);
+	_locked = false;
+
+	// Reset the buffer
+	_bufPos = 0;
+	_prevC = '\0';
+}
+
+void Processor::print_char(zchar c) {
+	static bool flag = false;
+
+	if (message || ostream_memory || enable_buffering) {
+		if (!flag) {
+			// Characters 0 and ZC_RETURN are special cases
+			if (c == ZC_RETURN) {
+				new_line();
+				return;
+			}
+			if (c == 0)
+				return;
+
+			// Flush the buffer before a whitespace or after a hyphen
+			if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (_prevC == '-' && c != '-'))
+				flush_buffer();
+
+			// Set the flag if this is part one of a style or font change
+			if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
+				flag = true;
+
+			// Remember the current character code
+			_prevC = c;
+		} else {
+			flag = false;
+		}
+
+		// Insert the character into the buffer
+		_buffer[_bufPos++] = c;
+
+		if (_bufPos == TEXT_BUFFER_SIZE)
+			error("Text buffer overflow");
+	} else {
+		stream_char(c);
+	}
+}
+
+void Processor::new_line()  {
+	flush_buffer();
+	stream_new_line();
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
index 5fe526f..ae4dbaa 100644
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -28,7 +28,6 @@ namespace Frotz {
 // TODO: Implement method stubs
 static void os_scrollback_char(zchar) {}
 static void os_scrollback_erase(zword) {}
-static void flush_buffer() {}
 static void script_open() {}
 static void script_close() {}
 static void script_mssg_on() {}
diff --git a/engines/gargoyle/frotz/processor_text.cpp b/engines/gargoyle/frotz/processor_text.cpp
index e660ac1..f6a82fb 100644
--- a/engines/gargoyle/frotz/processor_text.cpp
+++ b/engines/gargoyle/frotz/processor_text.cpp
@@ -26,8 +26,6 @@ namespace Gargoyle {
 namespace Frotz {
 
 // TODO: Replace method stubs
-static void new_line() {}
-static void print_char(zchar) {}
 static void storeb(zword, zchar) {}
 static void storew(zword, zword) {}
 
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 9a1b4c4..78bde80 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -21,7 +21,6 @@ MODULE_OBJS := \
 	window_pair.o \
 	window_text_buffer.o \
 	window_text_grid.o \
-	frotz/buffer.o \
 	frotz/detection.o \
 	frotz/detection_tables.o \
 	frotz/err.o \
@@ -29,6 +28,7 @@ MODULE_OBJS := \
 	frotz/glk_interface.o \
 	frotz/mem.o \
 	frotz/processor.o \
+	frotz/processor_buffer.o \
 	frotz/processor_input.o \
 	frotz/processor_maths.o \
 	frotz/processor_objects.o \


Commit: 53612fa6f72cc2167f9b5a65bc09eb351248b718
    https://github.com/scummvm/scummvm/commit/53612fa6f72cc2167f9b5a65bc09eb351248b718
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added storeb and storew methods

Changed paths:
    engines/gargoyle/frotz/processor.cpp
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_input.cpp
    engines/gargoyle/frotz/processor_streams.cpp
    engines/gargoyle/frotz/processor_table.cpp
    engines/gargoyle/frotz/processor_text.cpp


diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
index 85a539b..9341013 100644
--- a/engines/gargoyle/frotz/processor.cpp
+++ b/engines/gargoyle/frotz/processor.cpp
@@ -135,8 +135,8 @@ Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 		GlkInterface(syst, gameDesc), Errors(),
 		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
-		_randomInterval(0), _randomCtr(0), first_restart(true), _bufPos(0),
-		_locked(false), _prevC('\0') {
+		_randomInterval(0), _randomCtr(0), first_restart(true), script_valid(false),
+		_bufPos(0), _locked(false), _prevC('\0') {
 	static const Opcode OP0_OPCODES[16] = {
 		&Processor::z_rtrue,
 		&Processor::z_rfalse,
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index d754b1f..1554265 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -56,6 +56,7 @@ private:
 	uint _randomInterval;
 	uint _randomCtr;
 	bool first_restart;
+	bool script_valid;
 
 	// Stack data
 	zword _stack[STACK_SIZE];
@@ -202,6 +203,21 @@ private:
 	/**@}*/
 
 	/**
+	 * \defgroup Memory support methods
+	 * @{
+	 */
+
+	/**
+	 * Write a byte value to the dynamic Z-machine memory.
+	 */
+	void storeb(zword addr, zbyte value);
+
+	/**
+	 * Write a word value to the dynamic Z-machine memory.
+	 */
+	void storew(zword addr, zword value);
+
+	/**
 	 * \defgroup Object support methods
 	 * @{
 	 */
@@ -308,6 +324,26 @@ private:
 	zchar stream_read_input(int max, zchar *buf, zword timeout, zword routine,
 		bool hot_keys, bool no_scripting);
 
+	/*
+	 * script_open
+	 *
+	 * Open the transscript file. 'AMFV' makes this more complicated as it
+	 * turns transscription on/off several times to exclude some text from
+	 * the transscription file. This wasn't a problem for the original V4
+	 * interpreters which always sent transscription to the printer, but it
+	 * means a problem to modern interpreters that offer to open a new file
+	 * every time transscription is turned on. Our solution is to append to
+	 * the old transscription file in V1 to V4, and to ask for a new file
+	 * name in V5+.
+	 *
+	 */
+	void script_open();
+
+	/*
+	 * Stop transscription.
+	 */
+	void script_close();
+
 	 /**@}*/
 
 	/**
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
index 86a8abc..43f0aaf 100644
--- a/engines/gargoyle/frotz/processor_input.cpp
+++ b/engines/gargoyle/frotz/processor_input.cpp
@@ -26,8 +26,6 @@ namespace Gargoyle {
 namespace Frotz {
 
 // TODO: Implement method stubs
-static void storeb(zword, zchar) {}
-static void storew(zword, zword) {}
 static void save_undo() {}
 static zword os_read_mouse() { return 0; }
 
@@ -80,7 +78,35 @@ int Processor::read_number() {
 	    value = 10 * value + buffer[i] - '0';
 
     return value;
+}
+
+void Processor::storeb(zword addr, zbyte value) {
+	if (addr >= h_dynamic_size)
+		runtimeError(ERR_STORE_RANGE);
+
+	if (addr == H_FLAGS + 1) {	/* flags register is modified */
+
+		h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
+		h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
+
+		if (value & SCRIPTING_FLAG) {
+			if (!ostream_script)
+				script_open();
+		} else {
+			if (ostream_script)
+				script_close();
+		}
+
+		// TOR - glkified / refresh_text_style ();
+	}
+
+	SET_BYTE(addr, value);
+
+}
 
+void Processor::storew(zword addr, zword value) {
+	storeb((zword)(addr + 0), hi(value));
+	storeb((zword)(addr + 1), lo(value));
 }
 
 void Processor::z_read() {
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
index ae4dbaa..a536095 100644
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -222,6 +222,14 @@ continue_input:
     return key;
 }
 
+void Processor::script_open() {
+	// TODO
+}
+
+void Processor::script_close() {
+	// TODO
+}
+
 void Processor::z_input_stream() {
 	flush_buffer();
 
diff --git a/engines/gargoyle/frotz/processor_table.cpp b/engines/gargoyle/frotz/processor_table.cpp
index 4e660ca..d449062 100644
--- a/engines/gargoyle/frotz/processor_table.cpp
+++ b/engines/gargoyle/frotz/processor_table.cpp
@@ -25,11 +25,6 @@
 namespace Gargoyle {
 namespace Frotz {
 
-// TODO: Method stubs to implement
-static void storeb(zword, zbyte) {}
-static void storew(zword, zword) {}
-
-
 void Processor::z_copy_table() {
     zword addr;
     zword size = zargs[2];
diff --git a/engines/gargoyle/frotz/processor_text.cpp b/engines/gargoyle/frotz/processor_text.cpp
index f6a82fb..630bd50 100644
--- a/engines/gargoyle/frotz/processor_text.cpp
+++ b/engines/gargoyle/frotz/processor_text.cpp
@@ -25,11 +25,6 @@
 namespace Gargoyle {
 namespace Frotz {
 
-// TODO: Replace method stubs
-static void storeb(zword, zchar) {}
-static void storew(zword, zword) {}
-
-
 zchar Processor::ZSCII_TO_LATIN1[] = {
     0x0e4, 0x0f6, 0x0fc, 0x0c4, 0x0d6, 0x0dc, 0x0df, 0x0bb,
     0x0ab, 0x0eb, 0x0ef, 0x0ff, 0x0cb, 0x0cf, 0x0e1, 0x0e9,


Commit: 1b4791f92328e4dc28e697cd9f12383c7f10b932
    https://github.com/scummvm/scummvm/commit/1b4791f92328e4dc28e697cd9f12383c7f10b932
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Removal of method stubs that are now implemented

Changed paths:
    engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/frotz/glk_interface.h
    engines/gargoyle/frotz/processor_objects.cpp
    engines/gargoyle/frotz/processor_screen.cpp
    engines/gargoyle/frotz/processor_streams.cpp
    engines/gargoyle/frotz/processor_variables.cpp


diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index 302cc43..2b33fbd 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -199,12 +199,6 @@ enum FontStyle {
 #define BEEP_HIGH       1
 #define BEEP_LOW        2
 
-/*** Constants for os_restart_game */
-
-#define RESTART_BEGIN 0
-#define RESTART_WPROP_SET 1
-#define RESTART_END 2
-
 /*** Constants for os_menu */
 
 #define MENU_NEW 0
diff --git a/engines/gargoyle/frotz/glk_interface.h b/engines/gargoyle/frotz/glk_interface.h
index f02fa0d..161a1f3 100644
--- a/engines/gargoyle/frotz/glk_interface.h
+++ b/engines/gargoyle/frotz/glk_interface.h
@@ -36,6 +36,13 @@ enum SoundEffect {
 	EFFECT_FINISH_WITH = 4
 };
 
+enum RestartAction {
+	RESTART_BEGIN = 0,
+	RESTART_WPROP_SET = 1,
+	RESTART_END = 2
+};
+
+
 /**
  * Implements an intermediate interface on top of the GLK layer, providing screen
  * and sound effect handling
@@ -134,6 +141,11 @@ protected:
 	void packspaces(zchar *src, zchar *dst);
 
 	void smartstatusline();
+
+	/**
+	 * Called during game restarts
+	 */
+	void os_restart_game(RestartAction) {}
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/frotz/processor_objects.cpp b/engines/gargoyle/frotz/processor_objects.cpp
index 3d2b171..986bf87 100644
--- a/engines/gargoyle/frotz/processor_objects.cpp
+++ b/engines/gargoyle/frotz/processor_objects.cpp
@@ -25,9 +25,6 @@
 namespace Gargoyle {
 namespace Frotz {
 
-// TODO: Method stubs to implement
-static void new_line() {}
-
 #define MAX_OBJECT 2000
 
 enum O1 {
diff --git a/engines/gargoyle/frotz/processor_screen.cpp b/engines/gargoyle/frotz/processor_screen.cpp
index 5b3b8cb..27d3cda 100644
--- a/engines/gargoyle/frotz/processor_screen.cpp
+++ b/engines/gargoyle/frotz/processor_screen.cpp
@@ -25,11 +25,6 @@
 namespace Gargoyle {
 namespace Frotz {
 
-// TODO: Method stubs to implement
-static void storew(zword, zword) {}
-static void print_char(zchar) {}
-
-
 void Processor::screen_char(zchar c) {
 	// TODO
 }
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
index a536095..3f35c1f 100644
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -28,8 +28,6 @@ namespace Frotz {
 // TODO: Implement method stubs
 static void os_scrollback_char(zchar) {}
 static void os_scrollback_erase(zword) {}
-static void script_open() {}
-static void script_close() {}
 static void script_mssg_on() {}
 static void script_mssg_off() {}
 static void script_char(zchar) {}
@@ -51,7 +49,6 @@ static void record_open() {}
 static void record_close() {}
 static void record_write_key(zchar) {}
 static void record_write_input(zchar *, zchar) {}
-static void os_restart_game(zword) {}
 static void restart_header() {}
 
 
diff --git a/engines/gargoyle/frotz/processor_variables.cpp b/engines/gargoyle/frotz/processor_variables.cpp
index 24bbd28..19a40b9 100644
--- a/engines/gargoyle/frotz/processor_variables.cpp
+++ b/engines/gargoyle/frotz/processor_variables.cpp
@@ -25,10 +25,6 @@
 namespace Gargoyle {
 namespace Frotz {
 
-// TODO: Replace method stubs
-static void storew(zword, zword) {}
-
-
 void Processor::z_dec() {
 	zword value;
 


Commit: 1ca59e74c38c397d9631c0779bf102136be83f1d
    https://github.com/scummvm/scummvm/commit/1ca59e74c38c397d9631c0779bf102136be83f1d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added storeb & storew into the Mem class

Changed paths:
  A engines/gargoyle/frotz/processor_mem.cpp
    engines/gargoyle/frotz/mem.cpp
    engines/gargoyle/frotz/mem.h
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_input.cpp
    engines/gargoyle/frotz/processor_streams.cpp
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
index d9ff53d..57b2825 100644
--- a/engines/gargoyle/frotz/mem.cpp
+++ b/engines/gargoyle/frotz/mem.cpp
@@ -134,11 +134,10 @@ void Mem::initialize() {
 		if (story_size - size < 0x8000)
 			n = story_size - size;
 
-		setPC(size);
+		SET_PC(size);
 
 		if (story_fp->read(pcp, n) != n)
 			error("Story file read error");
-
 	}
 
 	// Read header extension table
@@ -236,5 +235,91 @@ zword Mem::get_header_extension(int entry) {
 	return val;   
 }
 
+void Mem::set_header_extension(int entry, zword val) {
+	zword addr;
+
+	if (h_extension_table == 0 || entry > hx_table_size)
+		return;
+
+	addr = h_extension_table + 2 * entry;
+	SET_WORD(addr, val);
+}
+
+void Mem::restart_header(void) {
+	zword screen_x_size;
+	zword screen_y_size;
+	zbyte font_x_size;
+	zbyte font_y_size;
+
+	int i;
+
+	SET_BYTE(H_CONFIG, h_config);
+	SET_WORD(H_FLAGS, h_flags);
+
+	if (h_version >= V4) {
+		SET_BYTE(H_INTERPRETER_NUMBER, h_interpreter_number);
+		SET_BYTE(H_INTERPRETER_VERSION, h_interpreter_version);
+		SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
+		SET_BYTE(H_SCREEN_COLS, h_screen_cols);
+	}
+
+	/* It's less trouble to use font size 1x1 for V5 games, especially
+	because of a bug in the unreleased German version of "Zork 1" */
+
+	if (h_version != V6) {
+		screen_x_size = (zword)h_screen_cols;
+		screen_y_size = (zword)h_screen_rows;
+		font_x_size = 1;
+		font_y_size = 1;
+	} else {
+		screen_x_size = h_screen_width;
+		screen_y_size = h_screen_height;
+		font_x_size = h_font_width;
+		font_y_size = h_font_height;
+	}
+
+	if (h_version >= V5) {
+		SET_WORD(H_SCREEN_WIDTH, screen_x_size);
+		SET_WORD(H_SCREEN_HEIGHT, screen_y_size);
+		SET_BYTE(H_FONT_HEIGHT, font_y_size);
+		SET_BYTE(H_FONT_WIDTH, font_x_size);
+		SET_BYTE(H_DEFAULT_BACKGROUND, h_default_background);
+		SET_BYTE(H_DEFAULT_FOREGROUND, h_default_foreground);
+	}
+
+	if (h_version == V6)
+		for (i = 0; i < 8; i++)
+			storeb((zword)(H_USER_NAME + i), h_user_name[i]);
+
+	SET_BYTE(H_STANDARD_HIGH, h_standard_high);
+	SET_BYTE(H_STANDARD_LOW, h_standard_low);
+
+	set_header_extension(HX_FLAGS, hx_flags);
+	set_header_extension(HX_FORE_COLOUR, hx_fore_colour);
+	set_header_extension(HX_BACK_COLOUR, hx_back_colour);
+}
+
+void Mem::storeb(zword addr, zbyte value) {
+	if (addr >= h_dynamic_size)
+		runtimeError(ERR_STORE_RANGE);
+
+	if (addr == H_FLAGS + 1) {
+		// flags register is modified
+
+		h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
+		h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
+
+		flagsChanged(value);
+	}
+
+	SET_BYTE(addr, value);
+}
+
+void Mem::storew(zword addr, zword value) {
+	storeb((zword)(addr + 0), hi(value));
+	storeb((zword)(addr + 1), lo(value));
+}
+
+
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index e12c062..c128994 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -24,6 +24,7 @@
 #define GARGOYLE_FROTZ_MEM
 
 #include "gargoyle/frotz/frotz_types.h"
+#include "gargoyle/frotz/err.h"
 
 namespace Gargoyle {
 namespace Frotz {
@@ -202,46 +203,51 @@ private:
 	 * Handles loading the game header
 	 */
 	void loadGameHeader();
-public:
+protected:
 	/**
-	 * Constructor
+	 * Read a value from the header extension (former mouse table).
 	 */
-	Mem();
+	zword get_header_extension(int entry);
 
 	/**
-	 * Initialize
+	 * Set an entry in the header extension (former mouse table).
 	 */
-	void initialize();
+	void set_header_extension(int entry, zword val);
 
 	/**
-	 * Read a word
+	 * Set all header fields which hold information about the interpreter.
 	 */
-	zword readWord() {
-		pcp += 2;
-		return READ_BE_UINT16(pcp - 2);
-	}
+	void restart_header();
 
 	/**
-	 * Read a word at a given index relative to pcp
+	 * Write a byte value to the dynamic Z-machine memory.
 	 */
-	zword readWord(size_t ofs) {
-		return READ_BE_UINT16(pcp + ofs);
-	}
+	void storeb(zword addr, zbyte value);
 
 	/**
-	 * Get the PC
+	 * Write a word value to the dynamic Z-machine memory.
 	 */
-	uint getPC() const { return pcp - zmp; }
+	void storew(zword addr, zword value);
 
 	/**
-	 * Set the PC
+	 * Generates a runtime error
 	 */
-	void setPC(uint ofs) { pcp = zmp + ofs; }
+	virtual void runtimeError(ErrorCode errNum) = 0;
 
 	/**
-	 * Read a value from the header extension (former mouse table).
+	 * Called when the flags are changed
 	 */
-	zword get_header_extension(int entry);
+	virtual void flagsChanged(zbyte value) = 0;
+public:
+	/**
+	 * Constructor
+	 */
+	Mem();
+
+	/**
+	 * Initialize
+	 */
+	void initialize();
 };
 
 } // End of namespace Frotz
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index 1554265..b70bf44 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -208,14 +208,16 @@ private:
 	 */
 
 	/**
-	 * Write a byte value to the dynamic Z-machine memory.
+	 * Generates a runtime error
 	 */
-	void storeb(zword addr, zbyte value);
+	virtual void runtimeError(ErrorCode errNum) override {
+		Errors::runtimeError(errNum);
+	}
 
 	/**
-	 * Write a word value to the dynamic Z-machine memory.
+	 * Called when the H_FLAGS field of the header has changed
 	 */
-	void storew(zword addr, zword value);
+	virtual void flagsChanged(zbyte value) override;
 
 	/**
 	 * \defgroup Object support methods
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
index 43f0aaf..b9c9604 100644
--- a/engines/gargoyle/frotz/processor_input.cpp
+++ b/engines/gargoyle/frotz/processor_input.cpp
@@ -80,35 +80,6 @@ int Processor::read_number() {
     return value;
 }
 
-void Processor::storeb(zword addr, zbyte value) {
-	if (addr >= h_dynamic_size)
-		runtimeError(ERR_STORE_RANGE);
-
-	if (addr == H_FLAGS + 1) {	/* flags register is modified */
-
-		h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
-		h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
-
-		if (value & SCRIPTING_FLAG) {
-			if (!ostream_script)
-				script_open();
-		} else {
-			if (ostream_script)
-				script_close();
-		}
-
-		// TOR - glkified / refresh_text_style ();
-	}
-
-	SET_BYTE(addr, value);
-
-}
-
-void Processor::storew(zword addr, zword value) {
-	storeb((zword)(addr + 0), hi(value));
-	storeb((zword)(addr + 1), lo(value));
-}
-
 void Processor::z_read() {
     zchar buffer[INPUT_BUFFER_SIZE];
     zword addr;
diff --git a/engines/gargoyle/frotz/processor_mem.cpp b/engines/gargoyle/frotz/processor_mem.cpp
new file mode 100644
index 0000000..1f3944e
--- /dev/null
+++ b/engines/gargoyle/frotz/processor_mem.cpp
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 "gargoyle/frotz/processor.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+void Processor::flagsChanged(zbyte value) {
+	if (value & SCRIPTING_FLAG) {
+		if (!ostream_script)
+			script_open();
+	} else {
+		if (ostream_script)
+			script_close();
+	}
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
index 3f35c1f..4406275 100644
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -49,7 +49,6 @@ static void record_open() {}
 static void record_close() {}
 static void record_write_key(zchar) {}
 static void record_write_input(zchar *, zchar) {}
-static void restart_header() {}
 
 
 void Processor::scrollback_char (zchar c) {
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 78bde80..14cc048 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -31,6 +31,7 @@ MODULE_OBJS := \
 	frotz/processor_buffer.o \
 	frotz/processor_input.o \
 	frotz/processor_maths.o \
+	frotz/processor_mem.o \
 	frotz/processor_objects.o \
 	frotz/processor_screen.o \
 	frotz/processor_streams.o \


Commit: 5507242395a0b183b8f445ca5a56284d974687f2
    https://github.com/scummvm/scummvm/commit/5507242395a0b183b8f445ca5a56284d974687f2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix casting of vardiac retrieval

Thanks to bgK for identifying the bad cast

Changed paths:
    engines/gargoyle/scott/scott.cpp


diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 3316995..698a2ea 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -1241,7 +1241,7 @@ void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
 			c = f->readByte();
 
 		// Get the next value
-		int *val = (int *)va_arg(va, int);
+		int *val = va_arg(va, int *);
 		*val = 0;
 
 		int factor = c == '-' ? -1 : 1;


Commit: e1de76b491e5145b837b55f7313b8fff07986572
    https://github.com/scummvm/scummvm/commit/e1de76b491e5145b837b55f7313b8fff07986572
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added remaining memory methods

Changed paths:
    engines/gargoyle/frotz/mem.cpp
    engines/gargoyle/frotz/mem.h
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_input.cpp
    engines/gargoyle/frotz/processor_mem.cpp


diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
index 57b2825..ad60b89 100644
--- a/engines/gargoyle/frotz/mem.cpp
+++ b/engines/gargoyle/frotz/mem.cpp
@@ -320,6 +320,103 @@ void Mem::storew(zword addr, zword value) {
 	storeb((zword)(addr + 1), lo(value));
 }
 
+void Mem::free_undo(int count) {
+	undo_t *p;
+
+	if (count > undo_count)
+		count = undo_count;
+	while (count--) {
+		p = first_undo;
+		if (curr_undo == first_undo)
+			curr_undo = curr_undo->next;
+		first_undo = first_undo->next;
+		free(p);
+		undo_count--;
+	}
+	if (first_undo)
+		first_undo->prev = NULL;
+	else
+		last_undo = NULL;
+}
+
+void Mem::reset_memory() {
+	story_fp = nullptr;
+	blorb_ofs = 0;
+	blorb_len = 0;
+
+	if (undo_mem) {
+		free_undo(undo_count);
+		delete undo_mem;
+	}
+
+	undo_mem = nullptr;
+	undo_count = 0;
+	delete[] zmp;
+	zmp = nullptr;
+}
+
+long Mem::mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff) {
+	unsigned size = mem_size;
+	zbyte *p = diff;
+	unsigned j;
+	zbyte c = 0;
+
+	for (;;) {
+		for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
+			size--;
+		if (size == 0) break;
+		size--;
+		if (j > 0x8000) {
+			*p++ = 0;
+			*p++ = 0xff;
+			*p++ = 0xff;
+			j -= 0x8000;
+		}
+		if (j > 0) {
+			*p++ = 0;
+			j--;
+			if (j <= 0x7f) {
+				*p++ = j;
+			} else {
+				*p++ = (j & 0x7f) | 0x80;
+				*p++ = (j & 0x7f80) >> 7;
+			}
+		}
+
+		*p++ = c;
+		*(b - 1) ^= c;
+	}
+
+	return p - diff;
+}
+
+void Mem::mem_undiff(zbyte *diff, long diff_length, zbyte *dest) {
+	zbyte c;
+
+	while (diff_length) {
+		c = *diff++;
+		diff_length--;
+		if (c == 0) {
+			unsigned runlen;
+
+			if (!diff_length)
+				return;  // Incomplete run
+			runlen = *diff++;
+			diff_length--;
+			if (runlen & 0x80) {
+				if (!diff_length)
+					return; // Incomplete extended run
+				c = *diff++;
+				diff_length--;
+				runlen = (runlen & 0x7f) | (((unsigned)c) << 7);
+			}
+
+			dest += runlen + 1;
+		} else {
+			*dest++ ^= c;
+		}
+	}
+}
 
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index c128994..a45eec5 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -230,6 +230,11 @@ protected:
 	void storew(zword addr, zword value);
 
 	/**
+	 * Free count undo blocks from the beginning of the undo list
+	 */
+	void free_undo(int count);
+
+	/**
 	 * Generates a runtime error
 	 */
 	virtual void runtimeError(ErrorCode errNum) = 0;
@@ -238,6 +243,26 @@ protected:
 	 * Called when the flags are changed
 	 */
 	virtual void flagsChanged(zbyte value) = 0;
+
+	/**
+	 * Close the story file and deallocate memory.
+	 */
+	void reset_memory();
+
+	/**
+	 * Set diff to a Quetzal-like difference between a and b,
+	 * copying a to b as we go.  It is assumed that diff points to a
+	 * buffer which is large enough to hold the diff.
+	 * mem_size is the number of bytes to compare.
+	 * Returns the number of bytes copied to diff.
+	 *
+	 */
+	long mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff);
+
+	/**
+	 * Applies a quetzal-like diff to dest
+	 */
+	void mem_undiff(zbyte *diff, long diff_length, zbyte *dest);
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index b70bf44..f9352ec 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -220,6 +220,18 @@ private:
 	virtual void flagsChanged(zbyte value) override;
 
 	/**
+	 * This function does the dirty work for z_save_undo.
+	 */
+	int save_undo();
+
+	/**
+	 * This function does the dirty work for z_restore_undo.
+	 */
+	int restore_undo();
+
+	/**@}*/
+
+	/**
 	 * \defgroup Object support methods
 	 * @{
 	 */
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
index b9c9604..48951d2 100644
--- a/engines/gargoyle/frotz/processor_input.cpp
+++ b/engines/gargoyle/frotz/processor_input.cpp
@@ -26,7 +26,6 @@ namespace Gargoyle {
 namespace Frotz {
 
 // TODO: Implement method stubs
-static void save_undo() {}
 static zword os_read_mouse() { return 0; }
 
 
diff --git a/engines/gargoyle/frotz/processor_mem.cpp b/engines/gargoyle/frotz/processor_mem.cpp
index 1f3944e..71abe77 100644
--- a/engines/gargoyle/frotz/processor_mem.cpp
+++ b/engines/gargoyle/frotz/processor_mem.cpp
@@ -35,5 +35,88 @@ void Processor::flagsChanged(zbyte value) {
 	}
 }
 
+int Processor::save_undo() {
+	long diff_size;
+	zword stack_size;
+	undo_t *p;
+
+	if (_undo_slots == 0)
+		// undo feature unavailable
+		return -1;
+
+	// save undo possible
+	while (last_undo != curr_undo) {
+		p = last_undo;
+		last_undo = last_undo->prev;
+		delete p;
+		undo_count--;
+	}
+	if (last_undo)
+		last_undo->next = nullptr;
+	else
+		first_undo = nullptr;
+
+	if (undo_count == _undo_slots)
+		free_undo(1);
+
+	diff_size = mem_diff(zmp, prev_zmp, h_dynamic_size, undo_diff);
+	stack_size = _stack + STACK_SIZE - _sp;
+	do {
+		p = (undo_t *) malloc(sizeof(undo_t) + diff_size + stack_size * sizeof(*_sp));
+		if (p == nullptr)
+			free_undo(1);
+	} while (!p && undo_count);
+	if (p == nullptr)
+		return -1;
+
+	GET_PC(p->pc);
+	p->frame_count = _frameCount;
+	p->diff_size = diff_size;
+	p->stack_size = stack_size;
+	p->frame_offset = _fp - _stack;
+	memcpy(p + 1, undo_diff, diff_size);
+	memcpy((zbyte *)(p + 1) + diff_size, _sp, stack_size * sizeof(*_sp));
+
+	if (!first_undo) {
+		p->prev = nullptr;
+		first_undo = p;
+	} else {
+		last_undo->next = p;
+		p->prev = last_undo;
+	}
+
+	p->next = nullptr;
+	curr_undo = last_undo = p;
+	undo_count++;
+
+	return 1;
+}
+
+int Processor::restore_undo(void) {
+	if (_undo_slots == 0)
+		// undo feature unavailable
+		return -1;
+
+	if (curr_undo == nullptr)
+		// no saved game state
+		return 0;
+
+	// undo possible
+	memcpy(zmp, prev_zmp, h_dynamic_size);
+	SET_PC(curr_undo->pc);
+	_sp = _stack + STACK_SIZE - curr_undo->stack_size;
+	_fp = _stack + curr_undo->frame_offset;
+	_frameCount = curr_undo->frame_count;
+	mem_undiff((zbyte *)(curr_undo + 1), curr_undo->diff_size, prev_zmp);
+	memcpy(_sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
+		curr_undo->stack_size * sizeof(*_sp));
+
+	curr_undo = curr_undo->prev;
+
+	restart_header();
+
+	return 2;
+}
+
 } // End of namespace Scott
 } // End of namespace Gargoyle


Commit: 2c37a949136e4bd6a38daf420eb4638ce8d5f4ae
    https://github.com/scummvm/scummvm/commit/2c37a949136e4bd6a38daf420eb4638ce8d5f4ae
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Fix detecting .dat gamefiles, added Windows game versions detections

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


diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
index 5ff0cc7..61cf201 100644
--- a/engines/gargoyle/detection.cpp
+++ b/engines/gargoyle/detection.cpp
@@ -133,7 +133,8 @@ static const PlainGameDescriptor gargoyleGames[] = {
 	{ "adventure13", "Adventure 13" },
 	{ "adventure14", "Adventure 14" },
 	{ "buckaroobonzai", "Buckaroo Banzai" },
-
+	{ "marveladventure", "Marvel Adventure #1" },
+	{ "scottsampler", "Adventure International's Mini-Adventure Sampler" },
 	{0, 0}
 };
 
diff --git a/engines/gargoyle/scott/detection.cpp b/engines/gargoyle/scott/detection.cpp
index 1b8041a..e4047a5 100644
--- a/engines/gargoyle/scott/detection.cpp
+++ b/engines/gargoyle/scott/detection.cpp
@@ -35,6 +35,25 @@ struct ScottGame {
 };
 
 const ScottGame SCOTT_GAMES[] = {
+	// PC game versions
+	{ "7c6f495d757a54e73d259efc718d8024", "adventureland",     15896, "Adventureland" },
+	{ "ea535fa7684508410151b4561de1f323", "pirateadventure",   16325, "Pirate Adventure" },
+	{ "379c77a9a483886366b3b5c425e56410", "missionimpossible", 15275, "Mission Impossible" },
+	{ "a530a6857d1092eaa177eee575c94c71", "voodoocastle",      15852, "Voodoo Castle" },
+	{ "5ebb4ade985670bb2eac54f8fa202214", "thecount",          17476, "The Count" },
+	{ "c57bb6df04dc77a2b232bc5bcab6e417", "strangeodyssey",    17489, "Strange Odyssey" },
+	{ "ce2931ac3d5cbc270a5cb7be9e614f6e", "mysteryfunhouse",   17165, "Mystery Fun House" },
+	{ "4e6127fad6b5d75eccd3f3b101f8c9c8", "pyramidofdoom",     17673, "Pyramid Of Doom" },
+	{ "2c08327ab06d5490bd9e367ddaeca627", "ghosttown",         17831, "Ghost Town" },
+	{ "8feb77f11d32e9567ce2fc7d435eaf44", "savageisland1",     19533, "Savage Island, Part 1" },
+	{ "20c40a349f7a214ac515fb1d63c30a87", "savageisland2",     18367, "Savage Island, Part 2" },
+	{ "e2a8f956ab215012d1495550c4c11ee8", "goldenvoyage",      18513, "The Golden Voyage" },
+	{ "f986d7e1ee074f65b6c1d00461c9b3c3", "adventure13",       19232, "Adventure 13" },
+	{ "6d98f422cc986d959a3c74351785aea3", "adventure14",       19013, "Adventure 14" },
+	{ "aadcc04e6b37eb9d30a58b5bc775842e", "marveladventure",   18876, "Marvel Adventure #1" },
+	{ "d569a769f304dc02b3062d97458ddd01", "scottsampler",      13854, "Adventure International's Mini-Adventure Sampler" },
+
+	// PDA game versions
 	{ "ae541fc1085da2f7d561b72ed20a6bc1", "adventureland", 18003, "Adventureland" },
 	{ "cbd47ab4fcfe00231ffd71d52378d410", "pirateadventure", 18482, "Pirate Adventure" },
 	{ "9251ab2c64e63559d8a6e9e6246760a5", "missionimpossible", 17227, "Mission Impossible" },
@@ -59,7 +78,8 @@ bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 
 	// Loop through the files of the folder
 	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
-		if (file->isDirectory() || !file->getName().hasSuffix(".saga"))
+		if (file->isDirectory() || !(file->getName().hasSuffixIgnoreCase(".saga")
+				|| file->getName().hasSuffixIgnoreCase(".dat")))
 			continue;
 
 		if (gameFile.open(*file)) {


Commit: 7a52f21c0bb267d6851ede650298b1d084493ef6
    https://github.com/scummvm/scummvm/commit/7a52f21c0bb267d6851ede650298b1d084493ef6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added script/record/transcript/replay methods

Changed paths:
    engines/gargoyle/frotz/processor.cpp
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_input.cpp
    engines/gargoyle/frotz/processor_streams.cpp
    engines/gargoyle/glk.h
    engines/gargoyle/streams.cpp
    engines/gargoyle/streams.h


diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
index 9341013..ac18a23 100644
--- a/engines/gargoyle/frotz/processor.cpp
+++ b/engines/gargoyle/frotz/processor.cpp
@@ -136,7 +136,8 @@ Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
 		_randomInterval(0), _randomCtr(0), first_restart(true), script_valid(false),
-		_bufPos(0), _locked(false), _prevC('\0') {
+		_bufPos(0), _locked(false), _prevC('\0'), script_width(0),
+		sfp(nullptr), rfp(nullptr), pfp(nullptr) {
 	static const Opcode OP0_OPCODES[16] = {
 		&Processor::z_rtrue,
 		&Processor::z_rfalse,
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index f9352ec..50c951a 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -79,6 +79,10 @@ private:
 	Opcode op1_opcodes[16];
 	static Opcode var_opcodes[64];
 	static Opcode ext_opcodes[64];
+
+	// Stream related fields
+	int script_width;
+	strid_t sfp, rfp, pfp;
 private:
 	/**
 	 * \defgroup General support methods
@@ -358,7 +362,102 @@ private:
 	 */
 	void script_close();
 
-	 /**@}*/
+	/**
+	 * Write a newline to the transscript file.
+	 */
+	void script_new_line();
+
+	/**
+	 * Write a single character to the transscript file.
+	 */
+	void script_char(zchar c);
+
+	/**
+	 * Write a string to the transscript file.
+	 */
+	void script_word(const zchar *s);
+
+	/**
+	 * Send an input line to the transscript file.
+	 */
+	void script_write_input(const zchar *buf, zchar key);
+
+	/**
+	 * Remove an input line from the transscript file.
+	 */
+	void script_erase_input(const zchar *buf);
+
+	/**
+	 * Start sending a "debugging" message to the transscript file.
+	 */
+	void script_mssg_on();
+
+	/**
+	 * Stop writing a "debugging" message.
+	 */
+	void script_mssg_off();
+
+	/**
+	 * Open a file to record the player's input.
+	 */
+	void record_open();
+
+	/**
+	 * Stop recording the player's input.
+	 */
+	void record_close();
+
+	/**
+	 * Helper function for record_char.
+	 */
+	void record_code(int c, bool force_encoding);
+
+	/**
+	 * Write a character to the command file.
+	 */
+	void record_char(zchar c);
+
+	/**
+	 * Copy a keystroke to the command file.
+	 */
+	void record_write_key(zchar key);
+
+	/**
+	 * Copy a line of input to a command file.
+	 */
+	void record_write_input(const zchar *buf, zchar key);
+
+	/**
+	 * Open a file of commands for playback.
+	 */
+	void replay_open();
+
+	/**
+	 * Stop playback of commands.
+	 */
+	void replay_close();
+
+	/*
+	 * Helper function for replay_key and replay_line.
+	 */
+	int replay_code();
+
+	/**
+	 * Read a character from the command file.
+	 */
+	zchar replay_char();
+
+	/**
+	 * Read a keystroke from a command file.
+	 */
+	zchar replay_read_key();
+
+	/*
+	 * Read a line of input from a command file.
+	 */
+	zchar replay_read_input(zchar *buf);
+
+	/**@}*/
 
 	/**
 	 * \defgroup Text support methods
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
index 48951d2..89aa188 100644
--- a/engines/gargoyle/frotz/processor_input.cpp
+++ b/engines/gargoyle/frotz/processor_input.cpp
@@ -31,13 +31,6 @@ static zword os_read_mouse() { return 0; }
 
 #define INPUT_BUFFER_SIZE 200
 
-void Processor::z_make_menu() {
-    // This opcode was only used for the Macintosh version of Journey.
-	// It controls menus with numbers greater than 2 (menus 0, 1 and 2
-    // are system menus).
-    branch (false);
-}
-
 bool Processor::read_yes_or_no(const char *s) {
     zchar key;
 
@@ -65,6 +58,39 @@ void Processor::read_string(int max, zchar *buffer) {
     } while (key != ZC_RETURN);
 }
 
+bool Processor::is_terminator(zchar key) {
+	if (key == ZC_TIME_OUT)
+		return true;
+	if (key == ZC_RETURN)
+		return true;
+	if (key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX)
+		return true;
+
+	if (h_terminating_keys != 0) {
+		if (key >= ZC_ARROW_MIN && key <= ZC_MENU_CLICK) {
+
+			zword addr = h_terminating_keys;
+			zbyte c;
+
+			do {
+				LOW_BYTE(addr, c);
+				if (c == 255 || key == translate_from_zscii(c))
+					return true;
+				addr++;
+			} while (c != 0);
+		}
+	}
+
+	return false;
+}
+
+void Processor::z_make_menu() {
+	// This opcode was only used for the Macintosh version of Journey.
+	// It controls menus with numbers greater than 2 (menus 0, 1 and 2
+	// are system menus).
+	branch(false);
+}
+
 int Processor::read_number() {
     zchar buffer[6];
     int value = 0;
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
index 4406275..95205a5 100644
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -28,30 +28,15 @@ namespace Frotz {
 // TODO: Implement method stubs
 static void os_scrollback_char(zchar) {}
 static void os_scrollback_erase(zword) {}
-static void script_mssg_on() {}
-static void script_mssg_off() {}
-static void script_char(zchar) {}
-static void script_word(const zchar *) {}
-static void script_new_line() {}
-static void script_erase_input(const zchar *) {}
-static void script_write_input(zchar *, char) {}
 static void memory_open(zword, zword, bool) {}
 static void memory_close() {}
 static void memory_word(const zchar *) {}
 static void memory_new_line() {}
-static void replay_open() {}
-static void replay_close() {}
-static zchar replay_read_key() { return 0; }
-static zchar replay_read_input(zchar *) { return 0; }
 static zchar console_read_key(zword) { return 0; }
 static zchar console_read_input(uint, zchar *, uint, bool) { return 0; }
-static void record_open() {}
-static void record_close() {}
-static void record_write_key(zchar) {}
-static void record_write_input(zchar *, zchar) {}
 
 
-void Processor::scrollback_char (zchar c) {
+void Processor::scrollback_char(zchar c) {
     if (c == ZC_INDENT)
         { scrollback_char (' '); scrollback_char (' '); scrollback_char (' '); return; }
     if (c == ZC_GAP)
@@ -219,13 +204,282 @@ continue_input:
 }
 
 void Processor::script_open() {
-	// TODO
+	h_flags &= ~SCRIPTING_FLAG;
+
+	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript,
+		filemode_WriteAppend);
+	sfp = glk_stream_open_file(fref, filemode_WriteAppend);
+
+	if (sfp != nullptr) {
+		sfp->setPosition(0, seekmode_End);
+
+		h_flags |= SCRIPTING_FLAG;
+
+		script_valid = true;
+		ostream_script = true;
+
+		script_width = 0;
+	} else {
+		print_string("Cannot open file\n");
+	}
+
+	SET_WORD(H_FLAGS, h_flags);
 }
 
 void Processor::script_close() {
-	// TODO
+	h_flags &= ~SCRIPTING_FLAG;
+	SET_WORD(H_FLAGS, h_flags);
+
+	glk_stream_close(sfp);
+	ostream_script = false;
+}
+
+void Processor::script_new_line() {
+	script_char('\n');
+	script_width = 0;
+}
+
+void Processor::script_char(zchar c) {
+	if (c == ZC_INDENT && script_width != 0)
+		c = ' ';
+
+	if (c == ZC_INDENT) {
+		script_char(' ');
+		script_char(' ');
+		script_char(' ');
+		return;
+	}
+	if (c == ZC_GAP) {
+		script_char(' ');
+		script_char(' ');
+		return;
+	}
+
+	sfp->putCharUni(c);
+	script_width++;
+}
+
+void Processor::script_word(const zchar *s) {
+	int width;
+	int i;
+
+	if (*s == ZC_INDENT && script_width != 0)
+		script_char(*s++);
+
+	for (i = 0, width = 0; s[i] != 0; i++) {
+		if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT)
+			i++;
+		else if (s[i] == ZC_GAP)
+			width += 3;
+		else if (s[i] == ZC_INDENT)
+			width += 2;
+		else
+			width += 1;
+	}
+
+	if (_script_cols != 0 && script_width + width > _script_cols) {
+		if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
+			s++;
+
+		script_new_line();
+	}
+
+	for (i = 0; s[i] != 0; i++) {
+		if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
+			i++;
+		else
+			script_char(s[i]);
+	}
+}
+
+void Processor::script_write_input(const zchar *buf, zchar key) {
+	int width;
+	int i;
+
+	for (i = 0, width = 0; buf[i] != 0; i++)
+		width++;
+
+	if (_script_cols != 0 && script_width + width > _script_cols)
+		script_new_line();
+
+	for (i = 0; buf[i] != 0; i++)
+		script_char(buf[i]);
+
+	if (key == ZC_RETURN)
+		script_new_line();
+}
+
+void Processor::script_erase_input(const zchar *buf) {
+	int width;
+	int i;
+
+	for (i = 0, width = 0; buf[i] != 0; i++)
+		width++;
+
+	sfp->setPosition(-width, seekmode_Current);
+	script_width -= width;
+}
+
+void Processor::script_mssg_on() {
+	if (script_width != 0)
+		script_new_line();
+
+	script_char(ZC_INDENT);
+}
+
+void Processor::script_mssg_off() {
+	script_new_line();
+}
+
+void Processor::record_open() {
+	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Write);
+	if ((rfp = glk_stream_open_file(fref, filemode_Write)) != nullptr)
+		ostream_record = true;
+	else
+		print_string("Cannot open file\n");
+}
+
+void Processor::record_close() {
+	glk_stream_close(rfp);
+	ostream_record = false;
+}
+
+void Processor::record_code(int c, bool force_encoding) {
+	if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) {
+		int i;
+
+		rfp->putChar('[');
+
+		for (i = 10000; i != 0; i /= 10)
+			if (c >= i || i == 1)
+				rfp->putChar('0' + (c / i) % 10);
+
+		rfp->putChar(']');
+	} else {
+		rfp->putChar(c);
+	}
+}
+
+void Processor::record_char(zchar c) {
+	if (c != ZC_RETURN) {
+		if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) {
+			record_code(translate_to_zscii(c), false);
+			if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
+				record_code(mouse_x, true);
+				record_code(mouse_y, true);
+			}
+		} else {
+			record_code(1000 + c - ZC_HKEY_MIN, true);
+		}
+	}
+}
+
+void Processor::record_write_key(zchar key) {
+	record_char(key);
+	rfp->putChar('\n');
+}
+
+void Processor::record_write_input(const zchar *buf, zchar key) {
+	zchar c;
+
+	while ((c = *buf++) != 0)
+		record_char(c);
+
+	record_write_key(key);
+}
+
+void Processor::replay_open() {
+	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Read);
+	if ((pfp = glk_stream_open_file(fref, filemode_Read)) != nullptr)
+		istream_replay = true;
+	else
+		print_string("Cannot open file\n");
+}
+
+void Processor::replay_close() {
+	glk_stream_close(pfp);
+	istream_replay = false;
 }
 
+int Processor::replay_code() {
+	int c;
+
+	if ((c = pfp->getChar()) == '[') {
+		int c2;
+
+		c = 0;
+
+		while ((c2 = pfp->getChar()) != EOF && c2 >= '0' && c2 <= '9')
+			c = 10 * c + c2 - '0';
+
+		return (c2 == ']') ? c : EOF;
+	} else {
+		return c;
+	}
+}
+
+zchar Processor::replay_char() {
+	int c;
+
+	if ((c = replay_code()) != EOF) {
+		if (c != '\n') {
+			if (c < 1000) {
+
+				c = translate_from_zscii(c);
+
+				if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
+					mouse_x = replay_code();
+					mouse_y = replay_code();
+				}
+
+				return c;
+			} else {
+				return ZC_HKEY_MIN + c - 1000;
+			}
+		}
+
+		pfp->unputBuffer("\n", 1);
+		return ZC_RETURN;
+
+	} else {
+		return ZC_BAD;
+	}
+}
+
+zchar Processor::replay_read_key() {
+	zchar key = replay_char();
+
+	if (pfp->getChar() != '\n') {
+		replay_close();
+		return ZC_BAD;
+	} else {
+		return key;
+	}
+}
+
+zchar Processor::replay_read_input(zchar *buf) {
+	zchar c;
+
+	for (;;) {
+		c = replay_char();
+
+		if (c == ZC_BAD || is_terminator(c))
+			break;
+
+		*buf++ = c;
+	}
+
+	*buf = 0;
+
+	if (pfp->getChar() != '\n') {
+		replay_close();
+		return ZC_BAD;
+	} else {
+		return c;
+	}
+}
+
+
 void Processor::z_input_stream() {
 	flush_buffer();
 
@@ -260,7 +514,7 @@ void Processor::z_output_stream() {
     }
 }
 
-void Processor::z_restart(void) {
+void Processor::z_restart() {
 	flush_buffer();
 
 	os_restart_game(RESTART_BEGIN);
@@ -293,8 +547,7 @@ void Processor::z_restart(void) {
 	os_restart_game(RESTART_END);
 }
 
-
-void Processor::z_save(void) {
+void Processor::z_save() {
 #ifdef TODO
 	bool success = false;
 
@@ -310,8 +563,7 @@ void Processor::z_save(void) {
 
 		glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]);
 
-		stream_result_t result;
-		glk_stream_close(f, &result);
+		glk_stream_close(f);
 
 	} else {
 		long pc;
@@ -430,8 +682,8 @@ void Processor::z_restore() {
 			release = (unsigned) fgetc (gfp) << 8;
 			release |= fgetc (gfp);
 
-			(void) fgetc (gfp);
-			(void) fgetc (gfp);
+			() fgetc (gfp);
+			() fgetc (gfp);
 
 			/* Check the release number */
 
@@ -460,7 +712,7 @@ void Processor::z_restore() {
 					for (i = 0; i < skip; i++)
 						zmp[addr++] = fgetc (story_fp);
 					zmp[addr] = fgetc (gfp);
-					(void) fgetc (story_fp);
+					() fgetc (story_fp);
 				}
 
 				/* Check for errors */
@@ -519,7 +771,7 @@ finished:
 #endif
 }
 
-void Processor::z_verify(void) {
+void Processor::z_verify() {
 	zword checksum = 0;
 
 	// Sum all bytes in story file except header bytes
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index a7a1198..2f3620f 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -66,7 +66,7 @@ public:
 	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
 	                        glui32 wintype, glui32 rock = 0) const;
 
-	void glk_window_close(winid_t win, stream_result_t *result);
+	void glk_window_close(winid_t win, stream_result_t *result = nullptr);
 	void glk_window_get_size(winid_t win, glui32 *width, glui32 *height);
 	void glk_window_set_arrangement(winid_t win, glui32 method,
 	                                glui32 size, winid_t keyWin);
@@ -87,7 +87,7 @@ public:
 
 	strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock = 0);
 	strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
-	void glk_stream_close(strid_t str, stream_result_t *result);
+	void glk_stream_close(strid_t str, stream_result_t *result = nullptr);
 	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const;
 	glui32 glk_stream_get_rock(strid_t str) const;
 	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode);
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index 41f3caf..d5671f1 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -458,32 +458,30 @@ glui32 MemoryStream::getPosition() const {
 		return ((unsigned char *)_bufPtr - (unsigned char *)_buf);
 }
 
-void MemoryStream::setPosition(glui32 pos, glui32 seekMode) {
-	glsi32 newPos = pos;
-
+void MemoryStream::setPosition(glsi32 pos, glui32 seekMode) {
 	if (!_unicode) {
 		if (seekMode == seekmode_Current)
-			newPos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + newPos;
+			pos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + pos;
 		else if (seekMode == seekmode_End)
-			newPos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + newPos;
+			pos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + pos;
 		else
-			/* newPos = newPos */;
-		if (newPos < 0)
-			newPos = 0;
-		if (newPos > ((unsigned char *)_bufEof - (unsigned char *)_buf))
-			newPos = ((unsigned char *)_bufEof - (unsigned char *)_buf);
-		_bufPtr = (unsigned char *)_buf + newPos;
+			/* pos = pos */;
+		if (pos < 0)
+			pos = 0;
+		if (pos > ((unsigned char *)_bufEof - (unsigned char *)_buf))
+			pos = ((unsigned char *)_bufEof - (unsigned char *)_buf);
+		_bufPtr = (unsigned char *)_buf + pos;
 	} else {
 		if (seekMode == seekmode_Current)
-			newPos = ((glui32 *)_bufPtr - (glui32 *)_buf) + newPos;
+			pos = ((glui32 *)_bufPtr - (glui32 *)_buf) + pos;
 		else if (seekMode == seekmode_End)
-			newPos = ((glui32 *)_bufEof - (glui32 *)_buf) + newPos;
+			pos = ((glui32 *)_bufEof - (glui32 *)_buf) + pos;
 
-		if (newPos < 0)
-			newPos = 0;
-		if (newPos > ((glui32 *)_bufEof - (glui32 *)_buf))
-			newPos = ((glui32 *)_bufEof - (glui32 *)_buf);
-		_bufPtr = (glui32 *)_buf + newPos;
+		if (pos < 0)
+			pos = 0;
+		if (pos > ((glui32 *)_bufEof - (glui32 *)_buf))
+			pos = ((glui32 *)_bufEof - (glui32 *)_buf);
+		_bufPtr = (glui32 *)_buf + pos;
 	}
 }
 
@@ -1000,7 +998,7 @@ glui32 FileStream::getPosition() const {
 	return _outFile ? _outFile->pos() : _inStream->pos();
 }
 
-void FileStream::setPosition(glui32 pos, glui32 seekMode) {
+void FileStream::setPosition(glsi32 pos, glui32 seekMode) {
 	_lastOp = 0;
 	if (_unicode)
 		pos *= 4;
diff --git a/engines/gargoyle/streams.h b/engines/gargoyle/streams.h
index 772b021..ea9c651 100644
--- a/engines/gargoyle/streams.h
+++ b/engines/gargoyle/streams.h
@@ -223,7 +223,7 @@ public:
 		return 0;
 	}
 
-	virtual void setPosition(glui32 pos, glui32 seekMode) {}
+	virtual void setPosition(glsi32 pos, glui32 seekMode) {}
 
 	virtual void setStyle(glui32 val) {}
 
@@ -390,7 +390,7 @@ public:
 
 	virtual glui32 getPosition() const override;
 
-	virtual void setPosition(glui32 pos, glui32 seekMode) override;
+	virtual void setPosition(glsi32 pos, glui32 seekMode) override;
 
 	/**
 	 * Get a character from the stream
@@ -492,7 +492,7 @@ public:
 
 	virtual glui32 getPosition() const override;
 
-	virtual void setPosition(glui32 pos, glui32 seekMode) override;
+	virtual void setPosition(glsi32 pos, glui32 seekMode) override;
 
 	/**
 	 * Get a character from the stream


Commit: aab1dfeff8002a8b8f69d4d5f24110a7a01c263d
    https://github.com/scummvm/scummvm/commit/aab1dfeff8002a8b8f69d4d5f24110a7a01c263d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added memory redirect methods

Changed paths:
    engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_mem.cpp
    engines/gargoyle/frotz/processor_streams.cpp


diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index 2b33fbd..3ccaa45 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -239,6 +239,18 @@ struct UserOptions {
 	}
 };
 
+#define MAX_NESTING 16
+struct Redirect {
+	zword _xSize;
+	zword _table;
+	zword _width;
+	zword _total;
+
+	Redirect() : _xSize(0), _table(0), _width(0), _total(0) {}
+	Redirect(zword xSize, zword table, zword width = 0, zword total = 0) :
+		_xSize(xSize), _table(table), _width(width), _total(total) {}
+};
+
 } // End of namespace Frotz
 } // End of namespace Gargoyle
 
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index 50c951a..0a9d0ef 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -26,6 +26,8 @@
 #include "gargoyle/frotz/err.h"
 #include "gargoyle/frotz/mem.h"
 #include "gargoyle/frotz/glk_interface.h"
+#include "gargoyle/frotz/frotz_types.h"
+#include "common/stack.h"
 
 namespace Gargoyle {
 namespace Frotz {
@@ -83,6 +85,7 @@ private:
 	// Stream related fields
 	int script_width;
 	strid_t sfp, rfp, pfp;
+	Common::FixedStack<Redirect, MAX_NESTING> _redirect;
 private:
 	/**
 	 * \defgroup General support methods
@@ -233,6 +236,26 @@ private:
 	 */
 	int restore_undo();
 
+	/**
+	 * Begin output redirection to the memory of the Z-machine.
+	 */
+	void memory_open(zword table, zword xsize, bool buffering);
+
+	/**
+	 * End of output redirection.
+	 */
+	void memory_close();
+
+	/**
+	 * Redirect a newline to the memory of the Z-machine.
+	 */
+	void memory_new_line();
+
+	/**
+	 * Redirect a string of characters to the memory of the Z-machine.
+	 */
+	void memory_word(const zchar *s);
+
 	/**@}*/
 
 	/**
diff --git a/engines/gargoyle/frotz/processor_mem.cpp b/engines/gargoyle/frotz/processor_mem.cpp
index 71abe77..d46402c 100644
--- a/engines/gargoyle/frotz/processor_mem.cpp
+++ b/engines/gargoyle/frotz/processor_mem.cpp
@@ -118,5 +118,101 @@ int Processor::restore_undo(void) {
 	return 2;
 }
 
+/**
+ * TOR: glkify -- this is for V6 only
+ */
+static zword get_max_width(zword win) { return 80; }
+
+void Processor::memory_open(zword table, zword xsize, bool buffering) {
+	if (_redirect.size() < MAX_NESTING) {
+		if (!buffering)
+			xsize = 0xffff;
+		if (buffering && (short)xsize <= 0)
+			xsize = get_max_width((zword)(-(short)xsize));
+
+		storew(table, 0);
+
+		_redirect.push(Redirect(xsize, table));
+		ostream_memory = true;
+	} else {
+		runtimeError(ERR_STR3_NESTING);
+	}
+}
+
+void Processor::memory_new_line() {
+	zword size;
+	zword addr;
+
+	Redirect &r = _redirect.top();
+	r._total += r._width;
+	r._width = 0;
+
+	addr = r._table;
+
+	LOW_WORD(addr, size);
+	addr += 2;
+
+	if (r._xSize != 0xffff) {
+		r._table = addr + size;
+		size = 0;
+	} else {
+		storeb((zword)(addr + (size++)), 13);
+	}
+
+	storew(r._table, size);
+}
+
+void Processor::memory_word(const zchar *s) {
+	zword size;
+	zword addr;
+	zchar c;
+
+	Redirect &r = _redirect.top();
+	if (h_version == V6) {
+		int width = os_string_width(s);
+
+		if (r._xSize != 0xffff) {
+			if (r._width + width > r._xSize) {
+
+				if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
+					width = os_string_width(++s);
+
+				memory_new_line();
+			}
+		}
+
+		r._width += width;
+	}
+
+	addr = r._table;
+
+	LOW_WORD(addr, size);
+	addr += 2;
+
+	while ((c = *s++) != 0)
+		storeb((zword)(addr + (size++)), translate_to_zscii(c));
+
+	storew(r._table, size);
+}
+
+void Processor::memory_close(void) {
+	if (!_redirect.empty()) {
+		Redirect &r = _redirect.top();
+
+		if (r._xSize != 0xffff)
+			memory_new_line();
+
+		if (h_version == V6) {
+			h_line_width = (r._xSize != 0xffff) ? r._total : r._width;
+
+			SET_WORD(H_LINE_WIDTH, h_line_width);
+		}
+
+		_redirect.pop();
+		if (_redirect.empty())
+			ostream_memory = false;
+	}
+}
+
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
index 95205a5..97784c8 100644
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -28,10 +28,6 @@ namespace Frotz {
 // TODO: Implement method stubs
 static void os_scrollback_char(zchar) {}
 static void os_scrollback_erase(zword) {}
-static void memory_open(zword, zword, bool) {}
-static void memory_close() {}
-static void memory_word(const zchar *) {}
-static void memory_new_line() {}
 static zchar console_read_key(zword) { return 0; }
 static zchar console_read_input(uint, zchar *, uint, bool) { return 0; }
 


Commit: 7d2406870e733258c7d59d54a7b490fbf52c2109
    https://github.com/scummvm/scummvm/commit/7d2406870e733258c7d59d54a7b490fbf52c2109
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Add screen message methods

Changed paths:
    engines/gargoyle/frotz/glk_interface.cpp
    engines/gargoyle/frotz/glk_interface.h
    engines/gargoyle/frotz/processor_buffer.cpp
    engines/gargoyle/frotz/processor_input.cpp
    engines/gargoyle/frotz/processor_screen.cpp


diff --git a/engines/gargoyle/frotz/glk_interface.cpp b/engines/gargoyle/frotz/glk_interface.cpp
index 13d0a56..c536a48 100644
--- a/engines/gargoyle/frotz/glk_interface.cpp
+++ b/engines/gargoyle/frotz/glk_interface.cpp
@@ -397,5 +397,12 @@ void GlkInterface::smartstatusline() {
 	glk_window_move_cursor(gos_upper, cury - 1, curx - 1);
 }
 
+void GlkInterface::gos_cancel_pending_line() {
+	event_t ev;
+	glk_cancel_line_event(gos_linewin, &ev);
+	gos_linebuf[ev.val1] = '\0';
+	gos_linepending = 0;
+}
+
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/glk_interface.h b/engines/gargoyle/frotz/glk_interface.h
index 161a1f3..aeb33a8 100644
--- a/engines/gargoyle/frotz/glk_interface.h
+++ b/engines/gargoyle/frotz/glk_interface.h
@@ -143,9 +143,22 @@ protected:
 	void smartstatusline();
 
 	/**
+	 * Cancels any pending line
+	 */
+	void gos_cancel_pending_line();
+
+	/**
 	 * Called during game restarts
 	 */
 	void os_restart_game(RestartAction) {}
+
+	/**
+	 * Reads the mouse buttons
+	 */
+	zword os_read_mouse() {
+		// Not implemented
+		return 0;
+	}
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/frotz/processor_buffer.cpp b/engines/gargoyle/frotz/processor_buffer.cpp
index be30bfe..b2b6ccd 100644
--- a/engines/gargoyle/frotz/processor_buffer.cpp
+++ b/engines/gargoyle/frotz/processor_buffer.cpp
@@ -27,11 +27,6 @@
 namespace Gargoyle {
 namespace Frotz {
 
-// TODO: Replace these method stubs with correct calls
-void stream_char(zchar) {}
-void stream_word(const zchar *) {}
-void stream_new_line(void) {}
-
 void Processor::flush_buffer() {
 	/* Make sure we stop when flush_buffer is called from flush_buffer.
 	 * Note that this is difficult to avoid as we might print a newline
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
index 89aa188..fa9da21 100644
--- a/engines/gargoyle/frotz/processor_input.cpp
+++ b/engines/gargoyle/frotz/processor_input.cpp
@@ -25,10 +25,6 @@
 namespace Gargoyle {
 namespace Frotz {
 
-// TODO: Implement method stubs
-static zword os_read_mouse() { return 0; }
-
-
 #define INPUT_BUFFER_SIZE 200
 
 bool Processor::read_yes_or_no(const char *s) {
diff --git a/engines/gargoyle/frotz/processor_screen.cpp b/engines/gargoyle/frotz/processor_screen.cpp
index 27d3cda..77e2e94 100644
--- a/engines/gargoyle/frotz/processor_screen.cpp
+++ b/engines/gargoyle/frotz/processor_screen.cpp
@@ -25,28 +25,106 @@
 namespace Gargoyle {
 namespace Frotz {
 
-void Processor::screen_char(zchar c) {
-	// TODO
+void Processor::screen_mssg_on() {
+	if (gos_curwin == gos_lower) {
+		oldstyle = curstyle;
+		glk_set_style(style_Preformatted);
+		glk_put_string("\n    ");
+	}
 }
 
-void Processor::screen_new_line() {
-	// TODO
+void Processor::screen_mssg_off() {
+	if (gos_curwin == gos_lower) {
+		glk_put_char('\n');
+		zargs[0] = 0;
+		z_set_text_style();
+		zargs[0] = oldstyle;
+		z_set_text_style();
+	}
 }
 
-void Processor::screen_word(const zchar *s) {
-	// TODO
+void Processor::screen_char(zchar c) {
+	if (gos_linepending && (gos_curwin == gos_linewin)) {
+		gos_cancel_pending_line();
+		if (gos_curwin == gos_upper) {
+			curx = 1;
+			cury ++;
+		}
+		if (c == '\n')
+			return;
+	}
+
+	// check fixed flag in header, game can change it at whim
+	int forcefix = ((h_flags & FIXED_FONT_FLAG) != 0);
+	int curfix = ((curstyle & FIXED_WIDTH_STYLE) != 0);
+	if (forcefix && !curfix) {
+		zargs[0] = 0xf000;	// tickle tickle!
+		z_set_text_style();
+		fixforced = true;
+	} else if (!forcefix && fixforced) {
+		zargs[0] = 0xf000;	// tickle tickle!
+		z_set_text_style();
+		fixforced = false;
+	}
+
+	if (gos_upper && gos_curwin == gos_upper) {
+		if (c == '\n' || c == ZC_RETURN) {
+			glk_put_char('\n');
+			curx = 1;
+			cury ++;
+		} else {
+			if (cury == 1) {
+				if (curx <= ((sizeof statusline / sizeof(zchar)) - 1)) {
+					statusline[curx - 1] = c;
+					statusline[curx] = 0;
+				}
+				if (curx < h_screen_cols) {
+					glk_put_char_uni(c);
+				} else if (curx == h_screen_cols) {
+					glk_put_char_uni(c);
+					glk_window_move_cursor(gos_curwin, curx-1, cury-1);
+				} else {
+					smartstatusline();
+				}
+
+				curx++;
+			} else {
+				if (curx < h_screen_cols) {
+					glk_put_char_uni(c);
+				} else if (curx == (h_screen_cols)) {
+					glk_put_char_uni(c);
+					glk_window_move_cursor(gos_curwin, curx-1, cury-1);
+				}
+
+				curx++;
+			}
+		}
+	} else if (gos_curwin == gos_lower) {
+		if (c == ZC_RETURN)
+			glk_put_char('\n');
+		else glk_put_char_uni(c);
+	}
 }
 
-void Processor::screen_mssg_on() {
-	// TODO
+void Processor::screen_new_line() {
+	screen_char('\n');
 }
 
-void Processor::screen_mssg_off() {
-	// TODO
+void Processor::screen_word(const zchar *s) {
+	zchar c;
+	while ((c = *s++) != 0) {
+		if (c == ZC_NEW_FONT)
+			s++;
+		else if (c == ZC_NEW_STYLE)
+			s++;
+		else
+			screen_char(c);
+	}
 }
 
 
 void Processor::z_buffer_mode() {
+	// No implementation
 }
 
 void Processor::z_buffer_screen() {


Commit: ce7113b34a808ef8136114881f9d19ce857d13bc
    https://github.com/scummvm/scummvm/commit/ce7113b34a808ef8136114881f9d19ce857d13bc
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added OS read line and character methods

Changed paths:
    engines/gargoyle/frotz/glk_interface.cpp
    engines/gargoyle/frotz/glk_interface.h
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_streams.cpp


diff --git a/engines/gargoyle/frotz/glk_interface.cpp b/engines/gargoyle/frotz/glk_interface.cpp
index c536a48..c376dc0 100644
--- a/engines/gargoyle/frotz/glk_interface.cpp
+++ b/engines/gargoyle/frotz/glk_interface.cpp
@@ -404,5 +404,95 @@ void GlkInterface::gos_cancel_pending_line() {
 	gos_linepending = 0;
 }
 
+zchar GlkInterface::os_read_key(int timeout, bool show_cursor) {
+	event_t ev;
+	winid_t win = gos_curwin ? gos_curwin : gos_lower;
+
+	if (gos_linepending)
+		gos_cancel_pending_line();
+
+	glk_request_char_event_uni(win);
+	if (timeout != 0)
+		glk_request_timer_events(timeout * 100);
+
+	while (!shouldQuit()) {
+		glk_select(&ev);
+		if (ev.type == evtype_Arrange) {
+			gos_update_height();
+			gos_update_width();
+		} else if (ev.type == evtype_Timer) {
+			glk_cancel_char_event(win);
+			glk_request_timer_events(0);
+			return ZC_TIME_OUT;
+		} else if (ev.type == evtype_CharInput)
+			break;
+	}
+	if (shouldQuit())
+		return 0;
+
+	glk_request_timer_events(0);
+
+	if (gos_upper && mach_status_ht < curr_status_ht)
+		reset_status_ht();
+	curr_status_ht = 0;
+
+	switch (ev.val1) {
+	case keycode_Escape: return ZC_ESCAPE;
+	case keycode_PageUp: return ZC_ARROW_MIN;
+	case keycode_PageDown: return ZC_ARROW_MAX;
+	case keycode_Left: return ZC_ARROW_LEFT;
+	case keycode_Right: return ZC_ARROW_RIGHT;
+	case keycode_Up: return ZC_ARROW_UP;
+	case keycode_Down: return ZC_ARROW_DOWN;
+	case keycode_Return: return ZC_RETURN;
+	case keycode_Delete: return ZC_BACKSPACE;
+	case keycode_Tab: return ZC_INDENT;
+	default:
+		return ev.val1;
+	}
+}
+
+zchar GlkInterface::os_read_line(int max, zchar *buf, int timeout, int width, int continued) {
+	event_t ev;
+	winid_t win = gos_curwin ? gos_curwin : gos_lower;
+
+	if (!continued && gos_linepending)
+		gos_cancel_pending_line();
+
+	if (!continued || !gos_linepending) {
+		glk_request_line_event_uni(win, buf, max, os_string_length(buf));
+		if (timeout != 0)
+			glk_request_timer_events(timeout * 100);
+	}
+
+	gos_linepending = 0;
+
+	while (!shouldQuit()) {
+		glk_select(&ev);
+		if (ev.type == evtype_Arrange) {
+			gos_update_height();
+			gos_update_width();
+		} else if (ev.type == evtype_Timer) {
+			gos_linewin = win;
+			gos_linepending = 1;
+			gos_linebuf = buf;
+			return ZC_TIME_OUT;
+		} else if (ev.type == evtype_LineInput) {
+			break;
+		}
+	}
+	if (shouldQuit())
+		return 0;
+
+	glk_request_timer_events(0);
+	buf[ev.val1] = '\0';
+
+	if (gos_upper && mach_status_ht < curr_status_ht)
+		reset_status_ht();
+	curr_status_ht = 0;
+
+	return ZC_RETURN;
+}
+
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/glk_interface.h b/engines/gargoyle/frotz/glk_interface.h
index aeb33a8..5393c5d 100644
--- a/engines/gargoyle/frotz/glk_interface.h
+++ b/engines/gargoyle/frotz/glk_interface.h
@@ -159,6 +159,24 @@ protected:
 		// Not implemented
 		return 0;
 	}
+
+	void os_scrollback_char(zchar z) {
+		// Not implemented
+	}
+
+	void os_scrollback_erase(int amount) {
+		// Not implemented
+	}
+
+	/**
+	 * Waits for a keypress
+	 */
+	zchar os_read_key(int timeout, bool show_cursor);
+
+	/**
+	 * Waits for the user to type an input line
+	 */
+	zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued);
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index 0a9d0ef..6a28121 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -309,6 +309,16 @@ private:
 	 */
 
 	/**
+	 * Waits for the user to type an input line
+	 */
+	zchar console_read_input(int max, zchar *buf, zword timeout, bool continued);
+
+	/**
+	 * Waits for a keypress
+	 */
+	zchar console_read_key(zword timeout);
+
+	/**
 	 * Write a single character to the scrollback buffer.
 	 *
 	 */
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
index 97784c8..d75819e 100644
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -25,12 +25,13 @@
 namespace Gargoyle {
 namespace Frotz {
 
-// TODO: Implement method stubs
-static void os_scrollback_char(zchar) {}
-static void os_scrollback_erase(zword) {}
-static zchar console_read_key(zword) { return 0; }
-static zchar console_read_input(uint, zchar *, uint, bool) { return 0; }
+zchar Processor::console_read_input(int max, zchar *buf, zword timeout, bool continued) {
+	return os_read_line(max, buf, timeout, max, continued);
+}
 
+zchar Processor::console_read_key(zword timeout) {
+	return os_read_key(timeout, 0);
+}
 
 void Processor::scrollback_char(zchar c) {
     if (c == ZC_INDENT)


Commit: c19d40fa781a414560341a1d357b64875791f17f
    https://github.com/scummvm/scummvm/commit/c19d40fa781a414560341a1d357b64875791f17f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Implemented Quetzal class for savegames

Changed paths:
  A engines/gargoyle/frotz/quetzal.cpp
  A engines/gargoyle/frotz/quetzal.h
    engines/gargoyle/frotz/err.h
    engines/gargoyle/frotz/mem.cpp
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_streams.cpp
    engines/gargoyle/gargoyle.h
    engines/gargoyle/module.mk
    engines/gargoyle/streams.cpp


diff --git a/engines/gargoyle/frotz/err.h b/engines/gargoyle/frotz/err.h
index e71c988..090ce3f 100644
--- a/engines/gargoyle/frotz/err.h
+++ b/engines/gargoyle/frotz/err.h
@@ -96,7 +96,7 @@ protected:
 	/**
 	 * Get the PC. Is implemented by the Processor class, which derives from Errors
 	 */
-	virtual zword getPC() const = 0;
+	virtual uint getPC() const = 0;
 public:
 	/**
 	 * Constructor
diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
index ad60b89..e327fba 100644
--- a/engines/gargoyle/frotz/mem.cpp
+++ b/engines/gargoyle/frotz/mem.cpp
@@ -129,14 +129,11 @@ void Mem::initialize() {
 
 	// Load story file in chunks of 32KB
 	uint n = 0x8000;
-
 	for (uint size = 64; size < story_size; size += n) {
 		if (story_size - size < 0x8000)
 			n = story_size - size;
 
-		SET_PC(size);
-
-		if (story_fp->read(pcp, n) != n)
+		if (story_fp->read(zmp + size, n) != n)
 			error("Story file read error");
 	}
 
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index 6a28121..0f7ecc0 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -32,25 +32,27 @@
 namespace Gargoyle {
 namespace Frotz {
 
-#define CODE_BYTE(v)	   v = *pcp++
-#define CODE_WORD(v)       v = READ_BE_UINT16(pcp += 2)
-#define CODE_IDX_WORD(v,i) v = READ_BE_UINT16(pcp + i)
-#define GET_PC(v)          v = pcp - zmp
-#define SET_PC(v)          pcp = zmp + v
-
 #define TEXT_BUFFER_SIZE 200
 
+#define CODE_BYTE(v)	   v = codeByte()
+#define CODE_WORD(v)       v = codeWord()
+#define CODE_IDX_WORD(v,i) v = codeWordIdx(i)
+#define GET_PC(v)          v = getPC()
+#define SET_PC(v)          setPC(v)
+
 enum string_type {
 	LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY
 };
 
 class Processor;
+class Quetzal;
 typedef void (Processor::*Opcode)();
 
 /**
  * Zcode processor
  */
 class Processor : public Errors, public GlkInterface, public virtual Mem {
+	friend class Quetzal;
 private:
 	int _finished;
 	zword zargs[8];
@@ -1510,11 +1512,6 @@ protected:
 	void z_store();
 
 	/**@}*/
-protected:
-	/**
-	 * Get the PC. Is implemented by the Processor class, which derives from Errors
-	 */
-	virtual zword getPC() const { return pcp - zmp; }
 public:
 	/**
 	 * Constructor
@@ -1530,6 +1527,50 @@ public:
 	 * Z-code interpreter main loop
 	 */
 	void interpret();
+
+	/**
+	 * \defgroup Memory access methods
+	 * @{
+	 */
+
+	/**
+	 * Square brackets operator
+	 */
+	zbyte &operator[](uint addr) { return zmp[addr]; }
+
+	/**
+	 * Read a code byte
+	 */
+	zbyte codeByte() { return *pcp++; }
+
+	/**
+	 * Read a code word
+	 */
+	zword codeWord() {
+		zword v = READ_BE_UINT16(pcp);
+		pcp += 2;
+		return v;
+	}
+
+	/**
+	 * Return a code word at a given address
+	 */
+	zword codeWordIdx(uint addr) const {
+		return READ_BE_UINT16(pcp + addr);
+	}
+
+	/**
+	 * Return the current program execution offset
+	 * @remarks		This virtual as a convenient way for the ancestor Err class to access
+	 */
+	virtual uint getPC() const override { return pcp - zmp; }
+
+	/**
+	 * Set the program execution offset
+	 */
+	void setPC(uint addr) { pcp = zmp + addr; }
+
+	 /**@}*/
 };
 
 } // End of namespace Frotz
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
index d75819e..50d6425 100644
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ b/engines/gargoyle/frotz/processor_streams.cpp
@@ -21,6 +21,7 @@
  */
 
 #include "gargoyle/frotz/processor.h"
+#include "gargoyle/frotz/quetzal.h"
 
 namespace Gargoyle {
 namespace Frotz {
@@ -574,7 +575,7 @@ void Processor::z_save() {
 		if ((gfp = frotzopenprompt (FILE_SAVE)) == nullptr)
 			goto finished;
 
-		if (option_save_quetzal) {
+		if (_save_quetzal) {
 			success = save_quetzal (gfp, story_fp, blorb_ofs);
 		} else {
 			/* Write game file */
@@ -670,7 +671,7 @@ void Processor::z_restore() {
 		if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
 			goto finished;
 
-		if (option_save_quetzal) {
+		if (_save_quetzal) {
 			success = restore_quetzal (gfp, story_fp, blorb_ofs);
 
 		} else {
diff --git a/engines/gargoyle/frotz/quetzal.cpp b/engines/gargoyle/frotz/quetzal.cpp
new file mode 100644
index 0000000..183217a
--- /dev/null
+++ b/engines/gargoyle/frotz/quetzal.cpp
@@ -0,0 +1,485 @@
+/* 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 "gargoyle/frotz/quetzal.h"
+#include "gargoyle/frotz/processor.h"
+#include "common/memstream.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+/**
+ * Various parsing states within restoration.
+ */
+enum ParseState {
+	GOT_HEADER = 0x01,
+	GOT_STACK  = 0x02,
+	GOT_MEMORY = 0x04,
+	GOT_NONE   = 0x00,
+	GOT_ALL    = 0x07,
+	GOT_ERROR  = 0x80
+};
+
+
+bool Quetzal::read_word(Common::ReadStream *f, zword *result) {
+	*result = f->readUint16BE();
+	return true;
+}
+
+bool Quetzal::read_long(Common::ReadStream *f, uint *result) {
+	*result = f->readUint32BE();
+	return true;
+}
+
+bool Quetzal::save(Common::WriteStream *svf, Processor *proc) {
+	Processor &p = *proc;
+	uint ifzslen = 0, cmemlen = 0, stkslen = 0;
+	uint pc;
+	zword i, j, n;
+	zword nvars, nargs, nstk;
+	zbyte var;
+	long cmempos, stkspos;
+	int c;
+
+	// Set a temporary memory stream for writing out the data. This is needed, since we need to
+	// do some seeking within it at the end to fill out totals before properly writing it all out
+	Common::MemoryWriteStreamDynamic saveData(DisposeAfterUse::YES);
+	_out = &saveData;
+
+	// Write `IFZS' header.
+	write_chnk(ID_FORM, 0);
+	write_long(ID_IFZS);
+
+	// Write `IFhd' chunk
+	pc = p.getPC();
+	write_chnk(ID_IFhd, 13);
+	write_word(p.h_release);
+	for (i = H_SERIAL; i<H_SERIAL + 6; ++i)
+		write_byte(p[i]);
+
+	write_word(p.h_checksum);
+	write_long(pc << 8);		// Includes pad
+
+	// Write `CMem' chunk.
+	cmempos = svf->pos();
+	write_chnk(ID_CMem, 0);
+	_storyFile->seek(_blorbOffset);
+
+	// j holds current run length.
+	for (i = 0, j = 0, cmemlen = 0; i < p.h_dynamic_size; ++i) {
+		c = _storyFile->readByte();
+		c ^= p[i];
+
+		if (c == 0) {
+			// It's a run of equal bytes
+			++j;
+		} else {
+			// Write out any run there may be.
+			if (j > 0) {
+				for (; j > 0x100; j -= 0x100) {
+					write_run(0xFF);
+					cmemlen += 2;
+				}
+				write_run(j - 1);
+				cmemlen += 2;
+				j = 0;
+			}
+
+			// Any runs are now written. Write this (nonzero) byte
+			write_byte((zbyte)c);
+			++cmemlen;
+		}
+	}
+
+	// Reached end of dynamic memory. We ignore any unwritten run there may be at this point.
+	if (cmemlen & 1)
+		// Chunk length must be even.
+		write_byte(0);
+
+	// Write `Stks' chunk. You are not expected to understand this. ;)
+	stkspos = _storyFile->pos();
+	write_chnk(ID_Stks, 0);
+
+	// We construct a list of frame indices, most recent first, in `frames'.
+	// These indices are the offsets into the `stack' array of the word before
+	// the first word pushed in each frame.
+	frames[0] = p._sp - p._stack;	// The frame we'd get by doing a call now.
+	for (i = p._fp - p._stack + 4, n = 0; i < STACK_SIZE + 4; i = p._stack[i - 3] + 5)
+		frames[++n] = i;
+
+	// All versions other than V6 can use evaluation stack outside a function
+	// context. We write a faked stack frame (most fields zero) to cater for this.
+	if (p.h_version != V6) {
+		for (i = 0; i < 6; ++i)
+			write_byte(0);
+		nstk = STACK_SIZE - frames[n];
+		write_word(nstk);
+		for (j = STACK_SIZE - 1; j >= frames[n]; --j)
+			write_word(p._stack[j]);
+		stkslen = 8 + 2 * nstk;
+	}
+
+	// Write out the rest of the stack frames.
+	for (i = n; i > 0; --i) {
+		zword *pf = p._stack + frames[i] - 4;	// Points to call frame
+		nvars = (pf[0] & 0x0F00) >> 8;
+		nargs = pf[0] & 0x00FF;
+		nstk = frames[i] - frames[i - 1] - nvars - 4;
+		pc = ((uint)pf[3] << 9) | pf[2];
+
+		// Check type of call
+		switch (pf[0] & 0xF000)	{
+		case 0x0000:
+			// Function
+			var = p[pc];
+			pc = ((pc + 1) << 8) | nvars;
+			break;
+
+		case 0x1000:
+			// Procedure
+			var = 0;
+			pc = (pc << 8) | 0x10 | nvars;	// Set procedure flag
+			break;
+
+		default:
+			p.runtimeError(ERR_SAVE_IN_INTER);
+			return 0;
+		}
+		if (nargs != 0)
+			nargs = (1 << nargs) - 1;	// Make args into bitmap
+
+		// Write the main part of the frame...
+		write_long(pc);
+		write_byte(var);
+		write_byte(nargs);
+		write_word(nstk);
+
+		// Write the variables and eval stack
+		for (j = 0, --pf; j<nvars + nstk; ++j, --pf)
+			write_word(*pf);
+
+		// Calculate length written thus far
+		stkslen += 8 + 2 * (nvars + nstk);
+	}
+
+	// Fill in variable chunk lengths
+	ifzslen = 3 * 8 + 4 + 14 + cmemlen + stkslen;
+	if (cmemlen & 1)
+		++ifzslen;
+
+	saveData.seek(4);
+	saveData.writeUint32BE(ifzslen);
+	saveData.seek(cmempos + 4);
+	saveData.writeUint32BE(cmemlen);
+	saveData.seek(stkspos + 4);
+	saveData.writeUint32BE(stkslen);
+
+	// Write the save data out
+	svf->write(saveData.getData(), saveData.size());
+
+	// After all that, still nothing went wrong!
+	return true;
+}
+
+
+int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
+	Processor &p = *proc;
+	uint ifzslen, currlen, tmpl;
+	uint pc;
+	zword i, tmpw;
+	int fatal = 0;	// Set to -1 when errors must be fatal.
+	zbyte skip, progress = GOT_NONE;
+	int x, y;
+
+	// Check it's really an `IFZS' file.
+	tmpl = svf->readUint32BE();
+	ifzslen = svf->readUint32BE();
+	currlen = svf->readUint32BE();
+	if (tmpl != ID_FORM || currlen != ID_IFZS) {
+		p.print_string("This is not a saved game file!\n");
+		return 0;
+	}
+	if ((ifzslen & 1) || ifzslen<4)
+		// Sanity checks
+		return 0;
+	ifzslen -= 4;
+
+	// Read each chunk and process it
+	while (ifzslen > 0) {
+		// Read chunk header
+		if (ifzslen < 8)
+			// Couldn't contain a chunk
+			return 0;
+
+		tmpl = svf->readUint32BE();
+		currlen = svf->readUint32BE();
+		ifzslen -= 8;	// Reduce remaining by size of header
+
+		// Handle chunk body
+		if (ifzslen < currlen)
+			// Chunk goes past EOF?!
+			return 0;
+		skip = currlen & 1;
+		ifzslen -= currlen + (uint)skip;
+
+		switch (tmpl) {
+		// `IFhd' header chunk; must be first in file
+		case ID_IFhd:
+			if (progress & GOT_HEADER) {
+				p.print_string("Save file has two IFZS chunks!\n");
+				return fatal;
+			}
+			progress |= GOT_HEADER;
+			if (currlen < 13)
+				return fatal;
+
+			tmpw = svf->readUint16BE();
+			if (tmpw != p.h_release)
+				progress = GOT_ERROR;
+
+			for (i = H_SERIAL; i < H_SERIAL + 6; ++i) {
+				x = svf->readByte();
+				if (x != p[i])
+					progress = GOT_ERROR;
+			}
+
+			tmpw = svf->readUint16BE();
+			if (tmpw != p.h_checksum)
+				progress = GOT_ERROR;
+
+			if (progress & GOT_ERROR) {
+				p.print_string("File was not saved from this story!\n");
+				return fatal;
+			}
+
+			x = svf->readByte();
+			pc = (uint)x << 16;
+			x = svf->readByte();
+			pc |= (uint)x << 8;
+			x = svf->readByte();
+			pc |= (uint)x;
+
+			fatal = -1;		// Setting PC means errors must be fatal
+			p.setPC(pc);
+
+			svf->skip(13);	// Skip rest of chunk
+			break;
+
+		// `Stks' stacks chunk; restoring this is quite complex. ;)
+		case ID_Stks:
+			if (progress & GOT_STACK) {
+				p.print_string("File contains two stack chunks!\n");
+				break;
+			}
+			progress |= GOT_STACK;
+
+			fatal = -1;		// Setting SP means errors must be fatal
+			p._sp = p._stack + STACK_SIZE;
+
+			// All versions other than V6 may use evaluation stack outside any function context.
+			// As a result a faked function context will be present in the file here. We skip
+			// this context, but load the associated stack onto the stack proper...
+			if (p.h_version != V6) {
+				if (currlen < 8)
+					return fatal;
+
+				svf->skip(6);
+				tmpw = svf->readUint16BE();
+
+				if (tmpw > STACK_SIZE) {
+					p.print_string("Save-file has too much stack (and I can't cope).\n");
+					return fatal;
+				}
+
+				currlen -= 8;
+				if (currlen < (uint)tmpw * 2)
+					return fatal;
+				for (i = 0; i < tmpw; ++i)
+					*--p._sp = svf->readUint16BE();
+				currlen -= tmpw * 2;
+			}
+
+			// We now proceed to load the main block of stack frames
+			for (p._fp = p._stack + STACK_SIZE, p._frameCount = 0;
+					currlen > 0; currlen -= 8, ++p._frameCount) {
+				if (currlen < 8)				return fatal;
+				if (p._sp - p._stack < 4) {
+					// No space for frame
+					p.print_string("Save-file has too much stack (and I can't cope).\n");
+					return fatal;
+				}
+
+				// Read PC, procedure flag and formal param count
+				tmpl = svf->readUint32BE();
+				y = (int)(tmpl & 0x0F);		// Number of formals
+				tmpw = y << 8;
+
+				// Read result variable
+				x = svf->readByte();
+
+				// Check the procedure flag...
+				if (tmpl & 0x10) {
+					tmpw |= 0x1000;		// It's a procedure
+					tmpl >>= 8;			// Shift to get PC value
+				} else {
+					// Functions have type 0, so no need to or anything
+					tmpl >>= 8;			// Shift to get PC value
+					--tmpl;				// Point at result byte. */
+
+					// Sanity check on result variable...
+					if (p[tmpl] != (zbyte)x) {
+						p.print_string("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
+						return fatal;
+					}
+				}
+
+				*--p._sp = (zword)(tmpl >> 9);		// High part of PC
+				*--p._sp = (zword)(tmpl & 0x1FF);	// Low part of PC
+				*--p._sp = (zword)(p._fp - p._stack - 1);	// FP
+
+				// Read and process argument mask
+				x = svf->readByte();
+				++x;		// Should now be a power of 2
+				for (i = 0; i<8; ++i)
+					if (x & (1 << i))
+						break;
+				if (x ^ (1 << i)) {
+					// Not a power of 2
+					p.print_string("Save-file uses incomplete argument lists (which I can't handle)\n");
+					return fatal;
+				}
+
+				*--p._sp = tmpw | i;
+				p._fp = p._sp;	// FP for next frame
+
+				// Read amount of eval stack used
+				tmpw = svf->readUint16BE();
+
+				tmpw += y;	// Amount of stack + number of locals
+				if (p._sp - p._stack <= tmpw) {
+					p.print_string("Save-file has too much stack (and I can't cope).\n");
+					return fatal;
+				}
+				if (currlen < (uint)tmpw * 2)
+					return fatal;
+				
+				for (i = 0; i < tmpw; ++i)
+					--*p._sp = svf->readUint16BE();
+				currlen -= tmpw * 2;
+			}
+
+			// End of `Stks' processing...
+			break;
+
+		// Any more special chunk types must go in HERE or ABOVE
+		// `CMem' compressed memory chunk; uncompress it
+		case ID_CMem:
+			if (!(progress & GOT_MEMORY)) {
+				// Don't complain if two
+				_storyFile->seek(_blorbOffset);
+				
+				i = 0;	// Bytes written to data area
+				for (; currlen > 0; --currlen) {
+					x = svf->readByte();
+					if (x == 0) {
+						// Start of run
+						// Check for bogus run
+						if (currlen < 2) {
+							p.print_string("File contains bogus `CMem' chunk.\n");
+							svf->skip(currlen);
+
+							currlen = 1;
+							i = 0xFFFF;
+							break; // Keep going; may be a `UMem' too
+						}
+
+						// Copy story file to memory during the run
+						--currlen;
+						x = svf->readByte();
+						for (; x >= 0 && i < p.h_dynamic_size; --x, ++i)
+							p[i] = svf->readByte();
+					} else {
+						// Not a run
+						y = svf->readByte();
+						p[i] = (zbyte)(x ^ y);
+						++i;
+					}
+
+					// Make sure we don't load too much
+					if (i > p.h_dynamic_size) {
+						p.print_string("warning: `CMem' chunk too long!\n");
+						svf->skip(currlen);
+						break;	// Keep going; there may be a `UMem' too
+					}
+				}
+
+				// If chunk is short, assume a run
+				for (; i < p.h_dynamic_size; ++i)
+					p[i] = svf->readByte();
+
+				if (currlen == 0)
+					progress |= GOT_MEMORY;		// Only if succeeded
+				break;
+			}
+
+			// Intentional fall-through
+
+		case ID_UMem:
+			if (!(progress & GOT_MEMORY)) {
+				// Must be exactly the right size
+				if (currlen == p.h_dynamic_size) {
+					if (svf->read(p.zmp, currlen) == currlen) {
+						progress |= GOT_MEMORY;	// Only on success
+						break;
+					}
+				} else {
+					p.print_string("`UMem' chunk wrong size!\n");
+				}
+				
+				// Fall into default action (skip chunk) on errors
+			}
+
+			// Intentional fall-through
+
+		default:
+			svf->seek(currlen, SEEK_CUR);		// Skip chunk
+			break;
+		}
+
+		if (skip)
+			svf->skip(1);						// Skip pad byte
+	}
+
+	// We've reached the end of the file. For the restoration to have been a
+	// success, we must have had one of each of the required chunks.
+	if (!(progress & GOT_HEADER))
+		p.print_string("error: no valid header (`IFhd') chunk in file.\n");
+	if (!(progress & GOT_STACK))
+		p.print_string("error: no valid stack (`Stks') chunk in file.\n");
+	if (!(progress & GOT_MEMORY))
+		p.print_string("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
+
+	return (progress == GOT_ALL ? 2 : fatal);
+}
+
+} // End of namespace Scott
+} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/quetzal.h b/engines/gargoyle/frotz/quetzal.h
new file mode 100644
index 0000000..6975155
--- /dev/null
+++ b/engines/gargoyle/frotz/quetzal.h
@@ -0,0 +1,97 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 GARGOYLE_FROTZ_QUETZAL
+#define GARGOYLE_FROTZ_QUETZAL
+
+#include "gargoyle/glk_types.h"
+#include "gargoyle/frotz/frotz_types.h"
+
+namespace Gargoyle {
+namespace Frotz {
+
+enum QueztalTag {
+	ID_FORM = MKTAG('F', 'O', 'R', 'M'),
+	ID_IFZS = MKTAG('I', 'F', 'Z', 'S'),
+	ID_IFhd = MKTAG('I', 'F', 'h', 'd'),
+	ID_UMem = MKTAG('U', 'M', 'e', 'm'),
+	ID_CMem = MKTAG('C', 'M', 'e', 'm'),
+	ID_Stks = MKTAG('S', 't', 'k', 's'),
+	ID_ANNO = MKTAG('A', 'N', 'N', 'O')
+};
+
+class Processor;
+
+class Quetzal {
+private:
+	Common::SeekableReadStream *_storyFile;
+	Common::WriteStream *_out;
+	size_t _blorbOffset;
+	int _slot;
+	zword frames[STACK_SIZE / 4 + 1];
+private:
+	/**
+	 * Read a 16-bit value from the file
+	 */
+	bool read_word(Common::ReadStream *f, zword *result);
+
+	/**
+	 * Read  32-bit value from the file
+	 */
+	bool read_long(Common::ReadStream *f, uint *result);
+
+	void write_byte(zbyte b) { _out->writeByte(b); }
+	void write_bytx(zword b) { _out->writeByte(b & 0xFF); }
+	void write_word(zword w) { _out->writeUint16BE(w); }
+	void write_long(uint l) { _out->writeUint32BE(l); }
+	void write_run(zword run) { _out->writeUint16LE(run); }
+	void write_chnk(QueztalTag id, zword len) {
+		_out->writeUint32BE(id);
+		_out->writeUint32BE(len);
+	}
+public:
+	/**
+	 * Constructor
+	 */
+	Quetzal(Common::SeekableReadStream *storyFile, size_t blorbOffset, int slot) :
+		_storyFile(storyFile), _blorbOffset(blorbOffset), _slot(slot) {}
+
+	/*
+	 * Save a game using Quetzal format.
+	 * @param svf	Savegame file
+	 * @returns		Returns true if OK, false if failed
+	 */
+	bool save(Common::WriteStream *svf, Processor *proc);
+
+	/**
+	 * Restore a saved game using Quetzal format
+	 * @param svf	Savegame file
+	 * @returns		Return 2 if OK, 0 if an error occurred before any damage was done,
+	 *				-1 on a fatal error
+	 */
+	int restore(Common::SeekableReadStream *svf, Processor *proc);
+};
+
+} // End of namespace Frotz
+} // End of namespace Gargoyle
+
+#endif
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index aeaa0c0..676708f 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -174,6 +174,13 @@ public:
 	}
 
 	/**
+	 * Return the filename for a given save slot
+	 */
+	Common::String getSaveName(uint slot) const {
+		return Common::String::format("%s.%.3u", getTargetName().c_str(), slot);
+	}
+
+	/**
 	 * Display a message in a GUI dialog
 	 */
 	void GUIError(const char *msg, ...);
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 14cc048..4694449 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -38,6 +38,7 @@ MODULE_OBJS := \
 	frotz/processor_table.o \
 	frotz/processor_text.o \
 	frotz/processor_variables.o \
+	frotz/quetzal.o \
 	scott/detection.o \
 	scott/scott.o
 
diff --git a/engines/gargoyle/streams.cpp b/engines/gargoyle/streams.cpp
index d5671f1..6a87084 100644
--- a/engines/gargoyle/streams.cpp
+++ b/engines/gargoyle/streams.cpp
@@ -1563,7 +1563,7 @@ frefid_t Streams::iterate(frefid_t fref, glui32 *rock) {
 
 const Common::String FileReference::getSaveName() const {
 	assert(_slotNumber != -1);
-	return Common::String::format("%s.%.3d", g_vm->getTargetName().c_str(), _slotNumber);
+	return g_vm->getSaveName(_slotNumber);
 }
 
 bool FileReference::exists() const {


Commit: 6b23a72e517bce458246b33b6ead2a9d90afc258
    https://github.com/scummvm/scummvm/commit/6b23a72e517bce458246b33b6ead2a9d90afc258
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Merge Err class into Processor

Changed paths:
  R engines/gargoyle/frotz/err.cpp
  R engines/gargoyle/frotz/err.h
    engines/gargoyle/frotz/frotz_types.h
    engines/gargoyle/frotz/mem.h
    engines/gargoyle/frotz/processor.cpp
    engines/gargoyle/frotz/processor.h
    engines/gargoyle/frotz/processor_buffer.cpp
    engines/gargoyle/frotz/processor_text.cpp
    engines/gargoyle/module.mk


diff --git a/engines/gargoyle/frotz/err.cpp b/engines/gargoyle/frotz/err.cpp
deleted file mode 100644
index f75b764..0000000
--- a/engines/gargoyle/frotz/err.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/err.h"
-#include "gargoyle/frotz/frotz.h"
-#include "common/textconsole.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-const char *const Errors::ERR_MESSAGES[ERR_NUM_ERRORS] = {
-    "Text buffer overflow",
-    "Store out of dynamic memory",
-    "Division by zero",
-    "Illegal object",
-    "Illegal attribute",
-    "No such property",
-    "Stack overflow",
-    "Call to illegal address",
-    "Call to non-routine",
-    "Stack underflow",
-    "Illegal opcode",
-    "Bad stack frame",
-    "Jump to illegal address",
-    "Can't save while in interrupt",
-    "Nesting stream #3 too deep",
-    "Illegal window",
-    "Illegal window property",
-    "Print at illegal address",
-    "Illegal dictionary word length",
-    "@jin called with object 0",
-    "@get_child called with object 0",
-    "@get_parent called with object 0",
-    "@get_sibling called with object 0",
-    "@get_prop_addr called with object 0",
-    "@get_prop called with object 0",
-    "@put_prop called with object 0",
-    "@clear_attr called with object 0",
-    "@set_attr called with object 0",
-    "@test_attr called with object 0",
-    "@move_object called moving object 0",
-    "@move_object called moving into object 0",
-    "@remove_object called with object 0",
-    "@get_next_prop called with object 0"
-};
-
-Errors::Errors() {
-	Common::fill(&_count[0], &_count[ERR_NUM_ERRORS], 0);
-}
-
-void Errors::runtimeError(ErrorCode errNum) {
-#ifdef TODO
-	int wasfirst;
-    
-    if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
-	return;
-
-    if (g_vm->_err_report_mode == ERR_REPORT_FATAL
-		|| (!g_vm->_ignore_errors && errNum <= ERR_MAX_FATAL)) {
-		g_vm->_buffer.flush();
-		error(ERR_MESSAGES[errNum - 1]);
-		return;
-    }
-
-    wasfirst = (_count[errNum - 1] == 0);
-    _count[errNum - 1]++;
-    
-    if ((g_vm->_err_report_mode == ERR_REPORT_ALWAYS)
-			|| (_err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
-		long pc = g_vm->_processor
-		GET_PC(pc);
-		printString("Warning: ");
-		printString(ERR_MESSAGES[errNum - 1]);
-		printString(" (PC = ");
-		printLong(pc, 16);
-		printChar(')');
-        
-		if (_err_report_mode == ERR_REPORT_ONCE) {
-			printString(" (will ignore further occurrences)");
-		} else {
-			printString(" (occurence ");
-			printLong(_count[errNum - 1], 10);
-			printChar(')');
-		}
-
-		newLine();
-    }
-#endif
-}
-
-void Errors::printLong(uint value, int base) {
-    unsigned long i;
-    char c;
-
-    for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base)
-	if (value >= i || i == 1) {
-	    c = (value / i) % base;
-	    printChar(c + (c <= 9 ? '0' : 'a' - 10));
-	}
-}
-
-void Errors::printChar(const char c) {
-	// TODO
-}
-
-void Errors::printString(const char *str) {
-	// TODO
-}
-
-void Errors::newLine() {
-	// TODO
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/err.h b/engines/gargoyle/frotz/err.h
deleted file mode 100644
index 090ce3f..0000000
--- a/engines/gargoyle/frotz/err.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FROTZ_ERR
-#define GARGOYLE_FROTZ_ERR
-
-#include "gargoyle/frotz/frotz_types.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-enum ErrorCode {
-	ERR_TEXT_BUF_OVF   = 1,		///< Text buffer overflow
-	ERR_STORE_RANGE    = 2,		///< Store out of dynamic memory
-	ERR_DIV_ZERO       = 3,		///< Division by zero
-	ERR_ILL_OBJ        = 4,		///< Illegal object
-	ERR_ILL_ATTR       = 5,		///< Illegal attribute
-	ERR_NO_PROP        = 6,		///< No such property
-	ERR_STK_OVF        = 7,		///< Stack overflow
-	ERR_ILL_CALL_ADDR  = 8,		///< Call to illegal address
-	ERR_CALL_NON_RTN   = 9,		///< Call to non-routine
-	ERR_STK_UNDF       = 10,	///< Stack underflow
-	ERR_ILL_OPCODE     = 11,	///< Illegal opcode
-	ERR_BAD_FRAME      = 12,	///< Bad stack frame
-	ERR_ILL_JUMP_ADDR  = 13,	///< Jump to illegal address
-	ERR_SAVE_IN_INTER  = 14,	///< Can't save while in interrupt
-	ERR_STR3_NESTING   = 15,	///< Nesting stream #3 too deep
-	ERR_ILL_WIN        = 16,	///< Illegal window
-	ERR_ILL_WIN_PROP   = 17,	///< Illegal window property
-	ERR_ILL_PRINT_ADDR = 18,	///< Print at illegal address
-	ERR_DICT_LEN       = 19,	///< Illegal dictionary word length
-	ERR_MAX_FATAL      = 19,
-
-	// Less serious errors
-	ERR_JIN_0            = 20,	///< @jin called with object 0
-	ERR_GET_CHILD_0      = 21,	///< @get_child called with object 0
-	ERR_GET_PARENT_0     = 22,	///< @get_parent called with object 0
-	ERR_GET_SIBLING_0    = 23,	///< @get_sibling called with object 0
-	ERR_GET_PROP_ADDR_0  = 24,	///< @get_prop_addr called with object 0
-	ERR_GET_PROP_0       = 25,	///< @get_prop called with object 0
-	ERR_PUT_PROP_0       = 26,	///< @put_prop called with object 0
-	ERR_CLEAR_ATTR_0     = 27,	///< @clear_attr called with object 0
-	ERR_SET_ATTR_0       = 28,	///< @set_attr called with object 0
-	ERR_TEST_ATTR_0      = 29,	///< @test_attr called with object 0
-	ERR_MOVE_OBJECT_0    = 30,	///< @move_object called moving object 0
-	ERR_MOVE_OBJECT_TO_0 = 31,	///< @move_object called moving into object 0
-	ERR_REMOVE_OBJECT_0  = 32,	///< @remove_object called with object 0
-	ERR_GET_NEXT_PROP_0  = 33,	///< @get_next_prop called with object 0
-	ERR_NUM_ERRORS       = 33
-};
-
-class Errors {
-private:
-	static const char *const ERR_MESSAGES[ERR_NUM_ERRORS];
-	int _count[ERR_NUM_ERRORS];
-private:
-	/**
-	 * Print an unsigned 32bit number in decimal or hex.
-	 */
-	void printLong(uint value, int base);
-
-	/**
-	 * Print a character
-	 */
-	void printChar(const char c);
-
-	/**
-	 * Print a string
-	 */
-	void printString(const char *str);
-
-	/**
-	 * Add a newline
-	 */
-	void newLine();
-protected:
-	/**
-	 * Get the PC. Is implemented by the Processor class, which derives from Errors
-	 */
-	virtual uint getPC() const = 0;
-public:
-	/**
-	 * Constructor
-	 */
-	Errors();
-
-	/**
-	 * An error has occurred. Ignore it, pass it to os_fatal or report
-	 * it according to err_report_mode.
-	 * @param errNum		Numeric code for error (1 to ERR_NUM_ERRORS)
-	 */
-	void runtimeError(ErrorCode errNum);
-};
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index 3ccaa45..6efd0fc 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -149,19 +149,59 @@ enum {
 	TRANSPARENT_FLAG = 0x0001 ///< Game wants to use transparency     - V6
 };
 
+enum ErrorCode {
+	ERR_TEXT_BUF_OVF   = 1,		///< Text buffer overflow
+	ERR_STORE_RANGE    = 2,		///< Store out of dynamic memory
+	ERR_DIV_ZERO       = 3,		///< Division by zero
+	ERR_ILL_OBJ        = 4,		///< Illegal object
+	ERR_ILL_ATTR       = 5,		///< Illegal attribute
+	ERR_NO_PROP        = 6,		///< No such property
+	ERR_STK_OVF        = 7,		///< Stack overflow
+	ERR_ILL_CALL_ADDR  = 8,		///< Call to illegal address
+	ERR_CALL_NON_RTN   = 9,		///< Call to non-routine
+	ERR_STK_UNDF       = 10,	///< Stack underflow
+	ERR_ILL_OPCODE     = 11,	///< Illegal opcode
+	ERR_BAD_FRAME      = 12,	///< Bad stack frame
+	ERR_ILL_JUMP_ADDR  = 13,	///< Jump to illegal address
+	ERR_SAVE_IN_INTER  = 14,	///< Can't save while in interrupt
+	ERR_STR3_NESTING   = 15,	///< Nesting stream #3 too deep
+	ERR_ILL_WIN        = 16,	///< Illegal window
+	ERR_ILL_WIN_PROP   = 17,	///< Illegal window property
+	ERR_ILL_PRINT_ADDR = 18,	///< Print at illegal address
+	ERR_DICT_LEN       = 19,	///< Illegal dictionary word length
+	ERR_MAX_FATAL      = 19,
+
+	// Less serious errors
+	ERR_JIN_0            = 20,	///< @jin called with object 0
+	ERR_GET_CHILD_0      = 21,	///< @get_child called with object 0
+	ERR_GET_PARENT_0     = 22,	///< @get_parent called with object 0
+	ERR_GET_SIBLING_0    = 23,	///< @get_sibling called with object 0
+	ERR_GET_PROP_ADDR_0  = 24,	///< @get_prop_addr called with object 0
+	ERR_GET_PROP_0       = 25,	///< @get_prop called with object 0
+	ERR_PUT_PROP_0       = 26,	///< @put_prop called with object 0
+	ERR_CLEAR_ATTR_0     = 27,	///< @clear_attr called with object 0
+	ERR_SET_ATTR_0       = 28,	///< @set_attr called with object 0
+	ERR_TEST_ATTR_0      = 29,	///< @test_attr called with object 0
+	ERR_MOVE_OBJECT_0    = 30,	///< @move_object called moving object 0
+	ERR_MOVE_OBJECT_TO_0 = 31,	///< @move_object called moving into object 0
+	ERR_REMOVE_OBJECT_0  = 32,	///< @remove_object called with object 0
+	ERR_GET_NEXT_PROP_0  = 33,	///< @get_next_prop called with object 0
+	ERR_NUM_ERRORS       = 33
+};
+
 enum FrotzInterp {
-#define INTERP_DEFAULT 0
-#define INTERP_DEC_20 1
-#define INTERP_APPLE_IIE 2
-#define INTERP_MACINTOSH 3
-#define INTERP_AMIGA 4
-#define INTERP_ATARI_ST 5
-#define INTERP_MSDOS 6
-#define INTERP_CBM_128 7
-#define INTERP_CBM_64 8
-#define INTERP_APPLE_IIC 9
-#define INTERP_APPLE_IIGS 10
-#define INTERP_TANDY 11
+	INTERP_DEFAULT    =  0,
+	INTERP_DEC_20     =  1,
+	INTERP_APPLE_IIE  =  2,
+	INTERP_MACINTOSH  =  3,
+	INTERP_AMIGA      =  4,
+	INTERP_ATARI_ST   =  5,
+	INTERP_MSDOS      =  6,
+	INTERP_CBM_128    =  7,
+	INTERP_CBM_64     =  8,
+	INTERP_APPLE_IIC  =  9,
+	INTERP_APPLE_IIGS = 10,
+	INTERP_TANDY      = 11
 };
 
 enum Colour {
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
index a45eec5..a2e0e34 100644
--- a/engines/gargoyle/frotz/mem.h
+++ b/engines/gargoyle/frotz/mem.h
@@ -24,7 +24,6 @@
 #define GARGOYLE_FROTZ_MEM
 
 #include "gargoyle/frotz/frotz_types.h"
-#include "gargoyle/frotz/err.h"
 
 namespace Gargoyle {
 namespace Frotz {
diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
index ac18a23..3336aae 100644
--- a/engines/gargoyle/frotz/processor.cpp
+++ b/engines/gargoyle/frotz/processor.cpp
@@ -132,7 +132,7 @@ Opcode Processor::ext_opcodes[64] = {
 };
 
 Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
-		GlkInterface(syst, gameDesc), Errors(),
+		GlkInterface(syst, gameDesc),
 		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
 		_randomInterval(0), _randomCtr(0), first_restart(true), script_valid(false),
@@ -180,6 +180,7 @@ Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 	Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
 	Common::fill(&zargs[0], &zargs[8], 0);
 	Common::fill(&_buffer[0], &_buffer[TEXT_BUFFER_SIZE], '\0');
+	Common::fill(&_errorCount[0], &_errorCount[ERR_NUM_ERRORS], 0);
 }
 
 void Processor::initialize() {
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
index 0f7ecc0..3ead900 100644
--- a/engines/gargoyle/frotz/processor.h
+++ b/engines/gargoyle/frotz/processor.h
@@ -23,7 +23,6 @@
 #ifndef GARGOYLE_FROTZ_PROCESSOR
 #define GARGOYLE_FROTZ_PROCESSOR
 
-#include "gargoyle/frotz/err.h"
 #include "gargoyle/frotz/mem.h"
 #include "gargoyle/frotz/glk_interface.h"
 #include "gargoyle/frotz/frotz_types.h"
@@ -51,9 +50,15 @@ typedef void (Processor::*Opcode)();
 /**
  * Zcode processor
  */
-class Processor : public Errors, public GlkInterface, public virtual Mem {
+class Processor : public GlkInterface, public virtual Mem {
 	friend class Quetzal;
 private:
+	Opcode op0_opcodes[16];
+	Opcode op1_opcodes[16];
+	static const char *const ERR_MESSAGES[ERR_NUM_ERRORS];
+	static Opcode var_opcodes[64];
+	static Opcode ext_opcodes[64];
+
 	int _finished;
 	zword zargs[8];
 	int zargc;
@@ -72,17 +77,13 @@ private:
 	static zchar ZSCII_TO_LATIN1[];
 	zchar *_decoded, *_encoded;
 	int _resolution;
+	int _errorCount[ERR_NUM_ERRORS];
 
 	// Buffer related fields
-	zchar _buffer[TEXT_BUFFER_SIZE];
-	size_t _bufPos;
 	bool _locked;
 	zchar _prevC;
-
-	Opcode op0_opcodes[16];
-	Opcode op1_opcodes[16];
-	static Opcode var_opcodes[64];
-	static Opcode ext_opcodes[64];
+	zchar _buffer[TEXT_BUFFER_SIZE];
+	size_t _bufPos;
 
 	// Stream related fields
 	int script_width;
@@ -172,6 +173,16 @@ private:
 	  */
 	void print_char(zchar c);
 
+	/**
+	 * Print a string of ASCII characters.
+	 */
+	void print_string(const char *s);
+
+	/**
+	 * Print an unsigned 32bit number in decimal or hex.
+	 */
+	void print_long(uint value, int base);
+
 	 /**
 	  * High level newline function.
 	  */
@@ -182,6 +193,13 @@ private:
 	 */
 	bool bufferEmpty() const { return !_bufPos; }
 
+	/**
+	 * An error has occurred. Ignore it, pass it to os_fatal or report
+	 * it according to err_report_mode.
+	 * @param errNum		Numeric code for error (1 to ERR_NUM_ERRORS)
+	 */
+	void runtimeError(ErrorCode errNum);
+
 	/**@}*/
 
 	/**
@@ -217,13 +235,6 @@ private:
 	 */
 
 	/**
-	 * Generates a runtime error
-	 */
-	virtual void runtimeError(ErrorCode errNum) override {
-		Errors::runtimeError(errNum);
-	}
-
-	/**
 	 * Called when the H_FLAGS field of the header has changed
 	 */
 	virtual void flagsChanged(zbyte value) override;
@@ -1395,11 +1406,6 @@ protected:
 	void z_print_ret();
 
 	/**
-	 * Print a string of ASCII characters.
-	 */
-	void print_string(const char *s);
-
-	/**
 	 * Print unicode character
 	 *
 	 * 	zargs[0] = Unicode
@@ -1561,9 +1567,8 @@ public:
 
 	/**
 	 * Return the current program execution offset
-	 * @remarks		This virtual as a convenient way for the ancestor Err class to access
 	 */
-	virtual uint getPC() const override { return pcp - zmp; }
+	uint getPC() const { return pcp - zmp; }
 
 	/**
 	 * Set the program execution offset
diff --git a/engines/gargoyle/frotz/processor_buffer.cpp b/engines/gargoyle/frotz/processor_buffer.cpp
index b2b6ccd..62980fb 100644
--- a/engines/gargoyle/frotz/processor_buffer.cpp
+++ b/engines/gargoyle/frotz/processor_buffer.cpp
@@ -27,6 +27,42 @@
 namespace Gargoyle {
 namespace Frotz {
 
+const char *const Processor::ERR_MESSAGES[ERR_NUM_ERRORS] = {
+    "Text buffer overflow",
+    "Store out of dynamic memory",
+    "Division by zero",
+    "Illegal object",
+    "Illegal attribute",
+    "No such property",
+    "Stack overflow",
+    "Call to illegal address",
+    "Call to non-routine",
+    "Stack underflow",
+    "Illegal opcode",
+    "Bad stack frame",
+    "Jump to illegal address",
+    "Can't save while in interrupt",
+    "Nesting stream #3 too deep",
+    "Illegal window",
+    "Illegal window property",
+    "Print at illegal address",
+    "Illegal dictionary word length",
+    "@jin called with object 0",
+    "@get_child called with object 0",
+    "@get_parent called with object 0",
+    "@get_sibling called with object 0",
+    "@get_prop_addr called with object 0",
+    "@get_prop called with object 0",
+    "@put_prop called with object 0",
+    "@clear_attr called with object 0",
+    "@set_attr called with object 0",
+    "@test_attr called with object 0",
+    "@move_object called moving object 0",
+    "@move_object called moving into object 0",
+    "@remove_object called with object 0",
+    "@get_next_prop called with object 0"
+};
+
 void Processor::flush_buffer() {
 	/* Make sure we stop when flush_buffer is called from flush_buffer.
 	 * Note that this is difficult to avoid as we might print a newline
@@ -85,10 +121,72 @@ void Processor::print_char(zchar c) {
 	}
 }
 
+void Processor::print_string(const char *s) {
+	char c;
+
+	while ((c = *s++) != 0) {
+		if (c == '\n')
+			new_line();
+		else
+			print_char(c);
+	}
+}
+
+void Processor::print_long(uint value, int base) {
+	unsigned long i;
+	char c;
+
+	for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base) {
+		if (value >= i || i == 1) {
+			c = (value / i) % base;
+			print_char(c + (c <= 9 ? '0' : 'a' - 10));
+		}
+	}
+}
+
 void Processor::new_line()  {
 	flush_buffer();
 	stream_new_line();
 }
 
+void Processor::runtimeError(ErrorCode errNum) {
+	int wasfirst;
+
+	if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
+		return;
+
+	if (_err_report_mode == ERR_REPORT_FATAL
+		|| (!_ignore_errors && errNum <= ERR_MAX_FATAL)) {
+		flush_buffer();
+		error(ERR_MESSAGES[errNum - 1]);
+		return;
+	}
+
+	wasfirst = (_errorCount[errNum - 1] == 0);
+	_errorCount[errNum - 1]++;
+
+	if ((_err_report_mode == ERR_REPORT_ALWAYS)
+		|| (_err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
+		long pc;
+		GET_PC(pc);
+		print_string("Warning: ");
+		print_string(ERR_MESSAGES[errNum - 1]);
+		print_string(" (PC = ");
+		print_long(pc, 16);
+		print_char(')');
+
+		if (_err_report_mode == ERR_REPORT_ONCE) {
+			print_string(" (will ignore further occurrences)");
+		}
+		else {
+			print_string(" (occurence ");
+			print_long(_errorCount[errNum - 1], 10);
+			print_char(')');
+		}
+
+		new_line();
+	}
+}
+
 } // End of namespace Scott
 } // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_text.cpp b/engines/gargoyle/frotz/processor_text.cpp
index 630bd50..0852dc4 100644
--- a/engines/gargoyle/frotz/processor_text.cpp
+++ b/engines/gargoyle/frotz/processor_text.cpp
@@ -479,17 +479,6 @@ void Processor::print_object(zword object) {
 	}
 }
 
-void Processor::print_string(const char *s) {
-	char c;
-
-	while ((c = *s++) != 0) {
-		if (c == '\n')
-			new_line();
-		else
-			print_char(c);
-	}
-}
-
 zword Processor::lookup_text(int padding, zword dct) {
 	zword entry_addr;
 	zword entry_count;
diff --git a/engines/gargoyle/module.mk b/engines/gargoyle/module.mk
index 4694449..3c09b32 100644
--- a/engines/gargoyle/module.mk
+++ b/engines/gargoyle/module.mk
@@ -23,7 +23,6 @@ MODULE_OBJS := \
 	window_text_grid.o \
 	frotz/detection.o \
 	frotz/detection_tables.o \
-	frotz/err.o \
 	frotz/frotz.o \
 	frotz/glk_interface.o \
 	frotz/mem.o \


Commit: 7b90a2b60ed09fdba081dd752b4167e0b0e1fd27
    https://github.com/scummvm/scummvm/commit/7b90a2b60ed09fdba081dd752b4167e0b0e1fd27
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Hook up processor execution to main runGame method

Changed paths:
    engines/gargoyle/frotz/frotz.cpp
    engines/gargoyle/frotz/frotz.h


diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
index 5c86189..3a7b913 100644
--- a/engines/gargoyle/frotz/frotz.cpp
+++ b/engines/gargoyle/frotz/frotz.cpp
@@ -34,11 +34,16 @@ Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) :
 	g_vm = this;
 }
 
+Frotz::~Frotz() {
+	reset_memory();
+}
+
 void Frotz::runGame(Common::SeekableReadStream *gameFile) {
 	story_fp = gameFile;
 	initialize();
 
-	// TODO
+	// Game loop
+	interpret();
 }
 
 void Frotz::initialize() {
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
index 78272f0..f7211f7 100644
--- a/engines/gargoyle/frotz/frotz.h
+++ b/engines/gargoyle/frotz/frotz.h
@@ -39,6 +39,11 @@ public:
 	Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc);
 
 	/**
+	 * Destructor
+	 */
+	virtual ~Frotz();
+
+	/**
 	 * Initialization
 	 */
 	void initialize();


Commit: 7d670ff157fbc3df45f70f9e7a5b537b3d13152b
    https://github.com/scummvm/scummvm/commit/7d670ff157fbc3df45f70f9e7a5b537b3d13152b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix stack size

Changed paths:
    engines/gargoyle/frotz/frotz_types.h


diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
index 6efd0fc..6b4fe67 100644
--- a/engines/gargoyle/frotz/frotz_types.h
+++ b/engines/gargoyle/frotz/frotz_types.h
@@ -30,7 +30,7 @@ namespace Gargoyle {
 namespace Frotz {
 
 #define MAX_UNDO_SLOTS 500
-#define STACK_SIZE 20
+#define STACK_SIZE 32768
 
 #define lo(v)	(v & 0xff)
 #define hi(v)	(v >> 8)


Commit: 1fb931fbd950324754536ee0b33ed0b91f68c9a2
    https://github.com/scummvm/scummvm/commit/1fb931fbd950324754536ee0b33ed0b91f68c9a2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Changing gargoyle folder to glk

Changed paths:
  A engines/glk/blorb.cpp
  A engines/glk/blorb.h
  A engines/glk/conf.cpp
  A engines/glk/conf.h
  A engines/glk/configure.engine
  A engines/glk/detection.cpp
  A engines/glk/detection_tables.h
  A engines/glk/events.cpp
  A engines/glk/events.h
  A engines/glk/fonts.cpp
  A engines/glk/fonts.h
  A engines/glk/frotz/detection.cpp
  A engines/glk/frotz/detection.h
  A engines/glk/frotz/detection_tables.cpp
  A engines/glk/frotz/detection_tables.h
  A engines/glk/frotz/frotz.cpp
  A engines/glk/frotz/frotz.h
  A engines/glk/frotz/frotz_types.h
  A engines/glk/frotz/glk_interface.cpp
  A engines/glk/frotz/glk_interface.h
  A engines/glk/frotz/mem.cpp
  A engines/glk/frotz/mem.h
  A engines/glk/frotz/processor.cpp
  A engines/glk/frotz/processor.h
  A engines/glk/frotz/processor_buffer.cpp
  A engines/glk/frotz/processor_input.cpp
  A engines/glk/frotz/processor_maths.cpp
  A engines/glk/frotz/processor_mem.cpp
  A engines/glk/frotz/processor_objects.cpp
  A engines/glk/frotz/processor_screen.cpp
  A engines/glk/frotz/processor_streams.cpp
  A engines/glk/frotz/processor_table.cpp
  A engines/glk/frotz/processor_text.cpp
  A engines/glk/frotz/processor_variables.cpp
  A engines/glk/frotz/quetzal.cpp
  A engines/glk/frotz/quetzal.h
  A engines/glk/gargoyle.cpp
  A engines/glk/gargoyle.h
  A engines/glk/glk.cpp
  A engines/glk/glk.h
  A engines/glk/glk_types.h
  A engines/glk/module.mk
  A engines/glk/picture.cpp
  A engines/glk/picture.h
  A engines/glk/scott/detection.cpp
  A engines/glk/scott/detection.h
  A engines/glk/scott/scott.cpp
  A engines/glk/scott/scott.h
  A engines/glk/screen.cpp
  A engines/glk/screen.h
  A engines/glk/selection.cpp
  A engines/glk/selection.h
  A engines/glk/speech.h
  A engines/glk/streams.cpp
  A engines/glk/streams.h
  A engines/glk/time.cpp
  A engines/glk/time.h
  A engines/glk/unicode.cpp
  A engines/glk/unicode.h
  A engines/glk/unicode_gen.cpp
  A engines/glk/unicode_gen.h
  A engines/glk/utils.cpp
  A engines/glk/utils.h
  A engines/glk/window_graphics.cpp
  A engines/glk/window_graphics.h
  A engines/glk/window_pair.cpp
  A engines/glk/window_pair.h
  A engines/glk/window_text_buffer.cpp
  A engines/glk/window_text_buffer.h
  A engines/glk/window_text_grid.cpp
  A engines/glk/window_text_grid.h
  A engines/glk/windows.cpp
  A engines/glk/windows.h
  R engines/gargoyle/blorb.cpp
  R engines/gargoyle/blorb.h
  R engines/gargoyle/conf.cpp
  R engines/gargoyle/conf.h
  R engines/gargoyle/configure.engine
  R engines/gargoyle/detection.cpp
  R engines/gargoyle/detection_tables.h
  R engines/gargoyle/events.cpp
  R engines/gargoyle/events.h
  R engines/gargoyle/fonts.cpp
  R engines/gargoyle/fonts.h
  R engines/gargoyle/frotz/detection.cpp
  R engines/gargoyle/frotz/detection.h
  R engines/gargoyle/frotz/detection_tables.cpp
  R engines/gargoyle/frotz/detection_tables.h
  R engines/gargoyle/frotz/frotz.cpp
  R engines/gargoyle/frotz/frotz.h
  R engines/gargoyle/frotz/frotz_types.h
  R engines/gargoyle/frotz/glk_interface.cpp
  R engines/gargoyle/frotz/glk_interface.h
  R engines/gargoyle/frotz/mem.cpp
  R engines/gargoyle/frotz/mem.h
  R engines/gargoyle/frotz/processor.cpp
  R engines/gargoyle/frotz/processor.h
  R engines/gargoyle/frotz/processor_buffer.cpp
  R engines/gargoyle/frotz/processor_input.cpp
  R engines/gargoyle/frotz/processor_maths.cpp
  R engines/gargoyle/frotz/processor_mem.cpp
  R engines/gargoyle/frotz/processor_objects.cpp
  R engines/gargoyle/frotz/processor_screen.cpp
  R engines/gargoyle/frotz/processor_streams.cpp
  R engines/gargoyle/frotz/processor_table.cpp
  R engines/gargoyle/frotz/processor_text.cpp
  R engines/gargoyle/frotz/processor_variables.cpp
  R engines/gargoyle/frotz/quetzal.cpp
  R engines/gargoyle/frotz/quetzal.h
  R engines/gargoyle/gargoyle.cpp
  R engines/gargoyle/gargoyle.h
  R engines/gargoyle/glk.cpp
  R engines/gargoyle/glk.h
  R engines/gargoyle/glk_types.h
  R engines/gargoyle/module.mk
  R engines/gargoyle/picture.cpp
  R engines/gargoyle/picture.h
  R engines/gargoyle/scott/detection.cpp
  R engines/gargoyle/scott/detection.h
  R engines/gargoyle/scott/scott.cpp
  R engines/gargoyle/scott/scott.h
  R engines/gargoyle/screen.cpp
  R engines/gargoyle/screen.h
  R engines/gargoyle/selection.cpp
  R engines/gargoyle/selection.h
  R engines/gargoyle/speech.h
  R engines/gargoyle/streams.cpp
  R engines/gargoyle/streams.h
  R engines/gargoyle/time.cpp
  R engines/gargoyle/time.h
  R engines/gargoyle/unicode.cpp
  R engines/gargoyle/unicode.h
  R engines/gargoyle/unicode_gen.cpp
  R engines/gargoyle/unicode_gen.h
  R engines/gargoyle/utils.cpp
  R engines/gargoyle/utils.h
  R engines/gargoyle/window_graphics.cpp
  R engines/gargoyle/window_graphics.h
  R engines/gargoyle/window_pair.cpp
  R engines/gargoyle/window_pair.h
  R engines/gargoyle/window_text_buffer.cpp
  R engines/gargoyle/window_text_buffer.h
  R engines/gargoyle/window_text_grid.cpp
  R engines/gargoyle/window_text_grid.h
  R engines/gargoyle/windows.cpp
  R engines/gargoyle/windows.h


diff --git a/engines/gargoyle/blorb.cpp b/engines/gargoyle/blorb.cpp
deleted file mode 100644
index 064f9c4..0000000
--- a/engines/gargoyle/blorb.cpp
+++ /dev/null
@@ -1,543 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/blorb.h"
-
-namespace Gargoyle {
-
-#define giblorb_Inited_Magic 0xB7012BEDU
-
-/**
- * Describes one chunk of the Blorb file.
- */
-struct giblorb_chunkdesc_struct {
-    glui32 type;
-    glui32 len;
-    glui32 startpos;	///< start of chunk header
-    glui32 datpos;		///< start of data (either startpos or startpos+8)
-
-    void *ptr;		///< pointer to malloc'd data, if loaded
-    int auxdatnum;	///< entry in the auxsound/auxpict array; -1 if none. This only applies to chunks that represent resources;
-};
-typedef giblorb_chunkdesc_struct giblorb_chunkdesc_t;
-
-/**
- * Describes one resource in the Blorb file.
- */
-struct giblorb_resdesc_struct {
-	glui32 usage;
-	glui32 resnum;
-	glui32 chunknum;
-};
-
-/**
- * Holds the complete description of an open Blorb file. 
- */
-struct giblorb_map_struct {
-    glui32 inited; ///< holds giblorb_Inited_Magic if the map structure is valid
-	Common::SeekableReadStream *file;
-
-    uint numchunks;
-    giblorb_chunkdesc_t *chunks;	///< list of chunk descriptors
-
-    int numresources;
-    giblorb_resdesc_t *resources;	///< list of resource descriptors
-    giblorb_resdesc_t **ressorted;	///< list of pointers to descriptors in map->resources -- sorted by usage and resource number.
-};
-
-/*--------------------------------------------------------------------------*/
-
-giblorb_err_t Blorb::giblorb_initialize() {
-	_file = nullptr;
-	_map = nullptr;
-	return giblorb_err_None;
-}
-
-giblorb_err_t Blorb::giblorb_create_map(Common::SeekableReadStream *file, giblorb_map_t **newmap) {
-	giblorb_err_t err;
-	giblorb_map_t *map;
-	glui32 readlen;
-	glui32 nextpos, totallength;
-	giblorb_chunkdesc_t *chunks;
-	int chunks_size, numchunks;
-	char buffer[16];
-
-	*newmap = nullptr;
-
-	if (!_libInited) {
-		err = giblorb_initialize();
-		if (err)
-			return err;
-		_libInited = true;
-	}
-
-	/* First, chew through the file and index the chunks. */
-	file->seek(0);
-
-	readlen = file->read(buffer, 12);
-	if (readlen != 12)
-		return giblorb_err_Read;
-
-	if (READ_BE_INT32(buffer + 0) != giblorb_ID_FORM)
-		return giblorb_err_Format;
-	if (READ_BE_INT32(buffer + 8) != giblorb_ID_IFRS)
-		return giblorb_err_Format;
-
-	totallength = READ_BE_INT32(buffer + 4) + 8;
-	nextpos = 12;
-
-	chunks_size = 8;
-	numchunks = 0;
-	chunks = new giblorb_chunkdesc_t[chunks_size];
-
-	while (nextpos < totallength) {
-		glui32 type, len;
-		int chunum;
-		giblorb_chunkdesc_t *chu;
-
-		file->seek(nextpos);
-
-		readlen = file->read(buffer, 8);
-		if (readlen != 8) {
-			delete[] chunks;
-			return giblorb_err_Read;
-		}
-
-		type = READ_BE_INT32(buffer + 0);
-		len = READ_BE_INT32(buffer + 4);
-
-		if (numchunks >= chunks_size) {
-			chunks_size *= 2;
-			chunks = new giblorb_chunkdesc_t[chunks_size];
-		}
-
-		chunum = numchunks;
-		chu = &(chunks[chunum]);
-		numchunks++;
-
-		chu->type = type;
-		chu->startpos = nextpos;
-		if (type == giblorb_ID_FORM) {
-			chu->datpos = nextpos;
-			chu->len = len + 8;
-		} else {
-			chu->datpos = nextpos + 8;
-			chu->len = len;
-		}
-		chu->ptr = nullptr;
-		chu->auxdatnum = -1;
-
-		nextpos = nextpos + len + 8;
-		if (nextpos & 1)
-			nextpos++;
-
-		if (nextpos > totallength) {
-			delete[] chunks;
-			return giblorb_err_Format;
-		}
-	}
-
-	// The basic IFF structure seems to be ok, and we have a list of chunks.
-	// Now we allocate the map structure itself.
-	map = new giblorb_map_t();
-	if (!map) {
-		delete[] chunks;
-		return giblorb_err_Alloc;
-	}
-
-	map->inited = giblorb_Inited_Magic;
-	map->file = file;
-	map->chunks = chunks;
-	map->numchunks = numchunks;
-	map->resources = nullptr;
-	map->ressorted = nullptr;
-	map->numresources = 0;
-
-	// Now we do everything else involved in loading the Blorb file,
-	// such as building resource lists.
-	err = giblorb_initialize_map(map);
-	if (err) {
-		giblorb_destroy_map(map);
-		return err;
-	}
-
-	*newmap = map;
-	return giblorb_err_None;
-}
-
-
-giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
-	// It is important that the map structure be kept valid during this function.
-	// If this returns an error, giblorb_destroy_map() will be called.
-	uint ix, jx;
-	giblorb_result_t chunkres;
-	giblorb_err_t err;
-	char *ptr;
-	glui32 len;
-	glui32 numres;
-	int gotindex = false;
-
-	for (ix = 0; ix<map->numchunks; ix++) {
-		giblorb_chunkdesc_t *chu = &map->chunks[ix];
-
-		switch (chu->type) {
-		case giblorb_ID_RIdx:
-			// Resource index chunk: build the resource list and sort it.
-
-			if (gotindex)
-				return giblorb_err_Format; // duplicate index chunk
-			err = giblorb_load_chunk_by_number(map, giblorb_method_Memory, &chunkres, ix);
-			if (err)
-				return err;
-
-			ptr = (char *)chunkres.data.ptr;
-			len = chunkres.length;
-			numres = READ_BE_INT32(ptr + 0);
-
-			if (numres) {
-				uint ix2;
-				giblorb_resdesc_t *resources;
-				giblorb_resdesc_t **ressorted;
-
-				if (len != numres * 12 + 4)
-					return giblorb_err_Format; // bad length field
-
-				resources = new giblorb_resdesc_t[numres];
-				ressorted = new giblorb_resdesc_t *[numres];
-				if (!ressorted || !resources) {
-					delete[] resources;
-					delete[] ressorted;
-					return giblorb_err_Alloc;
-				}
-
-				ix2 = 0;
-				for (jx = 0; jx < numres; jx++) {
-					giblorb_resdesc_t *res = &(resources[jx]);
-					glui32 respos;
-
-					res->usage = READ_BE_INT32(ptr + jx * 12 + 4);
-					res->resnum = READ_BE_INT32(ptr + jx * 12 + 8);
-					respos = READ_BE_INT32(ptr + jx * 12 + 12);
-
-					while (ix2 < map->numchunks
-						&& map->chunks[ix2].startpos < respos)
-						ix2++;
-
-					if (ix2 >= map->numchunks
-						|| map->chunks[ix2].startpos != respos) {
-						delete[] resources;
-						delete[] ressorted;
-						return giblorb_err_Format; // start pos does not match a real chunk
-					}
-
-					res->chunknum = ix2;
-
-					ressorted[jx] = res;
-				}
-
-				// Sort a resource list (actually a list of pointers to structures in map->resources.)
-				// This makes it easy to find resources by usage and resource number.
-				giblorb_qsort(ressorted, numres);
-
-				map->numresources = numres;
-				map->resources = resources;
-				map->ressorted = ressorted;
-			}
-
-			giblorb_unload_chunk(map, ix);
-			gotindex = true;
-			break;
-		}
-	}
-
-	return giblorb_err_None;
-}
-
-void Blorb::giblorb_qsort(giblorb_resdesc_t **list, size_t len) {
-	int ix, jx, res;
-	giblorb_resdesc_t *tmpptr, *pivot;
-
-	if (len < 6) {
-		// The list is short enough for a bubble-sort.
-		for (jx = len - 1; jx>0; jx--) {
-			for (ix = 0; ix<jx; ix++) {
-				res = sortsplot(list[ix], list[ix + 1]);
-				if (res > 0) {
-					tmpptr = list[ix];
-					list[ix] = list[ix + 1];
-					list[ix + 1] = tmpptr;
-				}
-			}
-		}
-	} else {
-		// Split the list.
-		pivot = list[len / 2];
-		ix = 0;
-		jx = len;
-		for (;;) {
-			while (ix < jx - 1 && sortsplot(list[ix], pivot) < 0)
-				ix++;
-			while (ix < jx - 1 && sortsplot(list[jx - 1], pivot) > 0)
-				jx--;
-			if (ix >= jx - 1)
-				break;
-			tmpptr = list[ix];
-			list[ix] = list[jx - 1];
-			list[jx - 1] = tmpptr;
-		}
-		ix++;
-		// Sort the halves.
-		giblorb_qsort(list + 0, ix);
-		giblorb_qsort(list + ix, len - ix);
-	}
-}
-
-giblorb_resdesc_t *Blorb::giblorb_bsearch(giblorb_resdesc_t *sample,
-	giblorb_resdesc_t **list, int len) {
-	int top, bot, val, res;
-
-	bot = 0;
-	top = len;
-
-	while (bot < top) {
-		val = (top + bot) / 2;
-		res = sortsplot(list[val], sample);
-		if (res == 0)
-			return list[val];
-		if (res < 0) {
-			bot = val + 1;
-		} else {
-			top = val;
-		}
-	}
-
-	return nullptr;
-}
-
-int Blorb::sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2) {
-	if (v1->usage < v2->usage)
-		return -1;
-	if (v1->usage > v2->usage)
-		return 1;
-	if (v1->resnum < v2->resnum)
-		return -1;
-	if (v1->resnum > v2->resnum)
-		return 1;
-	return 0;
-}
-
-giblorb_err_t Blorb::giblorb_destroy_map(giblorb_map_t *map) {
-	if (!map || !map->chunks || map->inited != giblorb_Inited_Magic)
-		return giblorb_err_NotAMap;
-
-	for (uint ix = 0; ix<map->numchunks; ix++) {
-		giblorb_chunkdesc_t *chu = &(map->chunks[ix]);
-		if (chu->ptr) {
-			delete chu->ptr;
-			chu->ptr = nullptr;
-		}
-	}
-
-	if (map->chunks) {
-		delete[] map->chunks;
-		map->chunks = nullptr;
-	}
-
-	map->numchunks = 0;
-
-	if (map->resources) {
-		delete[] map->resources;
-		map->resources = nullptr;
-	}
-
-	if (map->ressorted) {
-		delete[] map->ressorted;
-		map->ressorted = nullptr;
-	}
-
-	map->numresources = 0;
-	map->file = nullptr;
-	map->inited = 0;
-
-	delete map;
-
-	return giblorb_err_None;
-}
-
-giblorb_err_t Blorb::giblorb_load_chunk_by_type(giblorb_map_t *map,
-		glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count) {
-	uint ix;
-
-	for (ix = 0; ix < map->numchunks; ix++) {
-		if (map->chunks[ix].type == chunktype) {
-			if (count == 0)
-				break;
-			count--;
-		}
-	}
-
-	if (ix >= map->numchunks) {
-		return giblorb_err_NotFound;
-	}
-
-	return giblorb_load_chunk_by_number(map, method, res, ix);
-}
-
-giblorb_err_t Blorb::giblorb_load_chunk_by_number(giblorb_map_t *map,
-		glui32 method, giblorb_result_t *res, glui32 chunknum) {
-	giblorb_chunkdesc_t *chu;
-
-	if (chunknum < 0 || chunknum >= map->numchunks)
-		return giblorb_err_NotFound;
-
-	chu = &(map->chunks[chunknum]);
-
-	switch (method) {
-	case giblorb_method_DontLoad:
-		// do nothing
-		break;
-
-	case giblorb_method_FilePos:
-		res->data.startpos = chu->datpos;
-		break;
-
-	case giblorb_method_Memory:
-		if (!chu->ptr) {
-			glui32 readlen;
-			byte *dat = new byte[chu->len];
-
-			if (!dat)
-				return giblorb_err_Alloc;
-
-			map->file->seek(chu->datpos);
-
-			readlen = map->file->read(dat, chu->len);
-			if (readlen != chu->len)
-				return giblorb_err_Read;
-
-			chu->ptr = dat;
-		}
-
-		res->data.ptr = chu->ptr;
-		break;
-	}
-
-	res->chunknum = chunknum;
-	res->length = chu->len;
-	res->chunktype = chu->type;
-
-	return giblorb_err_None;
-}
-
-giblorb_err_t Blorb::giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum) {
-	giblorb_chunkdesc_t *chu;
-
-	if (chunknum < 0 || chunknum >= map->numchunks)
-		return giblorb_err_NotFound;
-
-	chu = &(map->chunks[chunknum]);
-
-	if (chu->ptr) {
-		delete chu->ptr;
-		chu->ptr = nullptr;
-	}
-
-	return giblorb_err_None;
-}
-
-giblorb_err_t Blorb::giblorb_load_resource(giblorb_map_t *map, glui32 method,
-		giblorb_result_t *res, glui32 usage, glui32 resnum) {
-	giblorb_resdesc_t sample;
-	giblorb_resdesc_t *found;
-
-	sample.usage = usage;
-	sample.resnum = resnum;
-
-	found = giblorb_bsearch(&sample, map->ressorted, map->numresources);
-
-	if (!found)
-		return giblorb_err_NotFound;
-
-	return giblorb_load_chunk_by_number(map, method, res, found->chunknum);
-}
-
-giblorb_err_t Blorb::giblorb_count_resources(giblorb_map_t *map,
-		glui32 usage, glui32 *num, glui32 *min, glui32 *max) {
-	int ix;
-	int count;
-	glui32 val;
-	glui32 minval, maxval;
-
-	count = 0;
-	minval = 0;
-	maxval = 0;
-
-	for (ix = 0; ix<map->numresources; ix++) {
-		if (map->resources[ix].usage == usage) {
-			val = map->resources[ix].resnum;
-			if (count == 0) {
-				count++;
-				minval = val;
-				maxval = val;
-			}
-			else {
-				count++;
-				if (val < minval)
-					minval = val;
-				if (val > maxval)
-					maxval = val;
-			}
-		}
-	}
-
-	if (num)
-		*num = count;
-	if (min)
-		*min = minval;
-	if (max)
-		*max = maxval;
-
-	return giblorb_err_None;
-}
-
-giblorb_err_t Blorb::giblorb_set_resource_map(Common::SeekableReadStream *file) {
-	giblorb_err_t err;
-
-	err = giblorb_create_map(file, &_map);
-	if (err) {
-		_map = nullptr;
-		return err;
-	}
-
-	_file = file;
-	return giblorb_err_None;
-}
-
-giblorb_map_t *Blorb::giblorb_get_resource_map(void) {
-	return _map;
-}
-
-bool Blorb::giblorb_is_resource_map(void) const {
-	return _map != nullptr;
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/blorb.h b/engines/gargoyle/blorb.h
deleted file mode 100644
index 9de1947..0000000
--- a/engines/gargoyle/blorb.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_BLORB_H
-#define GARGOYLE_BLORB_H
-
-#include "gargoyle/glk_types.h"
-#include "gargoyle/streams.h"
-
-namespace Gargoyle {
-
-
-/**
- * Error type
- */
-typedef glui32 giblorb_err_t;
-
-/**
- * Error codes
- */
-enum giblorbError {
-	giblorb_err_None        = 0,
-	giblorb_err_CompileTime = 1,
-	giblorb_err_Alloc       = 2,
-	giblorb_err_Read        = 3,
-	giblorb_err_NotAMap     = 4,
-	giblorb_err_Format      = 5,
-	giblorb_err_NotFound    = 6
-};
-
-/**
- * Methods for loading a chunk
- */
-enum giblorbMethod {
-	giblorb_method_DontLoad = 0,
-	giblorb_method_Memory   = 1,
-	giblorb_method_FilePos  = 2
-};
-
-enum {
-	giblorb_ID_Snd       = MKTAG('S', 'n', 'd', ' '),
-	giblorb_ID_Exec      = MKTAG('E', 'x', 'e', 'c'),
-	giblorb_ID_Pict      = MKTAG('P', 'i', 'c', 't'),
-	giblorb_ID_Copyright = MKTAG('(', 'c', ')', ' '),
-	giblorb_ID_AUTH      = MKTAG('A', 'U', 'T', 'H'),
-	giblorb_ID_ANNO      = MKTAG('A', 'N', 'N', 'O')
-};
-
-
-enum {
-	giblorb_ID_MOD  = MKTAG('M', 'O', 'D', ' '),
-	giblorb_ID_FORM = MKTAG('F', 'O', 'R', 'M'),
-	giblorb_ID_IFRS = MKTAG('I', 'F', 'R', 'S'),
-	giblorb_ID_RIdx = MKTAG('R', 'I', 'd', 'x'),
-	giblorb_ID_OGG  = MKTAG('O', 'G', 'G', 'V'),
-
-	// non-standard types
-	giblorb_ID_MIDI = MKTAG('M', 'I', 'D', 'I'),
-	giblorb_ID_MP3  = MKTAG('M', 'P', '3', ' '),
-	giblorb_ID_WAVE = MKTAG('W', 'A', 'V', 'E')
-};
-
-/**
- * Holds the complete description of an open Blorb file.
- * This type is opaque for normal interpreter use.
- */
-typedef struct giblorb_map_struct giblorb_map_t;
-
-/* giblorb_result_t: Result when you try to load a chunk. */
-typedef struct giblorb_result_struct {
-	glui32 chunknum; // The chunk number (for use in giblorb_unload_chunk(), etc.)
-	union {
-		void *ptr;			///< A pointer to the data (if you used giblorb_method_Memory)
-		glui32 startpos;	///< The position in the file (if you used giblorb_method_FilePos)
-	} data;
-
-	glui32 length;			///< The length of the data
-	glui32 chunktype;		///< The type of the chunk.
-} giblorb_result_t;
-
-typedef struct giblorb_resdesc_struct giblorb_resdesc_t;
-
-class Blorb {
-private:
-	bool _libInited;
-	Common::SeekableReadStream *_file;
-	giblorb_map_t *_map;
-private:
-	/**
-	 * Initializes Blorb
-	 */
-	giblorb_err_t giblorb_initialize();
-
-	giblorb_err_t giblorb_initialize_map(giblorb_map_t *map);
-	void giblorb_qsort(giblorb_resdesc_t **list, size_t len);
-	giblorb_resdesc_t *giblorb_bsearch(giblorb_resdesc_t *sample,
-		giblorb_resdesc_t **list, int len);
-	int sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2);
-public:
-	/**
-	 * Constructor
-	 */
-	Blorb() : _libInited(false), _file(nullptr), _map(nullptr) {}
-
-	giblorb_err_t giblorb_set_resource_map(Common::SeekableReadStream *file);
-	giblorb_map_t *giblorb_get_resource_map(void);
-	bool giblorb_is_resource_map(void) const;
-
-
-	giblorb_err_t giblorb_create_map(Common::SeekableReadStream *file, giblorb_map_t **newmap);
-	giblorb_err_t giblorb_destroy_map(giblorb_map_t *map);
-
-	giblorb_err_t giblorb_load_chunk_by_type(giblorb_map_t *map,
-		glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count);
-	giblorb_err_t giblorb_load_chunk_by_number(giblorb_map_t *map,
-		glui32 method, giblorb_result_t *res, glui32 chunknum);
-	giblorb_err_t giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum);
-
-	giblorb_err_t giblorb_load_resource(giblorb_map_t *map, glui32 method,
-		giblorb_result_t *res, glui32 usage, glui32 resnum);
-	giblorb_err_t giblorb_count_resources(giblorb_map_t *map,
-		glui32 usage, glui32 *num, glui32 *min, glui32 *max);
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/conf.cpp b/engines/gargoyle/conf.cpp
deleted file mode 100644
index 9a0d648..0000000
--- a/engines/gargoyle/conf.cpp
+++ /dev/null
@@ -1,254 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/conf.h"
-#include "gargoyle/fonts.h"
-#include "gargoyle/utils.h"
-#include "gargoyle/windows.h"
-#include "common/config-manager.h"
-#include "common/system.h"
-
-namespace Gargoyle {
-
-const byte WHITE[3] = { 0xff, 0xff, 0xff };
-const byte BLUE[3] = { 0x00, 0x00, 0x60 };
-const byte SCROLL_BG[3] = { 0xb0, 0xb0, 0xb0 };
-const byte SCROLL_FG[3] = { 0x80, 0x80, 0x80 };
-
-WindowStyle T_STYLES[style_NUMSTYLES] = {
-	{ PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Normal
-	{ PROPI, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Emphasized
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Preformatted
-	{ PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Header
-	{ PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Subheader
-	{ PROPZ, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Alert
-	{ PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< Note
-	{ PROPR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< BlockQuote
-	{ PROPB, { 0xff, 0xff, 0xff }, { 0x00, 0x60, 0x00 }, 0 }, ///< Input
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< User1
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x00, 0x00, 0x00 }, 0 }, ///< User2
-};
-
-WindowStyle G_STYLES[style_NUMSTYLES] = {
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Normal
-	{ MONOI, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Emphasized
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Preformatted
-	{ MONOB, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Header
-	{ MONOB, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Subheader
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Alert
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Note
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< BlockQuote
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< Input
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< User1
-	{ MONOR, { 0xff, 0xff, 0xff }, { 0x60, 0x60, 0x60 }, 0 }, ///< User2
-};
-
-Conf *g_conf;
-
-Conf::Conf() {
-	g_conf = this;
-	_imageW = g_system->getWidth();
-	_imageH = g_system->getHeight();
-	_cellW = _cellH = 8;
-	_leading = 0;
-	_baseLine = 0;
-
-	get("moreprompt", _morePrompt, "\207 more \207");
-	get("morecolor", _moreColor);
-	get("morecolor", _moreSave);
-	get("morefont", _moreFont, PROPB);
-	get("morealign", _moreAlign);
-	get("monoaspect", _monoAspect, 1.0);
-	get("propaspect", _propAspect, 1.0);
-	get("monosize", _monoSize, 11);
-	get("monor", _monoR);
-	get("monob", _monoR);
-	get("monoi", _monoI);
-	get("monoz", _monoZ);
-	get("monofont", _monoFont, "Liberation Mono");
-	get("propsize", _propSize, 12);
-	get("propr", _propR);
-	get("propb", _propR);
-	get("propi", _propI);
-	get("propz", _propZ);
-	get("propfont", _propFont, "Linux Libertine O");
-	get("rows", _rows, 25);
-	get("cols", _cols, 60);
-
-	if (ConfMan.hasKey("leading"))
-		_leading = atof(ConfMan.get("leading").c_str()) + 0.5;
-	if (ConfMan.hasKey("baseline"))
-		_baseLine = atof(ConfMan.get("baseline").c_str()) + 0.5;
-
-	if (ConfMan.hasKey("minrows"))
-		_rows = MAX(_rows, strToInt(ConfMan.get("minrows").c_str()));
-	if (ConfMan.hasKey("maxrows"))
-		_rows = MIN(_rows, strToInt(ConfMan.get("maxrows").c_str()));
-	if (ConfMan.hasKey("mincols"))
-		_cols = MAX(_cols, strToInt(ConfMan.get("mincols").c_str()));
-	if (ConfMan.hasKey("maxcols"))
-		_cols = MIN(_cols, strToInt(ConfMan.get("maxcols").c_str()));
-
-	get("lockrows", _lockRows);
-	get("lockcols", _lockCols);
-	get("wmarginx", _wMarginX, 15);
-	get("wmarginy", _wMarginY, 15);
-	_wMarginSaveX = _wMarginX;
-	_wMarginSaveY = _wMarginY;
-
-	get("wpaddingx", _wPaddingX);
-	get("wpaddingy", _wPaddingY);
-	get("wborderx", _wBorderX);
-	get("wbordery", _wBorderY);
-	get("tmarginx", _tMarginX, 7);
-	get("tmarginy", _tMarginY, 7);
-	get("gamma", _gamma, 1.0);
-
-	get("caretcolor", _caretColor);
-	get("caretcolor", _caretSave);
-	get("linkcolor", _linkColor, BLUE);
-	get("linkcolor", _linkSave, BLUE);
-	get("bordercolor", _borderColor);
-	get("bordercolor", _borderSave);
-	get("windowcolor", _windowColor, WHITE);
-	get("windowcolor", _windowSave, WHITE);
-	get("lcd", _lcd, 1);
-	get("caretshape", _caretShape, 2);
-
-	_linkStyle = ConfMan.hasKey("linkstyle") && !strToInt(ConfMan.get("linkstyle").c_str()) ? 0 : 1;
-
-	get("scrollwidth", _scrollWidth);
-	get("scrollbg", _scrollBg, SCROLL_BG);
-	get("scrollfg", _scrollFg, SCROLL_FG);
-	get("justify", _justify);
-	get("quotes", _quotes, 1);
-	get("dashes", _dashes, 1);
-	get("spaces", _spaces);
-	get("caps", _caps);
-	get("graphics", _graphics, true);
-	get("sound", _sound, true);
-	get("speak", _speak);
-	get("speak_input", _speakInput);
-	get("speak_language", _speakLanguage);
-	get("stylehint", _styleHint, 1);
-	get("safeclicks", _safeClicks);
-
-	Common::copy(T_STYLES, T_STYLES + style_NUMSTYLES, _tStyles);
-	Common::copy(G_STYLES, G_STYLES + style_NUMSTYLES, _gStyles);
-
-	char buffer[255];
-	const char *const TG_COLOR[2] = { "tcolor_%d", "gcolor_%d" };
-	for (int tg = 0; tg < 2; ++tg) {
-		for (int style = 0; style <= 10; ++style) {
-			Common::String key = Common::String::format(TG_COLOR[tg], style);
-			if (!ConfMan.hasKey(key))
-				continue;
-
-			strncpy(buffer, ConfMan.get(key).c_str(), 254);
-			buffer[255] = '\0';
-			char *fg = strtok(buffer, "\r\n\t ");
-			char *bg = strtok(nullptr, "\r\n\t ");
-
-			if (tg == 0) {
-				parseColor(fg, _tStyles[style].fg);
-				parseColor(bg, _tStyles[style].bg);
-			} else {
-				parseColor(fg, _gStyles[style].fg);
-				parseColor(bg, _gStyles[style].bg);
-			}
-		}
-	}
-
-	const char *const TG_FONT[2] = { "tfont_%d", "gfont_%d" };
-	for (int tg = 0; tg < 2; ++tg) {
-		for (int style = 0; style <= 10; ++style) {
-			Common::String key = Common::String::format(TG_FONT[tg], style);
-			if (!ConfMan.hasKey(key))
-				continue;
-
-			strncpy(buffer, ConfMan.get(key).c_str(), 254);
-			buffer[255] = '\0';
-			char *font = strtok(buffer, "\r\n\t ");
-
-			if (tg == 0)
-				_tStyles[style].font = Fonts::getId(font);
-			else
-				_gStyles[style].font = Fonts::getId(font);
-		}
-	}
-
-	Common::copy(_tStyles, _tStyles + style_NUMSTYLES, _tStylesDefault);
-	Common::copy(_gStyles, _gStyles + style_NUMSTYLES, _gStylesDefault);
-}
-
-void Conf::get(const Common::String &key, Common::String &field, const char *defaultVal) {
-	field = ConfMan.hasKey(key) ? ConfMan.get(key) : defaultVal;
-	field.trim();
-}
-
-void Conf::get(const Common::String &key, byte *color, const byte *defaultColor) {
-	if (ConfMan.hasKey(key)) {
-		parseColor(ConfMan.get(key), color);
-	} else if (defaultColor) {
-		Common::copy(defaultColor, defaultColor + 3, color);
-	} else {
-		Common::fill(color, color + 3, 0);
-	}
-}
-
-void Conf::get(const Common::String &key, int &field, int defaultVal) {
-	field = ConfMan.hasKey(key) ? strToInt(ConfMan.get(key).c_str()) : defaultVal;
-}
-
-void Conf::get(const Common::String &key, bool &field, bool defaultVal) {
-	field = ConfMan.hasKey(key) ? strToInt(ConfMan.get(key).c_str()) != 0 : defaultVal;
-}
-
-void Conf::get(const Common::String &key, FACES &field, FACES defaultFont) {
-	field = ConfMan.hasKey(key) ? Fonts::getId(ConfMan.get(key)) : defaultFont;
-}
-
-void Conf::get(const Common::String &key, double &field, double defaultVal) {
-	field = ConfMan.hasKey(key) ?  atof(ConfMan.get(key).c_str()) : defaultVal;
-}
-
-void Conf::parseColor(const Common::String &str, byte *color) {
-	char r[3], g[3], b[3];
-
-	if (str.size() == 6) {
-		r[0] = str[0];
-		r[1] = str[1];
-		r[2] = 0;
-		g[0] = str[2];
-		g[1] = str[3];
-		g[2] = 0;
-		b[0] = str[4];
-		b[1] = str[5];
-		b[2] = 0;
-
-		color[0] = strtol(r, nullptr, 16);
-		color[1] = strtol(g, nullptr, 16);
-		color[2] = strtol(b, nullptr, 16);
-	}
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/conf.h b/engines/gargoyle/conf.h
deleted file mode 100644
index 05dc6c9..0000000
--- a/engines/gargoyle/conf.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_CONF_H
-#define GARGOYLE_CONF_H
-
-#include "gargoyle/glk_types.h"
-#include "gargoyle/fonts.h"
-#include "gargoyle/windows.h"
-
-namespace Gargoyle {
-
-class Conf {
-private:
-	/**
-	 * Get a string
-	 */
-	void get(const Common::String &key, Common::String &field, const char *defaultVal = nullptr);
-
-	/**
-	 * Get a color
-	 */
-	void get(const Common::String &key, byte *color, const byte *defaultColor = nullptr);
-
-	/**
-	 * Get a font name into a font Id
-	 */
-	void get(const Common::String &key, FACES &field, FACES defaultFont);
-
-	/**
-	 * Get a numeric value
-	 */
-	void get(const Common::String &key, int &field, int defaultVal = 0);
-
-	/**
-	 * Get a numeric value
-	 */
-	void get(const Common::String &key, bool &field, bool defaultVal = false);
-
-	/**
-	 * Get a double
-	 */
-	void get(const Common::String &key, double &field, double defaultVal = 0.0);
-
-	/**
-	 * Parse a color
-	 */
-	void parseColor(const Common::String &str, byte *color);
-public:
-	Common::String _morePrompt;
-	byte _moreColor[3], _moreSave[3];
-	FACES _moreFont;
-	int _moreAlign;
-	double _monoAspect;
-	double _propAspect;
-	double _monoSize;
-	Common::String _monoR;
-	Common::String _monoB;
-	Common::String _monoI;
-	Common::String _monoZ;
-	Common::String _monoFont;
-	double _propSize;
-	Common::String _propR;
-	Common::String _propB;
-	Common::String _propI;
-	Common::String _propZ;
-	Common::String _propFont;
-	int _leading;
-	int _baseLine;
-	int _cols, _rows;
-	int _lockCols, _lockRows;
-	int _wMarginX, _wMarginY;
-	int _wMarginSaveX, _wMarginSaveY;
-	int _wPaddingX, _wPaddingY;
-	int _wBorderX, _wBorderY;
-	int _tMarginX, _tMarginY;
-	double _gamma;
-	byte _caretColor[3], _caretSave[3];
-	byte _linkColor[3], _linkSave[3];
-	byte _borderColor[3], _borderSave[3];
-	byte _windowColor[3], _windowSave[3];
-	int _lcd;
-	int _caretShape;
-	int _linkStyle;
-	int _scrollWidth;
-	byte _scrollBg[3], _scrollFg[3];
-	int _justify;
-	int _quotes;
-	int _dashes;
-	int _spaces;
-	int _caps;
-	bool _graphics;
-	bool _sound;
-	bool _speak;
-	bool _speakInput;
-	Common::String _speakLanguage;
-	int _styleHint;
-	bool _safeClicks;
-	WindowStyle _tStyles[style_NUMSTYLES];
-	WindowStyle _gStyles[style_NUMSTYLES];
-	WindowStyle _tStylesDefault[style_NUMSTYLES];
-	WindowStyle _gStylesDefault[style_NUMSTYLES];
-
-	int _imageW, _imageH;
-	int _cellW, _cellH;
-public:
-	/**
-	 * Constructor
-	 */
-	Conf();
-};
-
-extern Conf *g_conf;
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/configure.engine b/engines/gargoyle/configure.engine
deleted file mode 100644
index 3569297..0000000
--- a/engines/gargoyle/configure.engine
+++ /dev/null
@@ -1,3 +0,0 @@
-# This file is included from the main "configure" script
-# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine gargoyle "Interactive Fiction games" no "" "" "freetype2"
diff --git a/engines/gargoyle/detection.cpp b/engines/gargoyle/detection.cpp
deleted file mode 100644
index 61cf201..0000000
--- a/engines/gargoyle/detection.cpp
+++ /dev/null
@@ -1,323 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/gargoyle.h"
-
-#include "base/plugins.h"
-#include "common/md5.h"
-#include "common/memstream.h"
-#include "common/savefile.h"
-#include "common/str-array.h"
-#include "common/system.h"
-#include "engines/advancedDetector.h"
-#include "graphics/colormasks.h"
-#include "graphics/surface.h"
-
-#define MAX_SAVES 99
-
-namespace Gargoyle {
-
-struct GargoyleGameDescription {
-	ADGameDescription _desc;
-	Common::String _filename;
-	InterpreterType _interpType;
-	Common::String _md5;
-};
-
-const Common::String &GargoyleEngine::getFilename() const {
-	return _gameDescription->_filename;
-}
-uint32 GargoyleEngine::getFeatures() const {
-	return _gameDescription->_desc.flags;
-}
-
-bool GargoyleEngine::isDemo() const {
-	return (bool)(_gameDescription->_desc.flags & ADGF_DEMO);
-}
-
-Common::Language GargoyleEngine::getLanguage() const {
-	return _gameDescription->_desc.language;
-}
-
-InterpreterType GargoyleEngine::getInterpreterType() const {
-	return _gameDescription->_interpType;
-}
-
-const Common::String &GargoyleEngine::getGameMD5() const {
-	return _gameDescription->_md5;
-}
-
-} // End of namespace Gargoyle
-
-#include "gargoyle/frotz/detection_tables.h"
-#define ZCODE(ID, NAME) { ID, Gargoyle::Frotz::NAME##_DESC }
-
-static const PlainGameDescriptor gargoyleGames[] = {
-	{"zcode", "Zcode Games" },
-	{"scottadams", "Scott Adams Games"},
-
-	// Infocom/Z-code games
-	ZCODE("amfv", AMFV),
-	ZCODE("arthur", ARTHUR),
-	ZCODE("ballyhoo", BALLYHOO),
-	ZCODE("beyondzork", BEYONDZORK),
-	ZCODE("borderzone", BORDERZONE),
-	ZCODE("bureaucracy", BUREAUCRACY),
-	ZCODE("cutthroats", CUTTHROATS),
-	ZCODE("deadline", DEADLINE),
-	ZCODE("enchanter", ENCHANTER),
-	ZCODE("hhgttg", HHGTTG),
-	ZCODE("hijinx", HIJINX),
-	ZCODE("infidel", INFIDEL),
-	ZCODE("journey", JOURNEY),
-	ZCODE("lgop", LGOP),
-	ZCODE("lgop2", LGOP2),
-	ZCODE("lurking", LURKING),
-	ZCODE("minizork1", MINIZORK1),
-	ZCODE("moonmist", MOONMIST),
-	ZCODE("nordbert", NORDBERT),
-	ZCODE("planetfall", PLANETFALL),
-	ZCODE("plundered", PLUNDERED),
-	ZCODE("sampler1", SAMPLER1),
-	ZCODE("sampler2", SAMPLER2),
-	ZCODE("seastalker", SEASTALKER),
-	ZCODE("sherlockriddle", SHERLOCKRIDDLE),
-	ZCODE("shogun", SHOGUN),
-	ZCODE("sorcerer", SORCERER),
-	ZCODE("spellbreaker", SPELLBREAKER),
-	ZCODE("starcross", STARCROSS),
-	ZCODE("stationfall", STATIONFALL),
-	ZCODE("suspect", SUSPECT),
-	ZCODE("suspended", SUSPENDED),
-	ZCODE("trinity", TRINITY),
-	ZCODE("wishbringer", WISHBRINGER),
-	ZCODE("witness", WITNESS),
-	ZCODE("zork0", ZORK0),
-	ZCODE("zork1", ZORK1),
-	ZCODE("zork2", ZORK2),
-	ZCODE("zork3", ZORK3),
-	ZCODE("ztuu", ZTUU),
-
-	// Scott Adams games
-	{ "adventureland", "Adventureland" },
-	{ "pirateadventure", "Pirate Adventure" },
-	{ "missionimpossible", "Mission Impossible" },
-	{ "voodoocastle", "Voodoo Castle" },
-	{ "thecount", "The Count" },
-	{ "strangeodyssey", "Strange Odyssey" },
-	{ "mysteryfunhouse", "Mystery Fun House" },
-	{ "pyramidofdoom", "Pyramid Of Doom" },
-	{ "ghosttown", "Ghost Town" },
-	{ "savageisland1", "Savage Island, Part 1" },
-	{ "savageisland2", "Savage Island, Part 2" },
-	{ "goldenvoyage", "The Golden Voyage" },
-	{ "adventure13", "Adventure 13" },
-	{ "adventure14", "Adventure 14" },
-	{ "buckaroobonzai", "Buckaroo Banzai" },
-	{ "marveladventure", "Marvel Adventure #1" },
-	{ "scottsampler", "Adventure International's Mini-Adventure Sampler" },
-	{0, 0}
-};
-
-#include "common/config-manager.h"
-#include "common/file.h"
-#include "gargoyle/detection_tables.h"
-#include "gargoyle/frotz/detection.h"
-#include "gargoyle/frotz/frotz.h"
-#include "gargoyle/scott/detection.h"
-#include "gargoyle/scott/scott.h"
-
-class GargoyleMetaEngine : public AdvancedMetaEngine {
-public:
-	GargoyleMetaEngine() : AdvancedMetaEngine(Gargoyle::gameDescriptions, sizeof(Gargoyle::GargoyleGameDescription), gargoyleGames) {
-		_maxScanDepth = 3;
-	}
-
-	virtual const char *getName() const {
-		return "Gargoyle Engine";
-	}
-
-	virtual const char *getOriginalCopyright() const {
-		return "Gargoyle Engine (c) 2018";
-	}
-
-	virtual bool hasFeature(MetaEngineFeature f) const override;
-	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
-	virtual SaveStateList listSaves(const char *target) const;
-	virtual int getMaximumSaveSlot() const;
-	virtual void removeSaveState(const char *target, int slot) const;
-	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
-
-	virtual DetectedGames detectGames(const Common::FSList &fslist) const override;
-
-	virtual ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const override;
-};
-
-bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
-	return
-	    (f == kSupportsListSaves) ||
-	    (f == kSupportsLoadingDuringStartup) ||
-	    (f == kSupportsDeleteSave) ||
-	    (f == kSavesSupportMetaInfo) ||
-	    (f == kSavesSupportCreationDate) ||
-	    (f == kSavesSupportPlayTime) ||
-	    (f == kSimpleSavesNames);
-}
-
-bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
-	return
-	    (f == kSupportsRTL) ||
-	    (f == kSupportsLoadingDuringRuntime) ||
-	    (f == kSupportsSavingDuringRuntime);
-}
-
-bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
-	const Gargoyle::GargoyleGameDescription *gd = (const Gargoyle::GargoyleGameDescription *)desc;
-
-	switch (gd->_interpType) {
-	case Gargoyle::INTERPRETER_FROTZ:
-		*engine = new Gargoyle::Frotz::Frotz(syst, gd);
-		break;
-	case Gargoyle::INTERPRETER_SCOTT:
-		*engine = new Gargoyle::Scott::Scott(syst, gd);
-		break;
-	default:
-		error("Unknown interpreter");
-	}
-
-	return gd != 0;
-}
-
-SaveStateList GargoyleMetaEngine::listSaves(const char *target) const {
-	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
-	Common::StringArray filenames;
-	Common::String saveDesc;
-	Common::String pattern = Common::String::format("%s.0##", target);
-	Gargoyle::SavegameHeader header;
-
-	filenames = saveFileMan->listSavefiles(pattern);
-
-	SaveStateList saveList;
-	for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
-		const char *ext = strrchr(file->c_str(), '.');
-		int slot = ext ? atoi(ext + 1) : -1;
-
-		if (slot >= 0 && slot <= MAX_SAVES) {
-			Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file);
-
-			if (in) {
-				if (Gargoyle::FileStream::readSavegameHeader(in, header))
-					saveList.push_back(SaveStateDescriptor(slot, header._saveName));
-
-				delete in;
-			}
-		}
-	}
-
-	// Sort saves based on slot number.
-	Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
-	return saveList;
-}
-
-int GargoyleMetaEngine::getMaximumSaveSlot() const {
-	return MAX_SAVES;
-}
-
-void GargoyleMetaEngine::removeSaveState(const char *target, int slot) const {
-	Common::String filename = Common::String::format("%s.%03d", target, slot);
-	g_system->getSavefileManager()->removeSavefile(filename);
-}
-
-SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
-	Common::String filename = Common::String::format("%s.%03d", target, slot);
-	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
-
-	if (in) {
-		Gargoyle::SavegameHeader header;
-		if (Gargoyle::FileStream::readSavegameHeader(in, header)) {
-			// Create the return descriptor
-			SaveStateDescriptor desc(slot, header._saveName);
-			desc.setSaveDate(header._year, header._month, header._day);
-			desc.setSaveTime(header._hour, header._minute);
-			desc.setPlayTime(header._totalFrames * GAME_FRAME_TIME);
-
-			delete in;
-			return desc;
-		}
-	}
-
-	return SaveStateDescriptor();
-}
-
-DetectedGames GargoyleMetaEngine::detectGames(const Common::FSList &fslist) const {
-	DetectedGames detectedGames;
-	Gargoyle::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
-	Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
-
-	return detectedGames;
-}
-
-static Gargoyle::GargoyleGameDescription gameDescription;
-
-ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
-	static char gameId[100];
-	strcpy(gameId, ConfMan.get("gameid").c_str());
-	Common::String filename = ConfMan.get("filename");
-
-	Common::FSList fslist;
-	DetectedGames detectedGames;
-	fslist.push_back(parent.getChild(filename));
-	ADDetectedGames results;
-	Common::File f;
-
-	// Check each sub-engine for any detected games
-	if (Gargoyle::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames))
-		gameDescription._interpType = Gargoyle::INTERPRETER_FROTZ;
-	else if (Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames))
-		gameDescription._interpType = Gargoyle::INTERPRETER_SCOTT;
-	else
-		// No match found, so return no results
-		return results;
-
-	// Set up the game description and return it
-	if (f.open(parent.getChild(filename))) {
-		DetectedGame gd = detectedGames.front();
-
-		gameDescription._desc.gameId = gameId;
-		gameDescription._desc.language = gd.language;
-		gameDescription._desc.platform = gd.platform;
-		gameDescription._desc.guiOptions = GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES);
-		gameDescription._filename = filename;
-		gameDescription._md5 = Common::computeStreamMD5AsString(f, 5000);
-
-		ADDetectedGame dg((ADGameDescription *)&gameDescription);
-		results.push_back(dg);
-	}
-
-	return results;
-}
-
-#if PLUGIN_ENABLED_DYNAMIC(GARGOYLE)
-REGISTER_PLUGIN_DYNAMIC(Gargoyle, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
-#else
-REGISTER_PLUGIN_STATIC(GARGOYLE, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
-#endif
diff --git a/engines/gargoyle/detection_tables.h b/engines/gargoyle/detection_tables.h
deleted file mode 100644
index 30491f6..0000000
--- a/engines/gargoyle/detection_tables.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-namespace Gargoyle {
-
-static const GargoyleGameDescription gameDescriptions[] = {
-	{ AD_TABLE_END_MARKER, "", (InterpreterType)0, "" }
-};
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.cpp b/engines/gargoyle/events.cpp
deleted file mode 100644
index 6c0cd71..0000000
--- a/engines/gargoyle/events.cpp
+++ /dev/null
@@ -1,402 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/events.h"
-#include "gargoyle/conf.h"
-#include "gargoyle/gargoyle.h"
-#include "gargoyle/screen.h"
-#include "gargoyle/selection.h"
-#include "gargoyle/windows.h"
-#include "graphics/cursorman.h"
-
-namespace Gargoyle {
-
-const byte ARROW[] = {
-	// byte 1: number of skipped pixels
-	// byte 2: number of plotted pixels
-	// then, pixels
-	0, 1, 5,
-	0, 2, 5, 5,
-	0, 3, 5, 0xF7, 5,
-	0, 3, 5, 0xF7, 5,
-	0, 4, 5, 0xF7, 0xF7, 5,
-	0, 4, 5, 0xF7, 0xF7, 5,
-	0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
-	0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
-	0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 7, 5, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 6, 5, 0xF7, 0xF7, 0xF7, 0xF7, 5,
-	0, 5, 5, 0xF7, 0xF7, 0xF7, 5,
-	2, 3, 5, 0xF7, 5,
-	3, 3, 5, 0xF7, 5,
-	3, 3, 5, 0xF7, 5,
-	4, 2, 5, 5
-};
-
-Events::Events() : _forceClick(false), _currentEvent(nullptr), _cursorId(CURSOR_NONE),
-	_timerMilli(0), _timerTimeExpiry(0), _priorFrameTime(0), _frameCounter(0) {
-	initializeCursors();
-}
-
-Events::~Events() {
-	for (int idx = 1; idx < 3; ++idx)
-		_cursors[idx].free();
-}
-
-void Events::initializeCursors() {
-	const Graphics::PixelFormat format = g_system->getScreenFormat();
-	const int WHITE = format.RGBToColor(0xff, 0xff, 0xff);
-	const int BLACK = 0;
-	const int TRANSPARENT = format.RGBToColor(0x80, 0x80, 0x80);
-
-	// Setup arrow cursor
-	Surface &arr = _cursors[CURSOR_ARROW];
-	arr.create(8, 16, g_system->getScreenFormat());
-	arr.fillRect(Common::Rect(0, 0, 8, 16), TRANSPARENT);
-
-	const byte *p = ARROW;
-	for (int y = 0; y < 16; ++y) {
-		int offset = *p++;
-		int len = *p++;
-
-		for (int x = offset; x < (offset  + len); ++x, ++p) {
-			arr.hLine(x, y, x, (*p == 0xf7) ? WHITE : BLACK);
-		}
-	}
-
-	// Setup selection cusor sized to the vertical line size
-	Surface &sel = _cursors[CURSOR_IBEAM];
-	sel.create(5, g_conf->_leading, g_system->getScreenFormat());
-	sel.fillRect(Common::Rect(0, 0, sel.w, sel.h), TRANSPARENT);
-	sel.hLine(0, 0, 4, 0);
-	sel.hLine(0, sel.h - 1, 4, 0);
-	sel.vLine(2, 1, sel.h - 1, 0);
-	sel._hotspot = Common::Point(2, sel.h - 1);
-
-	// TODO: Hyperlink hand cursor
-}
-
-void Events::checkForNextFrameCounter() {
-	// Check for next game frame
-	uint32 milli = g_system->getMillis();
-	if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) {
-		++_frameCounter;
-		_priorFrameTime = milli;
-
-		if (_redraw)
-			g_vm->_windows->redraw();
-		_redraw = false;
-		g_vm->_screen->update();
-		return;
-	}
-}
-
-void Events::getEvent(event_t *event, bool polled) {
-	_currentEvent  = event;
-	event->clear();
-
-	dispatchEvent(*_currentEvent, polled);
-
-	if (!polled) {
-		while (!g_vm->shouldQuit() && _currentEvent->type == evtype_None && !isTimerExpired()) {
-			pollEvents();
-			g_system->delayMillis(10);
-
-			dispatchEvent(*_currentEvent, polled);
-		}
-
-		if (g_vm->shouldQuit())
-			_currentEvent->type = evtype_Quit;
-	}
-
-	if (_currentEvent->type == evtype_None && isTimerExpired()) {
-		store(evtype_Timer, nullptr, 0, 0);
-		dispatchEvent(*_currentEvent, polled);
-
-		_timerTimeExpiry = g_system->getMillis() + _timerMilli;
-	}
-
-	_currentEvent = nullptr;
-}
-
-void Events::store(EvType type, Window *win, uint32 val1, uint32 val2) {
-	Event ev(type, win, val1, val2);
-
-	switch (type) {
-	case evtype_Arrange:
-	case evtype_Redraw:
-	case evtype_SoundNotify:
-	case evtype_Timer:
-		_eventsPolled.push(ev);
-		break;
-
-	default:
-		_eventsLogged.push(ev);
-		break;
-	}
-}
-
-void Events::dispatchEvent(Event &ev, bool polled) {
-	Event dispatch;
-
-	if (!polled) {
-		dispatch = _eventsLogged.retrieve();
-		if (!dispatch)
-			dispatch = _eventsPolled.retrieve();
-	} else {
-		dispatch = _eventsPolled.retrieve();
-	}
-
-	if (dispatch)
-		ev = dispatch;
-}
-
-void Events::pollEvents() {
-	Common::Event event;
-
-	do {
-		checkForNextFrameCounter();
-		g_system->getEventManager()->pollEvent(event);
-
-		switch (event.type) {
-		case Common::EVENT_KEYDOWN:
-			setCursor(CURSOR_NONE);
-			handleKeyDown(event.kbd);
-			return;
-
-		case Common::EVENT_LBUTTONDOWN:
-		case Common::EVENT_RBUTTONDOWN:
-			handleButtonDown(event.type == Common::EVENT_LBUTTONDOWN, event.mouse);
-			return;
-
-		case Common::EVENT_LBUTTONUP:
-		case Common::EVENT_RBUTTONUP:
-			handleButtonUp(event.type == Common::EVENT_LBUTTONUP, event.mouse);
-			return;
-
-		case Common::EVENT_WHEELUP:
-		case Common::EVENT_WHEELDOWN:
-			setCursor(CURSOR_NONE);
-			handleScroll(event.type == Common::EVENT_WHEELUP);
-			return;
-
-		case Common::EVENT_MOUSEMOVE:
-			handleMouseMove(event.mouse);
-			break;
-
-		default:
-			break;
-		}
-	} while (event.type == Common::EVENT_MOUSEMOVE);
-}
-
-void Events::handleKeyDown(const Common::KeyState &ks) {
-	Clipboard &clipboard = *g_vm->_clipboard;
-	Windows &windows = *g_vm->_windows;
-
-	if (ks.flags & Common::KBD_CTRL) {
-		if (ks.keycode == Common::KEYCODE_a)
-			windows.inputHandleKey(keycode_Home);
-		else if (ks.keycode == Common::KEYCODE_c)
-			clipboard.clipboardSend(CLIPBOARD);
-		else if (ks.keycode == Common::KEYCODE_e)
-			windows.inputHandleKey(keycode_End);
-		else if (ks.keycode == Common::KEYCODE_u)
-			windows.inputHandleKey(keycode_Escape);
-		else if (ks.keycode == Common::KEYCODE_v)
-			clipboard.clipboardReceive(CLIPBOARD);
-		else if (ks.keycode == Common::KEYCODE_x)
-			clipboard.clipboardSend(CLIPBOARD);
-		else if (ks.keycode == Common::KEYCODE_LEFT || ks.keycode == Common::KEYCODE_KP4)
-			windows.inputHandleKey(keycode_SkipWordLeft);
-		else if (ks.keycode == Common::KEYCODE_RIGHT || ks.keycode == Common::KEYCODE_KP6)
-			windows.inputHandleKey(keycode_SkipWordRight);
-
-		return;
-	}
-
-	if (ks.flags & Common::KBD_ALT)
-		return;
-
-	switch (ks.keycode) {
-	case Common::KEYCODE_RETURN:
-		windows.inputHandleKey(keycode_Return);
-		break;
-	case Common::KEYCODE_BACKSPACE:
-		windows.inputHandleKey(keycode_Delete);
-		break;
-	case Common::KEYCODE_DELETE:
-		windows.inputHandleKey(keycode_Erase);
-		break;
-	case Common::KEYCODE_TAB:
-		windows.inputHandleKey(keycode_Tab);
-		break;
-	case Common::KEYCODE_PAGEUP:
-		windows.inputHandleKey(keycode_PageUp);
-		break;
-	case Common::KEYCODE_PAGEDOWN:
-		windows.inputHandleKey(keycode_PageDown);
-		break;
-	case Common::KEYCODE_HOME:
-		windows.inputHandleKey(keycode_Home);
-		break;
-	case Common::KEYCODE_END:
-		windows.inputHandleKey(keycode_End);
-		break;
-	case Common::KEYCODE_LEFT:
-		windows.inputHandleKey(keycode_Left);
-		break;
-	case Common::KEYCODE_RIGHT:
-		windows.inputHandleKey(keycode_Right);
-		break;
-	case Common::KEYCODE_UP:
-		windows.inputHandleKey(keycode_Up);
-		break;
-	case Common::KEYCODE_DOWN:
-		windows.inputHandleKey(keycode_Down);
-		break;
-	case Common::KEYCODE_ESCAPE:
-		windows.inputHandleKey(keycode_Escape);
-		break;
-	case Common::KEYCODE_F1:
-		windows.inputHandleKey(keycode_Func1);
-		break;
-	case Common::KEYCODE_F2:
-		windows.inputHandleKey(keycode_Func2);
-		break;
-	case Common::KEYCODE_F3:
-		windows.inputHandleKey(keycode_Func3);
-		break;
-	case Common::KEYCODE_F4:
-		windows.inputHandleKey(keycode_Func4);
-		break;
-	case Common::KEYCODE_F5:
-		windows.inputHandleKey(keycode_Func5);
-		break;
-	case Common::KEYCODE_F6:
-		windows.inputHandleKey(keycode_Func6);
-		break;
-	case Common::KEYCODE_F7:
-		windows.inputHandleKey(keycode_Func7);
-		break;
-	case Common::KEYCODE_F8:
-		windows.inputHandleKey(keycode_Func8);
-		break;
-	case Common::KEYCODE_F9:
-		windows.inputHandleKey(keycode_Func9);
-		break;
-	case Common::KEYCODE_F10:
-		windows.inputHandleKey(keycode_Func10);
-		break;
-	case Common::KEYCODE_F11:
-		windows.inputHandleKey(keycode_Func11);
-		break;
-	case Common::KEYCODE_F12:
-		windows.inputHandleKey(keycode_Func12);
-		break;
-	default:
-		windows.inputHandleKey(ks.ascii);
-		break;
-		break;
-	}
-}
-
-void Events::handleScroll(bool wheelUp) {
-	g_vm->_windows->inputHandleKey(wheelUp ? keycode_MouseWheelUp : keycode_MouseWheelDown);
-}
-
-void Events::handleMouseMove(const Point &pos) {
-	if (_cursorId == CURSOR_NONE)
-		setCursor(CURSOR_ARROW);
-
-	// hyperlinks and selection
-	// TODO: Properly handle commented out lines
-	if (g_vm->_copySelect) {
-		//gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_ibeam);
-		g_vm->_selection->moveSelection(pos);
-	} else {
-		if (g_vm->_selection->getHyperlink(pos)) {
-			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), gdk_hand);
-		} else {
-			//gdk_window_set_cursor((GTK_WIDGET(widget)->window), nullptr);
-		}
-	}
-}
-
-void Events::handleButtonDown(bool isLeft, const Point &pos) {
-	if (isLeft) {
-		setCursor(CURSOR_IBEAM);
-		g_vm->_windows->inputHandleClick(pos);
-	} else {
-		g_vm->_clipboard->clipboardReceive(PRIMARY);
-	}
-}
-
-void Events::handleButtonUp(bool isLeft, const Point &pos) {
-	if (isLeft) {
-		setCursor(CURSOR_ARROW);
-		g_vm->_copySelect = false;
-		g_vm->_clipboard->clipboardSend(PRIMARY);
-	}
-}
-
-void Events::waitForPress() {
-	Common::Event e;
-
-	do {
-		g_system->getEventManager()->pollEvent(e);
-		g_system->delayMillis(10);
-		checkForNextFrameCounter();
-	} while (!g_vm->shouldQuit() && e.type != Common::EVENT_KEYDOWN &&
-	         e.type != Common::EVENT_LBUTTONDOWN && e.type != Common::EVENT_RBUTTONDOWN &&
-	         e.type != Common::EVENT_MBUTTONDOWN);
-}
-
-void Events::setCursor(CursorId cursorId) {
-	if (cursorId != _cursorId) {
-		if (cursorId == CURSOR_NONE) {
-			CursorMan.showMouse(false);
-		} else {
-			if (!CursorMan.isVisible())
-				CursorMan.showMouse(true);
-
-			const Surface &s = _cursors[cursorId];
-			const int TRANSPARENT = s.format.RGBToColor(0x80, 0x80, 0x80);
-
-			CursorMan.replaceCursor(s.getPixels(), s.w, s.h, s._hotspot.x, s._hotspot.y, TRANSPARENT, true, &s.format);
-		}
-
-		_cursorId = cursorId;
-	}
-}
-
-void Events::setTimerInterval(uint milli) {
-	_timerMilli = milli;
-	_timerTimeExpiry = g_system->getMillis() + milli;
-}
-
-bool Events::isTimerExpired() const {
-	return _timerMilli && g_system->getMillis() >= _timerTimeExpiry;
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/events.h b/engines/gargoyle/events.h
deleted file mode 100644
index 745f1ae..0000000
--- a/engines/gargoyle/events.h
+++ /dev/null
@@ -1,291 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_EVENTS_H
-#define GARGOYLE_EVENTS_H
-
-#include "common/events.h"
-#include "graphics/surface.h"
-#include "gargoyle/utils.h"
-
-namespace Gargoyle {
-
-#define GAME_FRAME_RATE 100
-#define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE)
-
-class Window;
-
-/**
- * Event types
- */
-enum EvType {
-	evtype_None         = 0,
-	evtype_Timer        = 1,
-	evtype_CharInput    = 2,
-	evtype_LineInput    = 3,
-	evtype_MouseInput   = 4,
-	evtype_Arrange      = 5,
-	evtype_Redraw       = 6,
-	evtype_SoundNotify  = 7,
-	evtype_Hyperlink    = 8,
-	evtype_VolumeNotify = 9,
-
-	// ScummVM custom events
-	evtype_Quit         = 99
-};
-
-/**
- * Keycodes
- */
-enum Keycode {
-	keycode_Unknown  = 0xffffffffU,
-	keycode_Left     = 0xfffffffeU,
-	keycode_Right    = 0xfffffffdU,
-	keycode_Up       = 0xfffffffcU,
-	keycode_Down     = 0xfffffffbU,
-	keycode_Return   = 0xfffffffaU,
-	keycode_Delete   = 0xfffffff9U,
-	keycode_Escape   = 0xfffffff8U,
-	keycode_Tab      = 0xfffffff7U,
-	keycode_PageUp   = 0xfffffff6U,
-	keycode_PageDown = 0xfffffff5U,
-	keycode_Home     = 0xfffffff4U,
-	keycode_End      = 0xfffffff3U,
-	keycode_Func1    = 0xffffffefU,
-	keycode_Func2    = 0xffffffeeU,
-	keycode_Func3    = 0xffffffedU,
-	keycode_Func4    = 0xffffffecU,
-	keycode_Func5    = 0xffffffebU,
-	keycode_Func6    = 0xffffffeaU,
-	keycode_Func7    = 0xffffffe9U,
-	keycode_Func8    = 0xffffffe8U,
-	keycode_Func9    = 0xffffffe7U,
-	keycode_Func10   = 0xffffffe6U,
-	keycode_Func11   = 0xffffffe5U,
-	keycode_Func12   = 0xffffffe4U,
-
-	// non standard keycodes
-	keycode_Erase          = 0xffffef7fU,
-	keycode_MouseWheelUp   = 0xffffeffeU,
-	keycode_MouseWheelDown = 0xffffefffU,
-	keycode_SkipWordLeft   = 0xfffff000U,
-	keycode_SkipWordRight  = 0xfffff001U,
-
-	// The last keycode is always = 0x100000000 - keycode_MAXVAL)
-	keycode_MAXVAL = 28U
-};
-
-/**
- * List of cursors
- */
-enum CursorId {
-	CURSOR_NONE = 0,
-	CURSOR_ARROW = 1,
-	CURSOR_IBEAM = 2,
-	CURSOR_HAND = 3
-};
-
-/**
- * Event structure
- */
-struct Event {
-	EvType type;
-	Window *window;
-	uint32 val1, val2;
-
-	/**
-	 * Constructor
-	 */
-	Event() {
-		clear();
-	}
-
-	/**
-	 * Constructor
-	 */
-	Event(EvType evType, Window *evWindow, uint32 evVal1, uint32 evVal2) {
-		type = evType;
-		window = evWindow;
-		val1 = evVal1;
-		val2 = evVal2;
-	}
-
-	/**
-	 * Clear
-	 */
-	void clear() {
-		type = evtype_None;
-		window = nullptr;
-		val1 = val2 = 0;
-	}
-
-	/**
-	 * Boolean cast to allow checking whether event is filled out
-	 */
-	operator bool() const {
-		return type != evtype_None;
-	}
-};
-typedef Event event_t;
-
-class EventQueue : public Common::Queue<Event> {
-public:
-	/**
-	 * Retrieve a pending event, if any
-	 */
-	Event retrieve() {
-		return empty() ? Event() : pop();
-	}
-};
-
-/**
- * Events manager
- */
-class Events {
-	struct Surface : public Graphics::Surface {
-		Common::Point _hotspot;
-	};
-private:
-	EventQueue _eventsPolled;       ///< User generated events
-	EventQueue _eventsLogged;       ///< Custom events generated by game code
-	Event *_currentEvent;           ///< Event pointer passed during event retrieval
-	uint32 _priorFrameTime;         ///< Time of prior game frame
-	uint32 _frameCounter;           ///< Frame counter
-	bool _redraw;                   ///< Screen needed redrawing
-	CursorId _cursorId;             ///< Current cursor Id
-	Surface _cursors[4];            ///< Cursor pixel data
-	uint _timerMilli;               ///< Time in milliseconds between timer events
-	uint _timerTimeExpiry;          ///< When to trigger next timer event
-private:
-	/**
-	 * Initialize the cursor graphics
-	 */
-	void initializeCursors();
-
-	/**
-	 * Checks for whether it's time for the next game frame
-	 */
-	void checkForNextFrameCounter();
-
-	/**
-	 * Dispatches an event
-	 */
-	void dispatchEvent(Event &ev, bool polled);
-
-	/**
-	 * Poll for user events
-	 */
-	void pollEvents();
-
-	/**
-	 * Handle a key down event
-	 */
-	void handleKeyDown(const Common::KeyState &ks);
-
-	/**
-	 * Handle scroll events
-	 */
-	void handleScroll(bool wheelUp);
-
-	/**
-	 * Handle mouse move events
-	 */
-	void handleMouseMove(const Point &pos);
-
-	/**
-	 * Handle mouse down events
-	 */
-	void handleButtonDown(bool isLeft, const Point &pos);
-
-	/**
-	 * Handle mouse up events
-	 */
-	void handleButtonUp(bool isLeft, const Point &pos);
-public:
-	bool _forceClick;
-public:
-	/**
-	 * Constructor
-	 */
-	Events();
-
-	/**
-	 * Destructor
-	 */
-	~Events();
-
-	/**
-	  * Get any pending event
-	  */
-	void getEvent(event_t *event, bool polled);
-
-	/**
-	 * Store an event for retrieval
-	 */
-	void store(EvType type, Window *win, uint32 val1 = 0, uint32 val2 = 0);
-
-	/**
-	 * Wait for a keyboard or mouse press
-	 */
-	void waitForPress();
-
-	/**
-	 * Get the total number of frames played
-	 */
-	uint32 getTotalPlayTicks() const {
-		return _frameCounter;
-	}
-
-	/**
-	 * Set the total number of frames played
-	 */
-	void setTotalPlayTicks(uint frames) {
-		_frameCounter = frames;
-	}
-
-	/**
-	 * Flags the screen for redrawing
-	 */
-	void redraw() {
-		_redraw = true;
-	}
-
-	/**
-	 * Sets the current cursor
-	 */
-	void setCursor(CursorId cursorId);
-
-	/**
-	 * Set a timer interval
-	 * @param   milli       Time in millieseconds for intervals, or 0 for off
-	 */
-	void setTimerInterval(uint milli);
-
-	/**
-	 * Returns true if it's time for a timer event
-	 */
-	bool isTimerExpired() const;
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/fonts.cpp b/engines/gargoyle/fonts.cpp
deleted file mode 100644
index 9c07412..0000000
--- a/engines/gargoyle/fonts.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/fonts.h"
-#include "gargoyle/glk_types.h"
-#include "gargoyle/conf.h"
-#include "gargoyle/gargoyle.h"
-#include "common/memstream.h"
-#include "common/unzip.h"
-#include "graphics/fonts/ttf.h"
-#include "graphics/fontman.h"
-
-namespace Gargoyle {
-
-#define FONTS_VERSION 1.0
-#define FONTS_FILENAME "fonts.dat"
-
-Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface), _fontsMissing(false) {
-	if (!loadFonts())
-		error("Could not load data file");
-
-	// TODO: See if there's any better way for getting the leading and baseline
-	Common::Rect r1 = _fontTable[7]->getBoundingBox('o');
-	Common::Rect r2 = _fontTable[7]->getBoundingBox('y');
-	double baseLine = (double)r1.bottom;
-	double leading = (double)r2.bottom + 2;
-
-	g_conf->_leading = MAX((double)g_conf->_leading, leading);
-	g_conf->_baseLine = MAX((double)g_conf->_baseLine, baseLine);
-	g_conf->_cellW = _fontTable[0]->getStringWidth("0");
-	g_conf->_cellH = g_conf->_leading;
-}
-
-Fonts::~Fonts() {
-	for (int idx = 0; idx < FONTS_TOTAL; ++idx)
-		delete _fontTable[idx];
-}
-
-bool Fonts::loadFonts() {
-	Common::Archive *archive = nullptr;
-
-	if (!Common::File::exists(FONTS_FILENAME) || (archive = Common::makeZipArchive(FONTS_FILENAME)) == nullptr)
-		return false;
-
-	// Open the version.txt file within it to validate the version
-	Common::File f;
-	if (!f.open("version.txt", *archive)) {
-		delete archive;
-		return false;
-	}
-
-	// Validate the version
-	char buffer[4];
-	f.read(buffer, 3);
-	buffer[3] = '\0';
-
-	if (Common::String(buffer) != "1.0") {
-		delete archive;
-		return false;
-	}
-
-	// R ead in the fonts
-	double monoAspect = g_conf->_monoAspect;
-	double propAspect = g_conf->_propAspect;
-	double monoSize = g_conf->_monoSize;
-	double propSize = g_conf->_propSize;
-
-	_fontTable[0] = loadFont(MONOR, archive, monoSize, monoAspect, FONTR);
-	_fontTable[1] = loadFont(MONOB, archive, monoSize, monoAspect, FONTB);
-	_fontTable[2] = loadFont(MONOI, archive, monoSize, monoAspect, FONTI);
-	_fontTable[3] = loadFont(MONOZ, archive, monoSize, monoAspect, FONTZ);
-
-	_fontTable[4] = loadFont(PROPR, archive, propSize, propAspect, FONTR);
-	_fontTable[5] = loadFont(PROPB, archive, propSize, propAspect, FONTB);
-	_fontTable[6] = loadFont(PROPI, archive, propSize, propAspect, FONTI);
-	_fontTable[7] = loadFont(PROPZ, archive, propSize, propAspect, FONTZ);
-
-	delete archive;
-	return true;
-}
-
-const Graphics::Font *Fonts::loadFont(FACES face, Common::Archive *archive, double size, double aspect, int
- style) {
-	const char *const FILENAMES[8] = {
-		"GoMono-Regular.ttf", "GoMono-Bold.ttf", "GoMono-Italic.ttf", "GoMono-Bold-Italic.ttf",
-		"NotoSerif-Regular.ttf", "NotoSerif-Bold.ttf", "NotoSerif-Italic.ttf", "NotoSerif-Bold-Italic.ttf"
-	};
-	
-	Common::File f;
-	if (!f.open(FILENAMES[face], *archive))
-		error("Could not load font");
-
-	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
-}
-
-FACES Fonts::getId(const Common::String &name) {
-	if (name == "monor") return MONOR;
-	if (name == "monob") return MONOB;
-	if (name == "monoi") return MONOI;
-	if (name == "monoz") return MONOZ;
-	if (name == "propr") return PROPR;
-	if (name == "propb") return PROPB;
-	if (name == "propi") return PROPI;
-	if (name == "propz") return PROPZ;
-	return MONOR;
-}
-
-int Fonts::drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
-	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
-	const Graphics::Font *font = _fontTable[fontIdx];
-	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
-	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
-
-	pt.x += font->getStringWidth(text);
-	return MIN((int)pt.x, (int)_surface->w) * GLI_SUBPIX;
-}
-
-int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
-	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
-	const Graphics::Font *font = _fontTable[fontIdx];
-	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
-	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
-
-	pt.x += font->getStringWidth(text);
-	return MIN((int)pt.x, (int)_surface->w) * GLI_SUBPIX;
-}
-
-size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {
-	const Graphics::Font *font = _fontTable[fontIdx];
-	return font->getStringWidth(text) * GLI_SUBPIX;
-}
-
-size_t Fonts::stringWidthUni(int fontIdx, const Common::U32String &text, int spw) {
-	const Graphics::Font *font = _fontTable[fontIdx];
-	return font->getStringWidth(text) * GLI_SUBPIX;
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/fonts.h b/engines/gargoyle/fonts.h
deleted file mode 100644
index 737618e..0000000
--- a/engines/gargoyle/fonts.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FONTS_H
-#define GARGOYLE_FONTS_H
-
-#include "gargoyle/glk_types.h"
-#include "gargoyle/utils.h"
-#include "common/archive.h"
-#include "common/array.h"
-#include "common/file.h"
-#include "common/str.h"
-#include "common/ustr.h"
-#include "graphics/font.h"
-
-namespace Gargoyle {
-
-#define FONTS_TOTAL 8
-
-enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
-enum TYPES { MONOF, PROPF };
-enum STYLES { FONTR, FONTB, FONTI, FONTZ };
-
-/**
- * Fonts manager
- */
-class Fonts {
-private:
-	Graphics::ManagedSurface *_surface;
-	const Graphics::Font *_fontTable[FONTS_TOTAL];
-	bool _fontsMissing;
-private:
-	/**
-	 * Load all the fonts
-	 */
-	bool loadFonts();
-
-	/**
-	 * Load a single font
-	 */
-	const Graphics::Font *loadFont(FACES face, Common::Archive *archive, double size, double aspect, int style);
-public:
-	/**
-	 * Get the index/id of a font by name
-	 */
-	static FACES getId(const Common::String &name);
-public:
-	/**
-	 * Constructor
-	 */
-	Fonts(Graphics::ManagedSurface *surface);
-
-	/**
-	 * Destructor
-	 */
-	virtual ~Fonts();
-
-	/**
-	 * Draws a string using the specified font at the given co-ordinates
-	 * @param pos       Position for the bottom-left corner the text will be drawn with
-	 * @param fontIdx   Which font to use
-	 * @param rgb       RGB tuplet specifying the text color
-	 * @param text      The text to draw
-	 * @param spw       ??
-	 */
-	int drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
-
-	/**
-	 * Draws a unicode string using the specified font at the given co-ordinates
-	 * @param pos       Position for the bottom-left corner the text will be drawn with
-	 * @param fontIdx   Which font to use
-	 * @param rgb       RGB tuplet specifying the text color
-	 * @param text      The text to draw
-	 * @param spw       ??
-	 */
-	int drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
-
-	/**
-	 * Get the width in pixels of a string
-	 * @param fontIdx   Which font to use
-	 * @param text      Text to get the width of
-	 * @param spw       Delta X
-	 * @returns         Width of string multiplied by GLI_SUBPIX
-	 */
-	size_t stringWidth(int fontIdx, const Common::String &text, int spw = 0);
-
-	/**
-	 * Get the width in pixels of a unicode string
-	 * @param fontIdx   Which font to use
-	 * @param text      Text to get the width of
-	 * @param spw       Delta X
-	 * @returns         Width of string multiplied by GLI_SUBPIX
-	 */
-	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = 0);
-};
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/detection.cpp b/engines/gargoyle/frotz/detection.cpp
deleted file mode 100644
index fff8005..0000000
--- a/engines/gargoyle/frotz/detection.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/detection.h"
-#include "common/file.h"
-#include "common/md5.h"
-
-#include "gargoyle/frotz/detection_tables.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
-	const char *const EXTENSIONS[9] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb" };
-
-	// 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 (int idx = 0; idx < 9 && !hasExt; ++idx)
-			hasExt = filename.hasSuffixIgnoreCase(EXTENSIONS[idx]);
-		if (!hasExt)
-			continue;
-
-		// Open up the file and calculate the md5
-		Common::File gameFile;
-		if (!gameFile.open(*file))
-			continue;
-		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
-		size_t filesize = gameFile.size();
-		gameFile.close();
-
-		// Check for known game
-		const FrotzGameDescription *p = FROTZ_GAMES;
-		while (p->_gameId && p->_md5 && (md5 != p->_md5 || filesize != p->_filesize))
-			++p;
-
-		DetectedGame gd;
-		if (!p->_gameId) {
-			// Generic .dat files don't get reported as matches unless they have a known md5
-			if (filename.hasSuffixIgnoreCase(".dat"))
-				continue;
-
-			warning("Uknown zcode game %s - %s %d", filename.c_str(), md5.c_str(), filesize);
-			gd = DetectedGame("zcode", "Unrecognised zcode game", Common::UNK_LANG, Common::kPlatformUnknown);
-		} else {
-			gd = DetectedGame(p->_gameId, p->_description, p->_language, Common::kPlatformUnknown, p->_extra);
-		}
-
-		gd.addExtraEntry("filename", filename);
-		gameList.push_back(gd);
-	}
-
-	return !gameList.empty();
-}
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/detection.h b/engines/gargoyle/frotz/detection.h
deleted file mode 100644
index 2e70d7c..0000000
--- a/engines/gargoyle/frotz/detection.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FROTZ_DETECTION
-#define GARGOYLE_FROTZ_DETECTION
-
-#include "common/fs.h"
-#include "engines/game.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-class FrotzMetaEngine {
-public:
-	/**
-	 * Detect supported games
-	 */
-	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
-};
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/detection_tables.cpp b/engines/gargoyle/frotz/detection_tables.cpp
deleted file mode 100644
index af856ed..0000000
--- a/engines/gargoyle/frotz/detection_tables.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/detection_tables.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-const char *const AMFV_DESC = "A Mind Forever Voyaging";
-const char *const ARTHUR_DESC = "Arthur: The Quest for Excalibur";
-const char *const BALLYHOO_DESC = "Ballyhoo";
-const char *const BEYONDZORK_DESC = "Beyond Zork";
-const char *const BORDERZONE_DESC = "Border Zone";
-const char *const BUREAUCRACY_DESC = "Bureaucracy";
-const char *const CUTTHROATS_DESC = "Cutthroats";
-const char *const DEADLINE_DESC = "Deadline";
-const char *const ENCHANTER_DESC = "Enchanter";
-const char *const HHGTTG_DESC = "The Hitchhiker's Guide to the Galaxy";
-const char *const HIJINX_DESC = "Hollywood Hijinx";
-const char *const INFIDEL_DESC = "Infidel";
-const char *const JOURNEY_DESC = "Journey";
-const char *const LGOP_DESC = "Leather Goddesses of Phobos";
-const char *const LGOP2_DESC = "Leather Goddesses of Phobos 2";
-const char *const LURKING_DESC = "The Lurking Horror";
-const char *const MINIZORK1_DESC = "Mini Zork I: The Great Underground Empire";
-const char *const MOONMIST_DESC = "Moonmist";
-const char *const NORDBERT_DESC = "Nord and Bert Couldn't Make Head or Tail of It";
-const char *const PLANETFALL_DESC = "Planetfall";
-const char *const PLUNDERED_DESC = "Plundered Hearts";
-const char *const SAMPLER1_DESC = "Infocom Sampler 1";
-const char *const SAMPLER2_DESC = "Infocom Sampler 2";
-const char *const SEASTALKER_DESC = "Seastalker";
-const char *const SHERLOCKRIDDLE_DESC = "Sherlock: The Riddle of the Crown Jewels";
-const char *const SHOGUN_DESC = "James Clavell's Shogun";
-const char *const SORCERER_DESC = "Sorcerer";
-const char *const SPELLBREAKER_DESC = "Spellbreaker";
-const char *const STARCROSS_DESC = "Starcross";
-const char *const STATIONFALL_DESC = "Stationfall";
-const char *const SUSPECT_DESC = "Suspect";
-const char *const SUSPENDED_DESC = "Suspended";
-const char *const TRINITY_DESC = "Trinity";
-const char *const WISHBRINGER_DESC = "Wishbringer";
-const char *const WITNESS_DESC = "The Witness";
-const char *const ZORK0_DESC = "Zork Zero: The Revenge of Megaboz";
-const char *const ZORK1_DESC = "Zork I: The Great Underground Empire";
-const char *const ZORK2_DESC = "Zork II: The Wizard of Frobozz";
-const char *const ZORK3_DESC = "Zork III: The Dungeon Master";
-const char *const ZTUU_DESC = "Zork: The Undiscovered Underground";
-
-#define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
-#define ENTRY0(ID, DESCRIPTION, VERSION, MD5, FILESIZE) { ID, DESCRIPTION##_DESC, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
-#define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
-
-const FrotzGameDescription FROTZ_GAMES[] = {
-	ENTRY0("hhgttg", HHGTTG, "v31 Solid Gold", "379022bcd4ec74b90274c6100c33f579", 158412),
-	ENTRY0("hhgttg", HHGTTG, "v47", "fdda8f4239819402c62db866bb61a648", 112622),
-	ENTRY0("hhgttg", HHGTTG, "v56", "a214fcb42bc9f554d07d983a12f6a062", 113444),
-	ENTRY0("hhgttg", HHGTTG, "v58", "e867d49ad1fb9406ff4e0678a4ee2ac9", 113332),
-	ENTRY0("hhgttg", HHGTTG, "v59", "34f6abc1f2a42be127ef434fc475f0ee", 113334),
-
-	FROTZ_TABLE_END_MARKER
-};
-
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/detection_tables.h b/engines/gargoyle/frotz/detection_tables.h
deleted file mode 100644
index 48e0eb0..0000000
--- a/engines/gargoyle/frotz/detection_tables.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "engines/advancedDetector.h"
-#include "common/language.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-/**
- * Game descriptor for ZCode games
- */
-struct FrotzGameDescription {
-	const char *const _gameId;
-	const char *const _description;
-	const char *const _extra;
-	const char *const _md5;
-	size_t _filesize;
-	Common::Language _language;
-	const char *const _guiOptions;
-};
-
-extern const FrotzGameDescription FROTZ_GAMES[];
-extern const char *const AMFV_DESC;
-extern const char *const ARTHUR_DESC;
-extern const char *const BALLYHOO_DESC;
-extern const char *const BEYONDZORK_DESC;
-extern const char *const BORDERZONE_DESC;
-extern const char *const BUREAUCRACY_DESC;
-extern const char *const CUTTHROATS_DESC;
-extern const char *const DEADLINE_DESC;
-extern const char *const ENCHANTER_DESC;
-extern const char *const HHGTTG_DESC;
-extern const char *const HIJINX_DESC;
-extern const char *const INFIDEL_DESC;
-extern const char *const JOURNEY_DESC;
-extern const char *const LGOP_DESC;
-extern const char *const LGOP2_DESC;
-extern const char *const LURKING_DESC;
-extern const char *const MINIZORK1_DESC;
-extern const char *const MOONMIST_DESC;
-extern const char *const NORDBERT_DESC;
-extern const char *const PLANETFALL_DESC;
-extern const char *const PLUNDERED_DESC;
-extern const char *const SAMPLER1_DESC;
-extern const char *const SAMPLER2_DESC;
-extern const char *const SEASTALKER_DESC;
-extern const char *const SHERLOCKRIDDLE_DESC;
-extern const char *const SHOGUN_DESC;
-extern const char *const SORCERER_DESC;
-extern const char *const SPELLBREAKER_DESC;
-extern const char *const STARCROSS_DESC;
-extern const char *const STATIONFALL_DESC;
-extern const char *const SUSPECT_DESC;
-extern const char *const SUSPENDED_DESC;
-extern const char *const TRINITY_DESC;
-extern const char *const WISHBRINGER_DESC;
-extern const char *const WITNESS_DESC;
-extern const char *const ZORK0_DESC;
-extern const char *const ZORK1_DESC;
-extern const char *const ZORK2_DESC;
-extern const char *const ZORK3_DESC;
-extern const char *const ZTUU_DESC;
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/frotz.cpp b/engines/gargoyle/frotz/frotz.cpp
deleted file mode 100644
index 3a7b913..0000000
--- a/engines/gargoyle/frotz/frotz.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/frotz.h"
-#include "gargoyle/frotz/frotz_types.h"
-#include "common/config-manager.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-Frotz *g_vm;
-
-Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-		Processor(syst, gameDesc) {
-	g_vm = this;
-}
-
-Frotz::~Frotz() {
-	reset_memory();
-}
-
-void Frotz::runGame(Common::SeekableReadStream *gameFile) {
-	story_fp = gameFile;
-	initialize();
-
-	// Game loop
-	interpret();
-}
-
-void Frotz::initialize() {
-	if (ConfMan.hasKey("attribute_assignment") && ConfMan.getBool("attribute_assignment"))
-		_attribute_assignment = true;
-	if (ConfMan.hasKey("attribute_testing") && ConfMan.getBool("attribute_testing"))
-		_attribute_testing = true;
-	if (ConfMan.hasKey("ignore_errors") && ConfMan.getBool("ignore_errors"))
-		_ignore_errors = true;
-	if (ConfMan.hasKey("object_movement") && ConfMan.getBool("object_movement"))
-		_object_movement = true;
-	if (ConfMan.hasKey("object_locating") && ConfMan.getBool("object_locating"))
-		_object_locating = true;
-	if (ConfMan.hasKey("piracy") && ConfMan.getBool("piracy"))
-		_piracy = true;
-	if (ConfMan.hasKey("save_quetzal") && ConfMan.getBool("save_quetzal"))
-		_save_quetzal = true;
-	if (ConfMan.hasKey("random_seed"))
-		_random.setSeed(ConfMan.getInt("random_seed"));
-	if (ConfMan.hasKey("script_cols"))
-		_script_cols = ConfMan.getInt("script_cols");
-	if (ConfMan.hasKey("tandy_bit") && ConfMan.getBool("tandy_bit"))
-		_user_tandy_bit = true;
-	if (ConfMan.hasKey("undo_slots"))
-		_undo_slots = ConfMan.getInt("undo_slots");
-	if (ConfMan.hasKey("expand_abbreviations") && ConfMan.getBool("expand_abbreviations"))
-		_expand_abbreviations = true;
-	if (ConfMan.hasKey("err_report_mode")) {
-		_err_report_mode = ConfMan.getInt("err_report_mode");
-		if ((_err_report_mode < ERR_REPORT_NEVER) || (_err_report_mode > ERR_REPORT_FATAL))
-			_err_report_mode = ERR_DEFAULT_REPORT_MODE;
-	}
-
-	// Call process initialization
-	Processor::initialize();
-
-	// Restart the game
-	z_restart();
-}
-
-Common::Error Frotz::loadGameState(int slot) {
-	// TODO
-	return Common::kNoError;
-}
-
-Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
-	// TODO
-	return Common::kNoError;
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/frotz.h b/engines/gargoyle/frotz/frotz.h
deleted file mode 100644
index f7211f7..0000000
--- a/engines/gargoyle/frotz/frotz.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FROTZ_FROTZ
-#define GARGOYLE_FROTZ_FROTZ
-
-#include "gargoyle/frotz/processor.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-/**
- * Frotz interpreter for Z-code games
- */
-class Frotz : public Processor {
-public:
-	/**
-	 * Constructor
-	 */
-	Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc);
-
-	/**
-	 * Destructor
-	 */
-	virtual ~Frotz();
-
-	/**
-	 * Initialization
-	 */
-	void initialize();
-
-	/**
-	 * Execute the game
-	 */
-	virtual void runGame(Common::SeekableReadStream *gameFile) override;
-
-	/**
-	 * Load a savegame
-	 */
-	virtual Common::Error loadGameState(int slot) override;
-
-	/**
-	 * Save the game
-	 */
-	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
-};
-
-extern Frotz *g_vm;
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/frotz_types.h b/engines/gargoyle/frotz/frotz_types.h
deleted file mode 100644
index 6b4fe67..0000000
--- a/engines/gargoyle/frotz/frotz_types.h
+++ /dev/null
@@ -1,297 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FROTZ_FROTZ_TYPES
-#define GARGOYLE_FROTZ_FROTZ_TYPES
-
-#include "gargoyle/glk_types.h"
-#include "common/algorithm.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-#define MAX_UNDO_SLOTS 500
-#define STACK_SIZE 32768
-
-#define lo(v)	(v & 0xff)
-#define hi(v)	(v >> 8)
-
-/* There are four error reporting modes: never report errors;
- * report only the first time a given error type occurs;
- * report every time an error occurs;
- * or treat all errors as fatal errors, killing the interpreter.
- * I strongly recommend "report once" as the default. But you can compile in a
- * different default by changing the definition of ERR_DEFAULT_REPORT_MODE.
- */
-enum ErrorReport {
-	ERR_REPORT_NEVER  = 0,
-	ERR_REPORT_ONCE   = 1,
-	ERR_REPORT_ALWAYS = 2,
-	ERR_REPORT_FATAL  = 3,
-
-	ERR_DEFAULT_REPORT_MODE = ERR_REPORT_NEVER
-};
-
-/**
- * Character codes
- */
-enum ZCode {
-	ZC_TIME_OUT      = 0x00,
-	ZC_NEW_STYLE     = 0x01,
-	ZC_NEW_FONT      = 0x02,
-	ZC_BACKSPACE     = 0x08,
-	ZC_INDENT        = 0x09,
-	ZC_GAP           = 0x0b,
-	ZC_RETURN        = 0x0d,
-	ZC_HKEY_MIN      = 0x0e,
-	ZC_HKEY_RECORD   = 0x0e,
-	ZC_HKEY_PLAYBACK = 0x0f,
-	ZC_HKEY_SEED     = 0x10,
-	ZC_HKEY_UNDO     = 0x11,
-	ZC_HKEY_RESTART  = 0x12,
-	ZC_HKEY_QUIT     = 0x13,
-	ZC_HKEY_DEBUG    = 0x14,
-	ZC_HKEY_HELP     = 0x15,
-	ZC_HKEY_MAX      = 0x15,
-	ZC_ESCAPE        = 0x1b,
-	ZC_ASCII_MIN     = 0x20,
-	ZC_ASCII_MAX     = 0x7e,
-	ZC_BAD           = 0x7f,
-	ZC_ARROW_MIN     = 0x81,
-	ZC_ARROW_UP      = 0x81,
-	ZC_ARROW_DOWN    = 0x82,
-	ZC_ARROW_LEFT    = 0x83,
-	ZC_ARROW_RIGHT   = 0x84,
-	ZC_ARROW_MAX     = 0x84,
-	ZC_FKEY_MIN      = 0x85,
-	ZC_FKEY_MAX      = 0x90,
-	ZC_NUMPAD_MIN    = 0x91,
-	ZC_NUMPAD_MAX    = 0x9a,
-	ZC_SINGLE_CLICK  = 0x9b,
-	ZC_DOUBLE_CLICK  = 0x9c,
-	ZC_MENU_CLICK    = 0x9d,
-	ZC_LATIN1_MIN    = 0xa0,
-	ZC_LATIN1_MAX    = 0xff
-};
-
-enum Story {
-	BEYOND_ZORK,
-	SHERLOCK,
-	ZORK_ZERO,
-	SHOGUN,
-	ARTHUR,
-	JOURNEY,
-	LURKING_HORROR,
-	UNKNOWN
-};
-
-enum Version {
-	V1 = 1,
-	V2 = 2,
-	V3 = 3,
-	V4 = 4,
-	V5 = 5,
-	V6 = 6,
-	V7 = 7,
-	V8 = 8,
-	V9 = 9
-};
-
-enum ConfigFlag {
-	CONFIG_BYTE_SWAPPED = 0x01, ///< Story file is byte swapped         - V3 
-	CONFIG_TIME         = 0x02, ///< Status line displays time          - V3 
-	CONFIG_TWODISKS     = 0x04, ///< Story file occupied two disks      - V3 
-	CONFIG_TANDY        = 0x08, ///< Tandy licensed game                - V3 
-	CONFIG_NOSTATUSLINE = 0x10, ///< Interpr can't support status lines - V3 
-	CONFIG_SPLITSCREEN  = 0x20, ///< Interpr supports split screen mode - V3 
-	CONFIG_PROPORTIONAL = 0x40, ///< Interpr uses proportional font     - V3 
-	
-	CONFIG_COLOUR       = 0x01, ///< Interpr supports colour            - V5+
-	CONFIG_PICTURES	    = 0x02, ///< Interpr supports pictures	        - V6 
-	CONFIG_BOLDFACE     = 0x04, ///< Interpr supports boldface style    - V4+
-	CONFIG_EMPHASIS     = 0x08, ///< Interpr supports emphasis style    - V4+
-	CONFIG_FIXED        = 0x10, ///< Interpr supports fixed width style - V4+
-	CONFIG_SOUND	    = 0x20, ///< Interpr supports sound             - V6 
-	CONFIG_TIMEDINPUT   = 0x80, ///< Interpr supports timed input       - V4+
-	
-	SCRIPTING_FLAG	  = 0x0001, ///< Outputting to transscription file  - V1+
-	FIXED_FONT_FLAG   = 0x0002, ///< Use fixed width font               - V3+
-	REFRESH_FLAG 	  = 0x0004, ///< Refresh the screen                 - V6 
-	GRAPHICS_FLAG	  = 0x0008, ///< Game wants to use graphics         - V5+
-	OLD_SOUND_FLAG	  = 0x0010, ///< Game wants to use sound effects    - V3 
-	UNDO_FLAG	  = 0x0010, ///< Game wants to use UNDO feature     - V5+
-	MOUSE_FLAG	  = 0x0020, ///< Game wants to use a mouse          - V5+
-	COLOUR_FLAG	  = 0x0040, ///< Game wants to use colours          - V5+
-	SOUND_FLAG	  = 0x0080, ///< Game wants to use sound effects    - V5+
-	MENU_FLAG	  = 0x0100  ///< Game wants to use menus            - V6 
-};
-
-enum {
-	TRANSPARENT_FLAG = 0x0001 ///< Game wants to use transparency     - V6
-};
-
-enum ErrorCode {
-	ERR_TEXT_BUF_OVF   = 1,		///< Text buffer overflow
-	ERR_STORE_RANGE    = 2,		///< Store out of dynamic memory
-	ERR_DIV_ZERO       = 3,		///< Division by zero
-	ERR_ILL_OBJ        = 4,		///< Illegal object
-	ERR_ILL_ATTR       = 5,		///< Illegal attribute
-	ERR_NO_PROP        = 6,		///< No such property
-	ERR_STK_OVF        = 7,		///< Stack overflow
-	ERR_ILL_CALL_ADDR  = 8,		///< Call to illegal address
-	ERR_CALL_NON_RTN   = 9,		///< Call to non-routine
-	ERR_STK_UNDF       = 10,	///< Stack underflow
-	ERR_ILL_OPCODE     = 11,	///< Illegal opcode
-	ERR_BAD_FRAME      = 12,	///< Bad stack frame
-	ERR_ILL_JUMP_ADDR  = 13,	///< Jump to illegal address
-	ERR_SAVE_IN_INTER  = 14,	///< Can't save while in interrupt
-	ERR_STR3_NESTING   = 15,	///< Nesting stream #3 too deep
-	ERR_ILL_WIN        = 16,	///< Illegal window
-	ERR_ILL_WIN_PROP   = 17,	///< Illegal window property
-	ERR_ILL_PRINT_ADDR = 18,	///< Print at illegal address
-	ERR_DICT_LEN       = 19,	///< Illegal dictionary word length
-	ERR_MAX_FATAL      = 19,
-
-	// Less serious errors
-	ERR_JIN_0            = 20,	///< @jin called with object 0
-	ERR_GET_CHILD_0      = 21,	///< @get_child called with object 0
-	ERR_GET_PARENT_0     = 22,	///< @get_parent called with object 0
-	ERR_GET_SIBLING_0    = 23,	///< @get_sibling called with object 0
-	ERR_GET_PROP_ADDR_0  = 24,	///< @get_prop_addr called with object 0
-	ERR_GET_PROP_0       = 25,	///< @get_prop called with object 0
-	ERR_PUT_PROP_0       = 26,	///< @put_prop called with object 0
-	ERR_CLEAR_ATTR_0     = 27,	///< @clear_attr called with object 0
-	ERR_SET_ATTR_0       = 28,	///< @set_attr called with object 0
-	ERR_TEST_ATTR_0      = 29,	///< @test_attr called with object 0
-	ERR_MOVE_OBJECT_0    = 30,	///< @move_object called moving object 0
-	ERR_MOVE_OBJECT_TO_0 = 31,	///< @move_object called moving into object 0
-	ERR_REMOVE_OBJECT_0  = 32,	///< @remove_object called with object 0
-	ERR_GET_NEXT_PROP_0  = 33,	///< @get_next_prop called with object 0
-	ERR_NUM_ERRORS       = 33
-};
-
-enum FrotzInterp {
-	INTERP_DEFAULT    =  0,
-	INTERP_DEC_20     =  1,
-	INTERP_APPLE_IIE  =  2,
-	INTERP_MACINTOSH  =  3,
-	INTERP_AMIGA      =  4,
-	INTERP_ATARI_ST   =  5,
-	INTERP_MSDOS      =  6,
-	INTERP_CBM_128    =  7,
-	INTERP_CBM_64     =  8,
-	INTERP_APPLE_IIC  =  9,
-	INTERP_APPLE_IIGS = 10,
-	INTERP_TANDY      = 11
-};
-
-enum Colour {
-	BLACK_COLOUR       = 2,
-	RED_COLOUR         = 3,
-	GREEN_COLOUR       = 4,
-	YELLOW_COLOUR      = 5,
-	BLUE_COLOUR        = 6,
-	MAGENTA_COLOUR     = 7,
-	CYAN_COLOUR        = 8,
-	WHITE_COLOUR       = 9,
-	GREY_COLOUR        = 10,	///< INTERP_MSDOS only
-	LIGHTGREY_COLOUR   = 10, 	///< INTERP_AMIGA only
-	MEDIUMGREY_COLOUR  = 11, 	///< INTERP_AMIGA only
-	DARKGREY_COLOUR    = 12,	///< INTERP_AMIGA only
-	TRANSPARENT_COLOUR = 15		///< ZSpec 1.1
-};
-
-enum Style {
-	REVERSE_STYLE     = 1,
-	BOLDFACE_STYLE    = 2,
-	EMPHASIS_STYLE    = 4,
-	FIXED_WIDTH_STYLE = 8
-};
-
-enum FontStyle {
-	TEXT_FONT        = 1,
-	PICTURE_FONT     = 2,
-	GRAPHICS_FONT    = 3,
-	FIXED_WIDTH_FONT = 4
-};
-
-/*** Constants for os_beep */
-
-#define BEEP_HIGH       1
-#define BEEP_LOW        2
-
-/*** Constants for os_menu */
-
-#define MENU_NEW 0
-#define MENU_ADD 1
-#define MENU_REMOVE 2
-
-typedef byte zbyte;
-typedef uint zchar;
-typedef uint16 zword;
-
-/**
- * User options
- */
-struct UserOptions {
-	int _attribute_assignment;
-	int _attribute_testing;
-	int _context_lines;
-	int _object_locating;
-	int _object_movement;
-	int _left_margin;
-	int _right_margin;
-	bool _ignore_errors;
-	bool _piracy;
-	int _undo_slots;
-	int _expand_abbreviations;
-	int _script_cols;
-	bool _save_quetzal;
-	int _err_report_mode;
-	bool _sound;
-	bool _user_tandy_bit;
-
-	UserOptions() : _attribute_assignment(0), _attribute_testing(0),
-		_context_lines(0), _object_locating(0), _object_movement(0),
-		_left_margin(0), _right_margin(0), _ignore_errors(false), _piracy(false),
-		_undo_slots(MAX_UNDO_SLOTS), _expand_abbreviations(0), _script_cols(80),
-		_save_quetzal(true), _err_report_mode(ERR_DEFAULT_REPORT_MODE), _sound(true),
-		_user_tandy_bit(false) {
-	}
-};
-
-#define MAX_NESTING 16
-struct Redirect {
-	zword _xSize;
-	zword _table;
-	zword _width;
-	zword _total;
-
-	Redirect() : _xSize(0), _table(0), _width(0), _total(0) {}
-	Redirect(zword xSize, zword table, zword width = 0, zword total = 0) :
-		_xSize(xSize), _table(table), _width(width), _total(total) {}
-};
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/glk_interface.cpp b/engines/gargoyle/frotz/glk_interface.cpp
deleted file mode 100644
index c376dc0..0000000
--- a/engines/gargoyle/frotz/glk_interface.cpp
+++ /dev/null
@@ -1,498 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/glk_interface.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-GlkInterface::GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-		Glk(syst, gameDesc),
-		oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
-		curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
-		curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
-		gos_lower(nullptr), gos_curwin(nullptr), gos_linepending(0), gos_linebuf(nullptr),
-		gos_linewin(nullptr), gos_channel(nullptr), cwin(0), mwin(0), mouse_x(0), mouse_y(0),
-		menu_selected(0), ostream_screen(false), ostream_script(false), ostream_memory(false),
-		ostream_record(false), istream_replay(false), message(false),
-		enable_wrapping(false), enable_scripting(false), enable_scrolling(false),
-		enable_buffering(false), next_sample(0), next_volume(0),
-		_soundLocked(false), _soundPlaying(false) {
-	Common::fill(&statusline[0], &statusline[256], '\0');
-}
-
-void GlkInterface::initialize() {
-	uint width, height;
-
-	/*
-	 * Init glk stuff
-	 */
-
-	// monor
-	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Weight, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Oblique, 0);
-
-	// monob
-	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Weight, 1);
-	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Oblique, 0);
-
-	// monoi
-	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Weight, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Oblique, 1);
-
-	// monoz
-	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Weight, 1);
-	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Oblique, 1);
-
-	// propr
-	glk_stylehint_set(wintype_TextBuffer, style_Normal,       stylehint_Proportional, 1);
-	glk_stylehint_set(wintype_TextGrid,   style_Normal,       stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Normal,       stylehint_Weight, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Normal,       stylehint_Oblique, 0);
-
-	// propb
-	glk_stylehint_set(wintype_TextBuffer, style_Header,       stylehint_Proportional, 1);
-	glk_stylehint_set(wintype_TextGrid,   style_Header,       stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Header,       stylehint_Weight, 1);
-	glk_stylehint_set(wintype_AllTypes,   style_Header,       stylehint_Oblique, 0);
-
-	// propi
-	glk_stylehint_set(wintype_TextBuffer, style_Emphasized,   stylehint_Proportional, 1);
-	glk_stylehint_set(wintype_TextGrid,   style_Emphasized,   stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Emphasized,   stylehint_Weight, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Emphasized,   stylehint_Oblique, 1);
-
-	// propi
-	glk_stylehint_set(wintype_TextBuffer, style_Note,         stylehint_Proportional, 1);
-	glk_stylehint_set(wintype_TextGrid,   style_Note,         stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Weight, 1);
-	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Oblique, 1);
-
-	gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
-	if (!gos_lower)
-		gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
-	glk_window_get_size(gos_lower, &width, &height);
-	glk_window_close(gos_lower, NULL);
-
-	gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
-	gos_upper = glk_window_open(gos_lower,
-			winmethod_Above | winmethod_Fixed,
-			0,
-			wintype_TextGrid, 0);
-
-	gos_channel = NULL;
-
-	glk_set_window(gos_lower);
-	gos_curwin = gos_lower;
-
-	/*
-	 * Icky magic bit setting
-	 */
-
-	if (h_version == V3 && _user_tandy_bit)
-		h_config |= CONFIG_TANDY;
-
-	if (h_version == V3 && gos_upper)
-		h_config |= CONFIG_SPLITSCREEN;
-
-	if (h_version == V3 && !gos_upper)
-		h_config |= CONFIG_NOSTATUSLINE;
-
-	if (h_version >= V4)
-		h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
-			CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
-
-	if (h_version >= V5)
-		h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
-
-	if ((h_version >= 5) && (h_flags & SOUND_FLAG))
-		h_flags |= SOUND_FLAG;
-
-	if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
-		h_flags |= OLD_SOUND_FLAG;
-
-	if ((h_version == 6) && (_sound != 0)) 
-		h_config |= CONFIG_SOUND;
-
-	if (h_version >= V5 && (h_flags & UNDO_FLAG))
-		if (_undo_slots == 0)
-			h_flags &= ~UNDO_FLAG;
-
-	h_screen_cols = width;
-	h_screen_rows = height;
-
-	h_screen_height = h_screen_rows;
-	h_screen_width = h_screen_cols;
-
-	h_font_width = 1;
-	h_font_height = 1;
-
-	/* Must be after screen dimensions are computed.  */
-	if (h_version == V6) {
-		h_flags &= ~GRAPHICS_FLAG;
-	}
-
-	// Use the ms-dos interpreter number for v6, because that's the
-	// kind of graphics files we understand.  Otherwise, use DEC.
-	h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
-	h_interpreter_version = 'F';
-
-	{
-		// Set these per spec 8.3.2.
-		h_default_foreground = WHITE_COLOUR;
-		h_default_background = BLACK_COLOUR;
-		if (h_flags & COLOUR_FLAG)
-			h_flags &= ~COLOUR_FLAG;
-	}
-}
-
-int GlkInterface::os_char_width(zchar z) {
-	return 1;
-}
-
-int GlkInterface::os_string_width(const zchar *s) {
-	int width = 0;
-	zchar c;
-	while ((c = *s++) != 0)
-		if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT)
-			s++;
-		else
-			width += os_char_width(c);
-	return width;
-}
-
-int GlkInterface::os_string_length(zchar *s) {
-	int length = 0;
-	while (*s++) length++;
-	return length;
-}
-
-void GlkInterface::os_prepare_sample(int a) {
-	glk_sound_load_hint(a, 1);
-}
-
-void GlkInterface::os_finish_with_sample(int a) {
-	glk_sound_load_hint(a, 0);
-}
-
-void GlkInterface::os_start_sample(int number, int volume, int repeats, zword eos) {
-	int vol;
-
-	if (!gos_channel) {
-		gos_channel = glk_schannel_create(0);
-		if (!gos_channel)
-			return;
-	}
-
-	switch (volume) {
-	case   1: vol = 0x02000; break;
-	case   2: vol = 0x04000; break;
-	case   3: vol = 0x06000; break;
-	case   4: vol = 0x08000; break;
-	case   5: vol = 0x0a000; break;
-	case   6: vol = 0x0c000; break;
-	case   7: vol = 0x0e000; break;
-	case   8: vol = 0x10000; break;
-	default:  vol = 0x20000; break;
-	}
-
-	// we dont do repeating or eos-callback for now...
-	glk_schannel_play_ext(gos_channel, number, 1, 0);
-	glk_schannel_set_volume(gos_channel, vol);
-}
-
-void GlkInterface::os_stop_sample(int a) {
-	if (!gos_channel)
-		return;
-	glk_schannel_stop(gos_channel);
-}
-
-void GlkInterface::os_beep(int volume) {
-}
-
-void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) {
-	// TODO
-}
-
-void GlkInterface::start_next_sample() {
-	// TODO
-}
-
-void GlkInterface::gos_update_width() {
-	glui32 width;
-	if (gos_upper) {
-		glk_window_get_size(gos_upper, &width, nullptr);
-		h_screen_cols = width;
-		SET_BYTE(H_SCREEN_COLS, width);
-		if ((uint)curx > width) {
-			glk_window_move_cursor(gos_upper, 0, cury - 1);
-			curx = 1;
-		}
-	}
-}
-
-void GlkInterface::gos_update_height() {
-	glui32 height_upper;
-	glui32 height_lower;
-	if (gos_curwin) {
-		glk_window_get_size(gos_upper, nullptr, &height_upper);
-		glk_window_get_size(gos_lower, nullptr, &height_lower);
-		h_screen_rows = height_upper + height_lower + 1;
-		SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
-	}
-}
-
-void GlkInterface::reset_status_ht() {
-	glui32 height;
-	if (gos_upper) {
-		glk_window_get_size(gos_upper, nullptr, &height);
-		if ((uint)mach_status_ht != height) {
-			glk_window_set_arrangement(
-				glk_window_get_parent(gos_upper),
-				winmethod_Above | winmethod_Fixed,
-				mach_status_ht, nullptr);
-		}
-	}
-}
-
-void GlkInterface::erase_window(zword w) {
-	if (w == 0)
-		glk_window_clear(gos_lower);
-	else if (gos_upper) {
-#ifdef GARGLK
-		garglk_set_reversevideo_stream(
-			glk_window_get_stream(gos_upper),
-			true);
-#endif /* GARGLK */
-		
-		memset(statusline, ' ', sizeof statusline);
-		glk_window_clear(gos_upper);
-		reset_status_ht();
-		curr_status_ht = 0;
-	}
-}
-
-void GlkInterface::split_window(zword lines) {
-	if (!gos_upper)
-		return;
-
-	// The top line is always set for V1 to V3 games
-	if (h_version < V4)
-		lines++;
-
-	if (!lines || lines > curr_status_ht) {
-		glui32 height;
-
-		glk_window_get_size(gos_upper, nullptr, &height);
-		if (lines != height)
-			glk_window_set_arrangement(
-				glk_window_get_parent(gos_upper),
-				winmethod_Above | winmethod_Fixed,
-				lines, nullptr);
-		curr_status_ht = lines;
-	}
-	mach_status_ht = lines;
-	if (cury > lines)
-	{
-		glk_window_move_cursor(gos_upper, 0, 0);
-		curx = cury = 1;
-	}
-	gos_update_width();
-
-	if (h_version == V3)
-		glk_window_clear(gos_upper);
-}
-
-void GlkInterface::restart_screen() {
-	erase_window(0);
-	erase_window(1);
-	split_window(0);
-}
-
-void GlkInterface::packspaces(zchar *src, zchar *dst) {
-	int killing = 0;
-	while (*src) {
-		if (*src == 0x20202020)
-			*src = ' ';
-		if (*src == ' ')
-			killing++;
-		else
-			killing = 0;
-		if (killing > 2)
-			src++;
-		else
-			*dst++ = *src++;
-	}
-
-	*dst = 0;
-}
-
-void GlkInterface::smartstatusline() {
-	zchar packed[256];
-	zchar buf[256];
-	zchar *a, *b, *c, *d;
-	int roomlen, scorelen, scoreofs;
-	int len, tmp;
-
-	packspaces(statusline, packed);
-	len = os_string_length(packed);
-
-	a = packed;
-	while (a[0] == ' ')
-		a++;
-
-	b = a;
-	while (b[0] != 0 && !(b[0] == ' ' && b[1] == ' '))
-		b++;
-
-	c = b;
-	while (c[0] == ' ')
-		c++;
-
-	d = packed + len - 1;
-	while (d[0] == ' ' && d > c)
-		d--;
-	if (d[0] != ' ' && d[0] != 0)
-		d++;
-	if (d < c)
-		d = c;
-
-	roomlen = b - a;
-	scorelen = d - c;
-	scoreofs = h_screen_cols - scorelen - 2;
-	if (scoreofs <= roomlen)
-		scoreofs = roomlen + 2;
-
-	for (tmp = 0; tmp < h_screen_cols; tmp++)
-		buf[tmp] = ' ';
-
-	memcpy(buf + 1 + scoreofs, c, scorelen * sizeof(zchar));
-	memcpy(buf + 1, a, roomlen * sizeof(zchar));
-
-	glk_window_move_cursor(gos_upper, 0, 0);
-	glk_put_buffer_uni(buf, h_screen_cols);
-	glk_window_move_cursor(gos_upper, cury - 1, curx - 1);
-}
-
-void GlkInterface::gos_cancel_pending_line() {
-	event_t ev;
-	glk_cancel_line_event(gos_linewin, &ev);
-	gos_linebuf[ev.val1] = '\0';
-	gos_linepending = 0;
-}
-
-zchar GlkInterface::os_read_key(int timeout, bool show_cursor) {
-	event_t ev;
-	winid_t win = gos_curwin ? gos_curwin : gos_lower;
-
-	if (gos_linepending)
-		gos_cancel_pending_line();
-
-	glk_request_char_event_uni(win);
-	if (timeout != 0)
-		glk_request_timer_events(timeout * 100);
-
-	while (!shouldQuit()) {
-		glk_select(&ev);
-		if (ev.type == evtype_Arrange) {
-			gos_update_height();
-			gos_update_width();
-		} else if (ev.type == evtype_Timer) {
-			glk_cancel_char_event(win);
-			glk_request_timer_events(0);
-			return ZC_TIME_OUT;
-		} else if (ev.type == evtype_CharInput)
-			break;
-	}
-	if (shouldQuit())
-		return 0;
-
-	glk_request_timer_events(0);
-
-	if (gos_upper && mach_status_ht < curr_status_ht)
-		reset_status_ht();
-	curr_status_ht = 0;
-
-	switch (ev.val1) {
-	case keycode_Escape: return ZC_ESCAPE;
-	case keycode_PageUp: return ZC_ARROW_MIN;
-	case keycode_PageDown: return ZC_ARROW_MAX;
-	case keycode_Left: return ZC_ARROW_LEFT;
-	case keycode_Right: return ZC_ARROW_RIGHT;
-	case keycode_Up: return ZC_ARROW_UP;
-	case keycode_Down: return ZC_ARROW_DOWN;
-	case keycode_Return: return ZC_RETURN;
-	case keycode_Delete: return ZC_BACKSPACE;
-	case keycode_Tab: return ZC_INDENT;
-	default:
-		return ev.val1;
-	}
-}
-
-zchar GlkInterface::os_read_line(int max, zchar *buf, int timeout, int width, int continued) {
-	event_t ev;
-	winid_t win = gos_curwin ? gos_curwin : gos_lower;
-
-	if (!continued && gos_linepending)
-		gos_cancel_pending_line();
-
-	if (!continued || !gos_linepending) {
-		glk_request_line_event_uni(win, buf, max, os_string_length(buf));
-		if (timeout != 0)
-			glk_request_timer_events(timeout * 100);
-	}
-
-	gos_linepending = 0;
-
-	while (!shouldQuit()) {
-		glk_select(&ev);
-		if (ev.type == evtype_Arrange) {
-			gos_update_height();
-			gos_update_width();
-		} else if (ev.type == evtype_Timer) {
-			gos_linewin = win;
-			gos_linepending = 1;
-			gos_linebuf = buf;
-			return ZC_TIME_OUT;
-		} else if (ev.type == evtype_LineInput) {
-			break;
-		}
-	}
-	if (shouldQuit())
-		return 0;
-
-	glk_request_timer_events(0);
-	buf[ev.val1] = '\0';
-
-	if (gos_upper && mach_status_ht < curr_status_ht)
-		reset_status_ht();
-	curr_status_ht = 0;
-
-	return ZC_RETURN;
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/glk_interface.h b/engines/gargoyle/frotz/glk_interface.h
deleted file mode 100644
index 5393c5d..0000000
--- a/engines/gargoyle/frotz/glk_interface.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FROTZ_GLK_INTERFACE
-#define GARGOYLE_FROTZ_GLK_INTERFACE
-
-#include "gargoyle/glk.h"
-#include "gargoyle/frotz/mem.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-enum SoundEffect {
-	EFFECT_PREPARE     = 1,
-	EFFECT_PLAY        = 2,
-	EFFECT_STOP        = 3,
-	EFFECT_FINISH_WITH = 4
-};
-
-enum RestartAction {
-	RESTART_BEGIN = 0,
-	RESTART_WPROP_SET = 1,
-	RESTART_END = 2
-};
-
-
-/**
- * Implements an intermediate interface on top of the GLK layer, providing screen
- * and sound effect handling
- */
-class GlkInterface : public Glk, public virtual UserOptions, public virtual Mem {
-public:
-	zchar statusline[256];
-	int oldstyle;
-	int curstyle;
-	int cury;
-	int curx;
-	int fixforced;
-
-	int curr_fg;
-	int curr_bg;
-	int curr_font;
-	int prev_font;
-	int temp_font;
-
-	int curr_status_ht;
-	int mach_status_ht;
-
-	winid_t gos_status;
-	winid_t gos_upper;
-	winid_t gos_lower;
-	winid_t gos_curwin;
-	int gos_linepending;
-	zchar *gos_linebuf;
-	winid_t gos_linewin;
-	schanid_t gos_channel;
-
-	// Current window and mouse data
-	int cwin;
-	int mwin;
-	int mouse_y;
-	int mouse_x;
-	int menu_selected;
-
-	// IO streams
-	bool ostream_screen;
-	bool ostream_script;
-	bool ostream_memory;
-	bool ostream_record;
-	bool istream_replay;
-	bool message;
-
-	// Window attributes
-	bool enable_wrapping;
-	bool enable_scripting;
-	bool enable_scrolling;
-	bool enable_buffering;
-
-	// Sound fields
-	int next_sample;
-	int next_volume;
-
-	bool _soundLocked;
-	bool _soundPlaying;
-protected:
-	int os_char_width(zchar z);
-	int os_string_width(const zchar *s);
-	int os_string_length(zchar *s);
-	void os_prepare_sample(int a);
-	void os_finish_with_sample(int a);
-
-	/**
-	 * Play the given sample at the given volume (ranging from 1 to 8 and
-	 * 255 meaning a default volume). The sound is played once or several
-	 * times in the background (255 meaning forever). In Z-code 3 the
-	 * repeats value is always 0 and the number of repeats is taken from
-	 * the sound file itself. The end_of_sound function is called as soon
-	 * as the sound finishes.
-	 */
-	void os_start_sample(int number, int volume, int repeats, zword eos);
-
-	void os_stop_sample(int a);
-	void os_beep(int volume);
-
-	/**
-	 * Call the IO interface to play a sample.
-	 */
-	void start_sample(int number, int volume, int repeats, zword eos);
-
-	void start_next_sample();
-	void gos_update_width();
-	void gos_update_height();
-	void reset_status_ht();
-	void erase_window(zword w);
-	void split_window(zword lines);
-	void restart_screen();
-
-	/**
-	 * statusline overflowed the window size ... bad game!
-	 * so ... split status text into regions, reformat and print anew.
-	 */
-	void packspaces(zchar *src, zchar *dst);
-
-	void smartstatusline();
-
-	/**
-	 * Cancels any pending line
-	 */
-	void gos_cancel_pending_line();
-
-	/**
-	 * Called during game restarts
-	 */
-	void os_restart_game(RestartAction) {}
-
-	/**
-	 * Reads the mouse buttons
-	 */
-	zword os_read_mouse() {
-		// Not implemented
-		return 0;
-	}
-
-	void os_scrollback_char(zchar z) {
-		// Not implemented
-	}
-
-	void os_scrollback_erase(int amount) {
-		// Not implemented
-	}
-
-	/**
-	 * Waits for a keypress
-	 */
-	zchar os_read_key(int timeout, bool show_cursor);
-
-	/**
-	 * Waits for the user to type an input line
-	 */
-	zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued);
-public:
-	/**
-	 * Constructor
-	 */
-	GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc);
-
-	/**
-	 * Initialization
-	 */
-	void initialize();
-};
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp
deleted file mode 100644
index e327fba..0000000
--- a/engines/gargoyle/frotz/mem.cpp
+++ /dev/null
@@ -1,419 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/mem.h"
-#include "gargoyle/frotz/frotz.h"
-#include "common/memstream.h"
-#include "common/textconsole.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-const Header::StoryEntry Header::RECORDS[25] = {
-	{       SHERLOCK,  21, "871214" },
-	{       SHERLOCK,  26, "880127" },
-	{    BEYOND_ZORK,  47, "870915" },
-	{    BEYOND_ZORK,  49, "870917" },
-	{    BEYOND_ZORK,  51, "870923" },
-	{    BEYOND_ZORK,  57, "871221" },
-	{      ZORK_ZERO, 296, "881019" },
-	{      ZORK_ZERO, 366, "890323" },
-	{      ZORK_ZERO, 383, "890602" },
-	{      ZORK_ZERO, 393, "890714" },
-	{         SHOGUN, 292, "890314" },
-	{         SHOGUN, 295, "890321" },
-	{         SHOGUN, 311, "890510" },
-	{         SHOGUN, 322, "890706" },
-	{         ARTHUR,  54, "890606" },
-	{         ARTHUR,  63, "890622" },
-	{         ARTHUR,  74, "890714" },
-	{        JOURNEY,  26, "890316" },
-	{        JOURNEY,  30, "890322" },
-	{        JOURNEY,  77, "890616" },
-	{        JOURNEY,  83, "890706" },
-	{ LURKING_HORROR, 203, "870506" },
-	{ LURKING_HORROR, 219, "870912" },
-	{ LURKING_HORROR, 221, "870918" },
-	{        UNKNOWN,   0, "------" }
-};
-
-void Header::loadHeader(Common::SeekableReadStream &f) {
-	h_version = f.readByte();
-	h_config = f.readByte();
-
-	if (h_version < V1 || h_version > V8)
-		error("Unknown Z-code version");
-
-	if (h_version == V6)
-		error("Cannot play Z-code version 6");
-
-	if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
-		error("Byte swapped story file");
-
-	h_release = f.readUint16BE();
-	h_resident_size = f.readUint16BE();
-	h_start_pc = f.readUint16BE();
-	h_dictionary = f.readUint16BE();
-	h_objects = f.readUint16BE();
-	h_globals = f.readUint16BE();
-	h_dynamic_size = f.readUint16BE();
-	h_flags = f.readUint16BE();
-	f.read(h_serial, 6);
-	
-	/* Auto-detect buggy story files that need special fixes */
-	_storyId = UNKNOWN;
-
-	for (int i = 0; RECORDS[i]._storyId != UNKNOWN; ++i) {
-		if (h_release == RECORDS[i]._release) {
-			if (!strncmp((const char *)h_serial, RECORDS[i]._serial, 6)) {
-				_storyId = RECORDS[i]._storyId;
-				break;
-			}
-		}
-	}
-
-	h_abbreviations = f.readUint16BE();
-	h_file_size = f.readUint16BE();
-	h_checksum = f.readUint16BE();
-	
-	f.seek(H_FUNCTIONS_OFFSET);
-	h_functions_offset = f.readUint16BE();
-	h_strings_offset = f.readUint16BE();
-	f.seek(H_TERMINATING_KEYS);
-	h_terminating_keys = f.readUint16BE();
-	f.seek(H_ALPHABET);
-	h_alphabet = f.readUint16BE();
-	h_extension_table = f.readUint16BE();
-
-
-	// Zork Zero Macintosh doesn't have the graphics flag set
-	if (_storyId == ZORK_ZERO && h_release == 296)
-		h_flags |= GRAPHICS_FLAG;
-}
-
-/*--------------------------------------------------------------------------*/
-
-Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0), story_size(0),
-		first_undo(nullptr), last_undo(nullptr), curr_undo(nullptr),
-		undo_mem(nullptr), prev_zmp(nullptr), undo_diff(nullptr),
-		undo_count(0), reserve_mem(0) {
-}
-
-void Mem::initialize() {
-	initializeStoryFile();
-	initializeUndo();
-	loadGameHeader();
-
-	// Allocate memory for story data
-	if ((zmp = (zbyte *)realloc(zmp, story_size)) == nullptr)
-		error("Out of memory");
-
-	// Load story file in chunks of 32KB
-	uint n = 0x8000;
-	for (uint size = 64; size < story_size; size += n) {
-		if (story_size - size < 0x8000)
-			n = story_size - size;
-
-		if (story_fp->read(zmp + size, n) != n)
-			error("Story file read error");
-	}
-
-	// Read header extension table
-	hx_table_size = get_header_extension(HX_TABLE_SIZE);
-	hx_unicode_table = get_header_extension(HX_UNICODE_TABLE);
-	hx_flags = get_header_extension(HX_FLAGS);
-}
-
-void Mem::initializeStoryFile() {
-	Common::SeekableReadStream *f = story_fp;
-	giblorb_map_t *map;
-	giblorb_result_t res;
-	uint32 magic;
-
-	magic = f->readUint32BE();
-
-	if (magic == MKTAG('F', 'O', 'R', 'M')) {
-		if (g_vm->giblorb_set_resource_map(f))
-			error("This Blorb file seems to be invalid.");
-
-		map = g_vm->giblorb_get_resource_map();
-
-		if (g_vm->giblorb_load_resource(map, giblorb_method_FilePos, &res, giblorb_ID_Exec, 0))
-			error("This Blorb file does not contain an executable chunk.");
-		if (res.chunktype != MKTAG('Z', 'C', 'O', 'D'))
-			error("This Blorb file contains an executable chunk, but it is not a Z-code file.");
-
-		blorb_ofs = res.data.startpos;
-		blorb_len = res.length;
-	} else {
-		blorb_ofs = 0;
-		blorb_len = f->size();
-	}
-
-	if (blorb_len < 64)
-		error("This file is too small to be a Z-code file.");
-}
-
-void Mem::initializeUndo() {
-	void *reserved = nullptr;
-
-	if (reserve_mem != 0) {
-		if ((reserved = malloc(reserve_mem)) == NULL)
-			return;
-	}
-
-	// Allocate h_dynamic_size bytes for previous dynamic zmp state
-	// + 1.5 h_dynamic_size for Quetzal diff + 2.
-	undo_mem = new zbyte[(h_dynamic_size * 5) / 2 + 2];
-	if (undo_mem != nullptr) {
-		prev_zmp = undo_mem;
-		undo_diff = undo_mem + h_dynamic_size;
-		memcpy(prev_zmp, zmp, h_dynamic_size);
-	} else {
-		_undo_slots = 0;
-	}
-
-	if (reserve_mem != 0)
-		delete reserved;
-}
-
-void Mem::loadGameHeader() {
-	// Load header
-	zmp = new byte[64];
-	story_fp->seek(blorb_ofs);
-	story_fp->read(zmp, 64);
-
-	Common::MemoryReadStream h(zmp, 64);
-	loadHeader(h);
-
-	// Calculate story file size in bytes
-	if (h_file_size != 0) {
-		story_size = (long)2 * h_file_size;
-
-		if (h_version >= V4)
-			story_size *= 2;
-		if (h_version >= V6)
-			story_size *= 2;
-	} else {
-		// Some old games lack the file size entry
-		story_size = blorb_len;
-	}
-}
-
-zword Mem::get_header_extension(int entry) {
-	zword addr;
-	zword val;
-
-	if (h_extension_table == 0 || entry > hx_table_size)
-		return 0;
-
-	addr = h_extension_table + 2 * entry;
-	LOW_WORD(addr, val);
-
-	return val;   
-}
-
-void Mem::set_header_extension(int entry, zword val) {
-	zword addr;
-
-	if (h_extension_table == 0 || entry > hx_table_size)
-		return;
-
-	addr = h_extension_table + 2 * entry;
-	SET_WORD(addr, val);
-}
-
-void Mem::restart_header(void) {
-	zword screen_x_size;
-	zword screen_y_size;
-	zbyte font_x_size;
-	zbyte font_y_size;
-
-	int i;
-
-	SET_BYTE(H_CONFIG, h_config);
-	SET_WORD(H_FLAGS, h_flags);
-
-	if (h_version >= V4) {
-		SET_BYTE(H_INTERPRETER_NUMBER, h_interpreter_number);
-		SET_BYTE(H_INTERPRETER_VERSION, h_interpreter_version);
-		SET_BYTE(H_SCREEN_ROWS, h_screen_rows);
-		SET_BYTE(H_SCREEN_COLS, h_screen_cols);
-	}
-
-	/* It's less trouble to use font size 1x1 for V5 games, especially
-	because of a bug in the unreleased German version of "Zork 1" */
-
-	if (h_version != V6) {
-		screen_x_size = (zword)h_screen_cols;
-		screen_y_size = (zword)h_screen_rows;
-		font_x_size = 1;
-		font_y_size = 1;
-	} else {
-		screen_x_size = h_screen_width;
-		screen_y_size = h_screen_height;
-		font_x_size = h_font_width;
-		font_y_size = h_font_height;
-	}
-
-	if (h_version >= V5) {
-		SET_WORD(H_SCREEN_WIDTH, screen_x_size);
-		SET_WORD(H_SCREEN_HEIGHT, screen_y_size);
-		SET_BYTE(H_FONT_HEIGHT, font_y_size);
-		SET_BYTE(H_FONT_WIDTH, font_x_size);
-		SET_BYTE(H_DEFAULT_BACKGROUND, h_default_background);
-		SET_BYTE(H_DEFAULT_FOREGROUND, h_default_foreground);
-	}
-
-	if (h_version == V6)
-		for (i = 0; i < 8; i++)
-			storeb((zword)(H_USER_NAME + i), h_user_name[i]);
-
-	SET_BYTE(H_STANDARD_HIGH, h_standard_high);
-	SET_BYTE(H_STANDARD_LOW, h_standard_low);
-
-	set_header_extension(HX_FLAGS, hx_flags);
-	set_header_extension(HX_FORE_COLOUR, hx_fore_colour);
-	set_header_extension(HX_BACK_COLOUR, hx_back_colour);
-}
-
-void Mem::storeb(zword addr, zbyte value) {
-	if (addr >= h_dynamic_size)
-		runtimeError(ERR_STORE_RANGE);
-
-	if (addr == H_FLAGS + 1) {
-		// flags register is modified
-
-		h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG);
-		h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG);
-
-		flagsChanged(value);
-	}
-
-	SET_BYTE(addr, value);
-}
-
-void Mem::storew(zword addr, zword value) {
-	storeb((zword)(addr + 0), hi(value));
-	storeb((zword)(addr + 1), lo(value));
-}
-
-void Mem::free_undo(int count) {
-	undo_t *p;
-
-	if (count > undo_count)
-		count = undo_count;
-	while (count--) {
-		p = first_undo;
-		if (curr_undo == first_undo)
-			curr_undo = curr_undo->next;
-		first_undo = first_undo->next;
-		free(p);
-		undo_count--;
-	}
-	if (first_undo)
-		first_undo->prev = NULL;
-	else
-		last_undo = NULL;
-}
-
-void Mem::reset_memory() {
-	story_fp = nullptr;
-	blorb_ofs = 0;
-	blorb_len = 0;
-
-	if (undo_mem) {
-		free_undo(undo_count);
-		delete undo_mem;
-	}
-
-	undo_mem = nullptr;
-	undo_count = 0;
-	delete[] zmp;
-	zmp = nullptr;
-}
-
-long Mem::mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff) {
-	unsigned size = mem_size;
-	zbyte *p = diff;
-	unsigned j;
-	zbyte c = 0;
-
-	for (;;) {
-		for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
-			size--;
-		if (size == 0) break;
-		size--;
-		if (j > 0x8000) {
-			*p++ = 0;
-			*p++ = 0xff;
-			*p++ = 0xff;
-			j -= 0x8000;
-		}
-		if (j > 0) {
-			*p++ = 0;
-			j--;
-			if (j <= 0x7f) {
-				*p++ = j;
-			} else {
-				*p++ = (j & 0x7f) | 0x80;
-				*p++ = (j & 0x7f80) >> 7;
-			}
-		}
-
-		*p++ = c;
-		*(b - 1) ^= c;
-	}
-
-	return p - diff;
-}
-
-void Mem::mem_undiff(zbyte *diff, long diff_length, zbyte *dest) {
-	zbyte c;
-
-	while (diff_length) {
-		c = *diff++;
-		diff_length--;
-		if (c == 0) {
-			unsigned runlen;
-
-			if (!diff_length)
-				return;  // Incomplete run
-			runlen = *diff++;
-			diff_length--;
-			if (runlen & 0x80) {
-				if (!diff_length)
-					return; // Incomplete extended run
-				c = *diff++;
-				diff_length--;
-				runlen = (runlen & 0x7f) | (((unsigned)c) << 7);
-			}
-
-			dest += runlen + 1;
-		} else {
-			*dest++ ^= c;
-		}
-	}
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h
deleted file mode 100644
index a2e0e34..0000000
--- a/engines/gargoyle/frotz/mem.h
+++ /dev/null
@@ -1,280 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FROTZ_MEM
-#define GARGOYLE_FROTZ_MEM
-
-#include "gargoyle/frotz/frotz_types.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-#define SET_WORD(addr,v)  zmp[addr] = hi(v); zmp[addr+1] = lo(v)
-#define LOW_WORD(addr,v)  v = READ_BE_UINT16(&zmp[addr])
-#define HIGH_WORD(addr,v) v = READ_BE_UINT16(&zmp[addr])
-#define HIGH_LONG(addr,v) v = READ_BE_UINT32(&zmp[addr])
-#define SET_BYTE(addr,v)   zmp[addr] = v
-#define LOW_BYTE(addr,v)   v = zmp[addr]
-
-enum HeaderByte {
-	H_VERSION             = 0,
-	H_CONFIG              = 1,
-	H_RELEASE             = 2,
-	H_RESIDENT_SIZE       = 4,
-	H_START_PC            = 6,
-	H_DICTIONARY          = 8,
-	H_OBJECTS             = 10,
-	H_GLOBALS             = 12,
-	H_DYNAMIC_SIZE        = 14,
-	H_FLAGS               = 16,
-	H_SERIAL              = 18,
-	H_ABBREVIATIONS       = 24,
-	H_FILE_SIZE           = 26,
-	H_CHECKSUM            = 28,
-	H_INTERPRETER_NUMBER  = 30,
-	H_INTERPRETER_VERSION = 31,
-	H_SCREEN_ROWS         = 32,
-	H_SCREEN_COLS         = 33,
-	H_SCREEN_WIDTH        = 34,
-	H_SCREEN_HEIGHT       = 36,
-	H_FONT_HEIGHT         = 38,		///< this is the font width in V5
-	H_FONT_WIDTH          = 39,		///< this is the font height in V5
-	H_FUNCTIONS_OFFSET    = 40,
-	H_STRINGS_OFFSET      = 42,
-	H_DEFAULT_BACKGROUND  = 44,
-	H_DEFAULT_FOREGROUND  = 45,
-	H_TERMINATING_KEYS    = 46,
-	H_LINE_WIDTH          = 48,
-	H_STANDARD_HIGH       = 50,
-	H_STANDARD_LOW        = 51,
-	H_ALPHABET            = 52,
-	H_EXTENSION_TABLE     = 54,
-	H_USER_NAME           = 56
-};
-
-enum {
-	HX_TABLE_SIZE    = 0,
-	HX_MOUSE_X       = 1,
-	HX_MOUSE_Y       = 2,
-	HX_UNICODE_TABLE = 3,
-	HX_FLAGS         = 4,
-	HX_FORE_COLOUR   = 5,
-	HX_BACK_COLOUR   = 6
-};
-
-/**
- * Stores undo information
- */
-struct undo_struct {
-	undo_struct *next;
-	undo_struct *prev;
-	long pc;
-	long diff_size;
-	zword frame_count;
-	zword stack_size;
-	zword frame_offset;
-	// undo diff and stack data follow
-};
-typedef undo_struct undo_t;
-
-/**
- * Story file header data
- */
-struct Header {
-private:
-	struct StoryEntry {
-		Story _storyId;
-		zword _release;
-		char _serial[7];
-	};
-	static const StoryEntry RECORDS[25];
-public:
-	zbyte h_version;
-	zbyte h_config;
-	zword h_release;
-	zword h_resident_size;
-	zword h_start_pc;
-	zword h_dictionary;
-	zword h_objects;
-	zword h_globals;
-	zword h_dynamic_size;
-	zword h_flags;
-	zbyte h_serial[6];
-	zword h_abbreviations;
-	zword h_file_size;
-	zword h_checksum;
-	zbyte h_interpreter_number;
-	zbyte h_interpreter_version;
-	zbyte h_screen_rows;
-	zbyte h_screen_cols;
-	zword h_screen_width;
-	zword h_screen_height;
-	zbyte h_font_height = 1;
-	zbyte h_font_width = 1;
-	zword h_functions_offset;
-	zword h_strings_offset;
-	zbyte h_default_background;
-	zbyte h_default_foreground;
-	zword h_terminating_keys;
-	zword h_line_width;
-	zbyte h_standard_high;
-	zbyte h_standard_low;
-	zword h_alphabet;
-	zword h_extension_table;
-	zbyte h_user_name[8];
-
-	zword hx_table_size;
-	zword hx_mouse_x;
-	zword hx_mouse_y;
-	zword hx_unicode_table;
-	zword hx_flags;
-	zword hx_fore_colour;
-	zword hx_back_colour;
-
-	Story _storyId;
-
-	/**
-	 * Constructor
-	 */
-	Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
-			h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
-			h_abbreviations(0), h_file_size(0), h_checksum(0), h_interpreter_number(0),
-			h_interpreter_version(0), h_screen_rows(0), h_screen_cols(0), h_screen_width(0),
-			h_screen_height(0), h_font_height(1), h_font_width(1), h_functions_offset(0),
-			h_strings_offset(0), h_default_background(0), h_default_foreground(0),
-			h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
-			h_alphabet(0), h_extension_table(0),
-			hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
-			hx_flags(0), hx_fore_colour(0), hx_back_colour(0), _storyId(UNKNOWN) {
-		Common::fill(&h_serial[0], &h_serial[6], '\0');
-		Common::fill(&h_user_name[0], &h_user_name[8], '\0');
-	}
-
-	/**
-	 * Load the header
-	 */
-	void loadHeader(Common::SeekableReadStream &f);
-};
-
-class Mem : public Header, public virtual UserOptions {
-protected:
-	Common::SeekableReadStream *story_fp;
-	uint blorb_ofs, blorb_len;
-	uint story_size;
-	byte *pcp;
-	byte *zmp;
-
-	undo_t *first_undo, *last_undo, *curr_undo;
-	zbyte *undo_mem, *prev_zmp, *undo_diff;
-	int undo_count;
-	int reserve_mem;
-private:
-	/**
-	 * Handles setting the story file, parsing it if it's a Blorb file
-	 */
-	void initializeStoryFile();
-
-	/**
-	 * Setup undo data
-	 */
-	void initializeUndo();
-
-	/**
-	 * Handles loading the game header
-	 */
-	void loadGameHeader();
-protected:
-	/**
-	 * Read a value from the header extension (former mouse table).
-	 */
-	zword get_header_extension(int entry);
-
-	/**
-	 * Set an entry in the header extension (former mouse table).
-	 */
-	void set_header_extension(int entry, zword val);
-
-	/**
-	 * Set all header fields which hold information about the interpreter.
-	 */
-	void restart_header();
-
-	/**
-	 * Write a byte value to the dynamic Z-machine memory.
-	 */
-	void storeb(zword addr, zbyte value);
-
-	/**
-	 * Write a word value to the dynamic Z-machine memory.
-	 */
-	void storew(zword addr, zword value);
-
-	/**
-	 * Free count undo blocks from the beginning of the undo list
-	 */
-	void free_undo(int count);
-
-	/**
-	 * Generates a runtime error
-	 */
-	virtual void runtimeError(ErrorCode errNum) = 0;
-
-	/**
-	 * Called when the flags are changed
-	 */
-	virtual void flagsChanged(zbyte value) = 0;
-
-	/**
-	 * Close the story file and deallocate memory.
-	 */
-	void reset_memory();
-
-	/**
-	 * Set diff to a Quetzal-like difference between a and b,
-	 * copying a to b as we go.  It is assumed that diff points to a
-	 * buffer which is large enough to hold the diff.
-	 * mem_size is the number of bytes to compare.
-	 * Returns the number of bytes copied to diff.
-	 *
-	 */
-	long mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff);
-
-	/**
-	 * Applies a quetzal-like diff to dest
-	 */
-	void mem_undiff(zbyte *diff, long diff_length, zbyte *dest);
-public:
-	/**
-	 * Constructor
-	 */
-	Mem();
-
-	/**
-	 * Initialize
-	 */
-	void initialize();
-};
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/processor.cpp b/engines/gargoyle/frotz/processor.cpp
deleted file mode 100644
index 3336aae..0000000
--- a/engines/gargoyle/frotz/processor.cpp
+++ /dev/null
@@ -1,664 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-#include "gargoyle/frotz/frotz.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-// TODO: Stubs to replace with actual code
-zword save_undo() { return 0; }
-zword restore_undo() { return 0; }
-
-
-Opcode Processor::var_opcodes[64] = {
-	&Processor::__illegal__,
-	&Processor::z_je,
-	&Processor::z_jl,
-	&Processor::z_jg,
-	&Processor::z_dec_chk,
-	&Processor::z_inc_chk,
-	&Processor::z_jin,
-	&Processor::z_test,
-	&Processor::z_or,
-	&Processor::z_and,
-	&Processor::z_test_attr,
-	&Processor::z_set_attr,
-	&Processor::z_clear_attr,
-	&Processor::z_store,
-	&Processor::z_insert_obj,
-	&Processor::z_loadw,
-	&Processor::z_loadb,
-	&Processor::z_get_prop,
-	&Processor::z_get_prop_addr,
-	&Processor::z_get_next_prop,
-	&Processor::z_add,
-	&Processor::z_sub,
-	&Processor::z_mul,
-	&Processor::z_div,
-	&Processor::z_mod,
-	&Processor::z_call_s,
-	&Processor::z_call_n,
-	&Processor::z_set_colour,
-	&Processor::z_throw,
-	&Processor::__illegal__,
-	&Processor::__illegal__,
-	&Processor::__illegal__,
-	&Processor::z_call_s,
-	&Processor::z_storew,
-	&Processor::z_storeb,
-	&Processor::z_put_prop,
-	&Processor::z_read,
-	&Processor::z_print_char,
-	&Processor::z_print_num,
-	&Processor::z_random,
-	&Processor::z_push,
-	&Processor::z_pull,
-	&Processor::z_split_window,
-	&Processor::z_set_window,
-	&Processor::z_call_s,
-	&Processor::z_erase_window,
-	&Processor::z_erase_line,
-	&Processor::z_set_cursor,
-	&Processor::z_get_cursor,
-	&Processor::z_set_text_style,
-	&Processor::z_buffer_mode,
-	&Processor::z_output_stream,
-	&Processor::z_input_stream,
-	&Processor::z_sound_effect,
-	&Processor::z_read_char,
-	&Processor::z_scan_table,
-	&Processor::z_not,
-	&Processor::z_call_n,
-	&Processor::z_call_n,
-	&Processor::z_tokenise,
-	&Processor::z_encode_text,
-	&Processor::z_copy_table,
-	&Processor::z_print_table,
-	&Processor::z_check_arg_count
-};
-
-Opcode Processor::ext_opcodes[64] = {
-	&Processor::z_save,
-	&Processor::z_restore,
-	&Processor::z_log_shift,
-	&Processor::z_art_shift,
-	&Processor::z_set_font,
-	&Processor::__illegal__,		// glkify - Processor::z_draw_picture,
-	&Processor::__illegal__,		// glkify - Processor::z_picture_data,
-	&Processor::__illegal__,		// glkify - Processor::z_erase_picture,
-	&Processor::__illegal__,		// glkify - Processor::z_set_margins,
-	&Processor::z_save_undo,
-	&Processor::z_restore_undo,
-	&Processor::z_print_unicode,
-	&Processor::z_check_unicode,
-	&Processor::z_set_true_colour,	// spec 1.1
-	&Processor::__illegal__,
-	&Processor::__illegal__,
-	&Processor::__illegal__,		// glkify - Processor::z_move_window,
-	&Processor::__illegal__,		// glkify - Processor::z_window_size,
-	&Processor::__illegal__,		// glkify - Processor::z_window_style,
-	&Processor::__illegal__,		// glkify - Processor::z_get_wind_prop,
-	&Processor::__illegal__,		// glkify - Processor::z_scroll_window,
-	&Processor::z_pop_stack,
-	&Processor::__illegal__,		// glkify - Processor::z_read_mouse,
-	&Processor::__illegal__,		// glkify - Processor::z_mouse_window,
-	&Processor::z_push_stack,
-	&Processor::__illegal__,		// glkify - Processor::z_put_wind_prop,
-	&Processor::z_print_form,
-	&Processor::z_make_menu,
-	&Processor::__illegal__,		// glkify - Processor::z_picture_table
-	&Processor::z_buffer_screen,	// spec 1.1
-};
-
-Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
-		GlkInterface(syst, gameDesc),
-		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
-		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
-		_randomInterval(0), _randomCtr(0), first_restart(true), script_valid(false),
-		_bufPos(0), _locked(false), _prevC('\0'), script_width(0),
-		sfp(nullptr), rfp(nullptr), pfp(nullptr) {
-	static const Opcode OP0_OPCODES[16] = {
-		&Processor::z_rtrue,
-		&Processor::z_rfalse,
-		&Processor::z_print,
-		&Processor::z_print_ret,
-		&Processor::z_nop,
-		&Processor::z_save,
-		&Processor::z_restore,
-		&Processor::z_restart,
-		&Processor::z_ret_popped,
-		&Processor::z_catch,
-		&Processor::z_quit,
-		&Processor::z_new_line,
-		&Processor::z_show_status,
-		&Processor::z_verify,
-		&Processor::__extended__,
-		&Processor::z_piracy
-	};
-	static const Opcode OP1_OPCODES[16] = {
-		&Processor::z_jz,
-		&Processor::z_get_sibling,
-		&Processor::z_get_child,
-		&Processor::z_get_parent,
-		&Processor::z_get_prop_len,
-		&Processor::z_inc,
-		&Processor::z_dec,
-		&Processor::z_print_addr,
-		&Processor::z_call_s,
-		&Processor::z_remove_obj,
-		&Processor::z_print_obj,
-		&Processor::z_ret,
-		&Processor::z_jump,
-		&Processor::z_print_paddr,
-		&Processor::z_load,
-		&Processor::z_call_n
-	};
-
-	Common::copy(&OP0_OPCODES[0], &OP0_OPCODES[16], op0_opcodes);
-	Common::copy(&OP1_OPCODES[0], &OP1_OPCODES[16], op1_opcodes);
-	Common::fill(&_stack[0], &_stack[STACK_SIZE], 0);
-	Common::fill(&zargs[0], &zargs[8], 0);
-	Common::fill(&_buffer[0], &_buffer[TEXT_BUFFER_SIZE], '\0');
-	Common::fill(&_errorCount[0], &_errorCount[ERR_NUM_ERRORS], 0);
-}
-
-void Processor::initialize() {
-	Mem::initialize();
-	GlkInterface::initialize();
-
-	if (h_version <= V4) {
-		op0_opcodes[9] = &Processor::z_pop;
-		op1_opcodes[15] = &Processor::z_not;
-	} else {
-		op0_opcodes[9] = &Processor::z_catch;
-		op1_opcodes[15] = &Processor::z_call_n;
-	}
-}
-
-void Processor::load_operand(zbyte type) {
-	zword value;
-
-	if (type & 2) {
-		// variable
-		zbyte variable;
-
-		CODE_BYTE(variable);
-
-		if (variable == 0)
-			value = *_sp++;
-		else if (variable < 16)
-			value = *(_fp - variable);
-		else {
-			zword addr = h_globals + 2 * (variable - 16);
-			LOW_WORD(addr, value);
-		}
-	} else if (type & 1) {
-		// small constant
-		zbyte bvalue;
-
-		CODE_BYTE(bvalue);
-		value = bvalue;
-
-	} else {
-		// large constant
-		CODE_WORD(value);
-	}
-
-	zargs[zargc++] = value;
-}
-
-void Processor::load_all_operands(zbyte specifier) {
-	for (int i = 6; i >= 0; i -= 2) {
-		zbyte type = (specifier >> i) & 0x03;
-
-		if (type == 3)
-			break;
-
-		load_operand(type);
-	}
-}
-
-void Processor::interpret() {
-	do {
-		zbyte opcode;
-		CODE_BYTE(opcode);
-		zargc = 0;
-
-		if (opcode < 0x80) {
-			// 2OP opcodes
-			load_operand((zbyte)(opcode & 0x40) ? 2 : 1);
-			load_operand((zbyte)(opcode & 0x20) ? 2 : 1);
-
-			(*this.*var_opcodes[opcode & 0x1f])();
-
-		} else if (opcode < 0xb0) {
-			// 1OP opcodes
-			load_operand((zbyte)(opcode >> 4));
-
-			(*this.*op1_opcodes[opcode & 0x0f])();
-
-		} else if (opcode < 0xc0) {
-			// 0OP opcodes
-			(*this.*op0_opcodes[opcode - 0xb0])();
-
-		} else {
-			// VAR opcodes
-			zbyte specifier1;
-			zbyte specifier2;
-
-			if (opcode == 0xec || opcode == 0xfa) {	// opcodes 0xec
-				CODE_BYTE(specifier1);			// and 0xfa are
-				CODE_BYTE(specifier2);          // call opcodes
-				load_all_operands(specifier1);	// with up to 8
-				load_all_operands(specifier2);	// arguments
-			} else {
-				CODE_BYTE(specifier1);
-				load_all_operands(specifier1);
-			}
-
-			(*this.*var_opcodes[opcode - 0xc0])();
-		}
-
-#if defined(DJGPP) && defined(SOUND_SUPPORT)
-		if (end_of_sound_flag)
-			end_of_sound();
-#endif
-	} while (!_finished);
-
-	_finished--;
-}
-
-void Processor::call(zword routine, int argc, zword *args, int ct) {
-	long pc;
-	zword value;
-	zbyte count;
-	int i;
-
-	if (_sp - _stack < 4)
-		runtimeError(ERR_STK_OVF);
-
-	GET_PC(pc);
-
-	*--_sp = (zword)(pc >> 9);
-	*--_sp = (zword)(pc & 0x1ff);
-	*--_sp = (zword)(_fp - _stack - 1);
-	*--_sp = (zword)(argc | (ct << (_save_quetzal ? 12 : 8)));
-
-	_fp = _sp;
-	_frameCount++;
-
-	// Calculate byte address of routine
-	if (h_version <= V3)
-		pc = (long)routine << 1;
-	else if (h_version <= V5)
-		pc = (long)routine << 2;
-	else if (h_version <= V7)
-		pc = ((long)routine << 2) + ((long)h_functions_offset << 3);
-	else if (h_version <= V8)
-		pc = (long)routine << 3;
-	else {
-		// h_version == V9
-		long indirect = (long)routine << 2;
-		HIGH_LONG(indirect, pc);
-	}
-
-	if ((uint)pc >= story_size)
-		runtimeError(ERR_ILL_CALL_ADDR);
-
-	SET_PC(pc);
-
-	// Initialise local variables
-	CODE_BYTE(count);
-
-	if (count > 15)
-		runtimeError(ERR_CALL_NON_RTN);
-	if (_sp - _stack < count)
-		runtimeError(ERR_STK_OVF);
-
-	if (_save_quetzal)
-		_fp[0] |= (zword)count << 8;	// Save local var count for Quetzal.
-
-	value = 0;
-
-	for (i = 0; i < count; i++) {
-		if (h_version <= V4)		// V1 to V4 games provide default
-			CODE_WORD(value);		// values for all local variables
-
-			*--_sp = (zword)((argc-- > 0) ? args[i] : value);
-	}
-
-	// Start main loop for direct calls
-	if (ct == 2)
-		interpret();
-}
-
-void Processor::ret(zword value) {
-	long pc;
-	int ct;
-
-	if (_sp > _fp)
-		runtimeError(ERR_STK_UNDF);
-
-	_sp = _fp;
-
-	ct = *_sp++ >> (_save_quetzal ? 12 : 8);
-	_frameCount--;
-	_fp = _stack + 1 + *_sp++;
-	pc = *_sp++;
-	pc = ((long)*_sp++ << 9) | pc;
-
-	SET_PC(pc);
-
-	// Handle resulting value
-	if (ct == 0)
-		store(value);
-	if (ct == 2)
-		*--_sp = value;
-
-	// Stop main loop for direct calls
-	if (ct == 2)
-		_finished++;
-}
-
-void Processor::branch(bool flag) {
-	long pc;
-	zword offset;
-	zbyte specifier;
-	zbyte off1;
-	zbyte off2;
-
-	CODE_BYTE(specifier);
-	off1 = specifier & 0x3f;
-
-	if (!flag)
-		specifier ^= 0x80;
-
-	if (!(specifier & 0x40)) {
-		// it's a long branch
-		if (off1 & 0x20)		// propagate sign bit
-			off1 |= 0xc0;
-
-		CODE_BYTE(off2);
-		offset = (off1 << 8) | off2;
-	} else {
-		// It's a short branch
-		offset = off1;
-	}
-
-	if (specifier & 0x80) {
-		if (offset > 1) {
-			// normal branch
-			GET_PC(pc);
-			pc += (short)offset - 2;
-			SET_PC(pc);
-		} else {
-			// special case, return 0 or 1
-			ret(offset);
-		}
-	}
-}
-
-void Processor::store(zword value) {
-	zbyte variable;
-
-	CODE_BYTE(variable);
-
-	if (variable == 0)
-		*--_sp = value;
-	else if (variable < 16)
-		*(_fp - variable) = value;
-	else {
-		zword addr = h_globals + 2 * (variable - 16);
-		SET_WORD(addr, value);
-	}
-}
-
-int Processor::direct_call(zword addr) {
-	zword saved_zargs[8];
-	int saved_zargc;
-	int i;
-
-	// Calls to address 0 return false
-	if (addr == 0)
-		return 0;
-
-	// Save operands and operand count
-	for (i = 0; i < 8; i++)
-		saved_zargs[i] = zargs[i];
-
-	saved_zargc = zargc;
-
-	// Call routine directly
-	call(addr, 0, 0, 2);
-
-	// Restore operands and operand count
-	for (i = 0; i < 8; i++)
-		zargs[i] = saved_zargs[i];
-
-	zargc = saved_zargc;
-
-	// Resulting value lies on top of the stack
-	return (short)*_sp++;
-}
-
-void Processor::seed_random(int value) {
-	if (value == 0) {
-		// Now using random values
-		_randomInterval = 0;
-	} else if (value < 1000) {
-		// special seed value
-		_randomCtr = 0;
-		_randomInterval = value;
-	} else {
-		// standard seed value
-		_random.setSeed(value);
-		_randomInterval = 0;
-	}
-}
-
-void Processor::__extended__() {
-	zbyte opcode;
-	zbyte specifier;
-
-	CODE_BYTE(opcode);
-	CODE_BYTE(specifier);
-
-	load_all_operands(specifier);
-
-	if (opcode < 0x1e)					// extended opcodes from 0x1e on
-		(*this.*ext_opcodes[opcode])();	// are reserved for future spec'
-}
-
-void Processor::__illegal__() {
-	runtimeError(ERR_ILL_OPCODE);
-}
-
-void Processor::z_catch() {
-	store(_save_quetzal ? _frameCount : (zword)(_fp - _stack));
-}
-
-void Processor::z_throw() {
-	if (_save_quetzal) {
-		if (zargs[1] > _frameCount)
-			runtimeError(ERR_BAD_FRAME);
-
-		// Unwind the stack a frame at a time.
-		for (; _frameCount > zargs[1]; --_frameCount)
-			_fp = _stack + 1 + _fp[1];
-	} else {
-		if (zargs[1] > STACK_SIZE)
-			runtimeError(ERR_BAD_FRAME);
-
-		_fp = _stack + zargs[1];
-	}
-
-	ret(zargs[0]);
-}
-
-void Processor::z_call_n() {
-	if (zargs[0] != 0)
-		call(zargs[0], zargc - 1, zargs + 1, 1);
-}
-
-void Processor::z_call_s() {
-	if (zargs[0] != 0)
-		call(zargs[0], zargc - 1, zargs + 1, 0);
-	else
-		store(0);
-}
-
-void Processor::z_check_arg_count() {
-	if (_fp == _stack + STACK_SIZE)
-		branch(zargs[0] == 0);
-	else
-		branch(zargs[0] <= (*_fp & 0xff));
-}
-
-void Processor::z_jump() {
-	long pc;
-	GET_PC(pc);
-
-	pc += (short)zargs[0] - 2;
-
-	if ((uint)pc >= story_size)
-		runtimeError(ERR_ILL_JUMP_ADDR);
-
-	SET_PC(pc);
-}
-
-void Processor::z_nop() {
-	// Do nothing
-}
-
-void Processor::z_quit() {
-	_finished = 9999;
-}
-
-void Processor::z_ret() {
-	ret(zargs[0]);
-}
-
-void Processor::z_ret_popped() {
-	ret(*_sp++);
-}
-
-void Processor::z_rfalse() {
-	ret(0);
-}
-
-void Processor::z_rtrue() {
-	ret(1);
-}
-
-void Processor::z_random() {
-    if ((short) zargs[0] <= 0) {
-		// set random seed
-		seed_random(- (short) zargs[0]);
-		store(0);
-
-    } else {
-		// generate random number
-		zword result;
-		if (_randomInterval != 0) {
-			// ...in special mode
-			result = _randomCtr++;
-			if (_randomCtr == _randomInterval)
-				_randomCtr = 0;
-		} else {
-			// ...in standard mode
-			result = _random.getRandomNumber(0xffff);
-		}
-
-		store((zword)(result % zargs[0] + 1));
-    }
-}
-
-void Processor::z_sound_effect() {
-    zword number = zargs[0];
-    zword effect = zargs[1];
-    zword volume = zargs[2];
-
-    if (zargc < 1)
-		number = 0;
-    if (zargc < 2)
-		effect = EFFECT_PLAY;
-    if (zargc < 3)
-		volume = 8;
-
-    if (number >= 3 || number == 0) {
-		_soundLocked = true;
-
-		if (_storyId == LURKING_HORROR && (number == 9 || number == 16)) {
-			if (effect == EFFECT_PLAY) {
-				next_sample = number;
-				next_volume = volume;
-
-				_soundLocked = false;
-
-				if (!_soundPlaying)
-					start_next_sample();
-			} else {
-				_soundLocked = false;
-			}
-			return;
-		}
-
-		_soundPlaying = false;
-
-		switch (effect) {
-
-		case EFFECT_PREPARE:
-			os_prepare_sample (number);
-			break;
-		case EFFECT_PLAY:
-			start_sample(number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0);
-			break;
-		case EFFECT_STOP:
-			os_stop_sample (number);
-			break;
-		case EFFECT_FINISH_WITH:
-			os_finish_with_sample (number);
-			break;
-		}
-
-		_soundLocked = false;
-	} else {
-		os_beep(number);
-	}
-}
-
-void Processor::z_piracy() {
-	branch(!_piracy);
-}
-
-void Processor::z_save_undo(void) {
-	store((zword)save_undo());
-}
-
-void Processor::z_restore_undo(void) {
-	store((zword)restore_undo());
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h
deleted file mode 100644
index 3ead900..0000000
--- a/engines/gargoyle/frotz/processor.h
+++ /dev/null
@@ -1,1584 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GARGOYLE_FROTZ_PROCESSOR
-#define GARGOYLE_FROTZ_PROCESSOR
-
-#include "gargoyle/frotz/mem.h"
-#include "gargoyle/frotz/glk_interface.h"
-#include "gargoyle/frotz/frotz_types.h"
-#include "common/stack.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-#define TEXT_BUFFER_SIZE 200
-
-#define CODE_BYTE(v)	   v = codeByte()
-#define CODE_WORD(v)       v = codeWord()
-#define CODE_IDX_WORD(v,i) v = codeWordIdx(i)
-#define GET_PC(v)          v = getPC()
-#define SET_PC(v)          setPC(v)
-
-enum string_type {
-	LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY
-};
-
-class Processor;
-class Quetzal;
-typedef void (Processor::*Opcode)();
-
-/**
- * Zcode processor
- */
-class Processor : public GlkInterface, public virtual Mem {
-	friend class Quetzal;
-private:
-	Opcode op0_opcodes[16];
-	Opcode op1_opcodes[16];
-	static const char *const ERR_MESSAGES[ERR_NUM_ERRORS];
-	static Opcode var_opcodes[64];
-	static Opcode ext_opcodes[64];
-
-	int _finished;
-	zword zargs[8];
-	int zargc;
-	uint _randomInterval;
-	uint _randomCtr;
-	bool first_restart;
-	bool script_valid;
-
-	// Stack data
-	zword _stack[STACK_SIZE];
-	zword *_sp;
-	zword *_fp;
-	zword _frameCount;
-
-	// Text related fields
-	static zchar ZSCII_TO_LATIN1[];
-	zchar *_decoded, *_encoded;
-	int _resolution;
-	int _errorCount[ERR_NUM_ERRORS];
-
-	// Buffer related fields
-	bool _locked;
-	zchar _prevC;
-	zchar _buffer[TEXT_BUFFER_SIZE];
-	size_t _bufPos;
-
-	// Stream related fields
-	int script_width;
-	strid_t sfp, rfp, pfp;
-	Common::FixedStack<Redirect, MAX_NESTING> _redirect;
-private:
-	/**
-	 * \defgroup General support methods
-	 * @{
-	 */
-
-	/**
-	 * Load an operand, either a variable or a constant.
-	 */
-	void load_operand(zbyte type);
-
-	/**
-	 * Given the operand specifier byte, load all (up to four) operands
-	 * for a VAR or EXT opcode.
-	 */
-	void load_all_operands(zbyte specifier);
-
-	/**
-	 * Call a subroutine. Save PC and FP then load new PC and initialise
-	 * new stack frame. Note that the caller may legally provide less or
-	 * more arguments than the function actually has. The call type "ct"
-	 * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call).
-	 */
-	void call(zword routine, int argc, zword *args, int ct);
-
-	/**
-	 * Return from the current subroutine and restore the previous _stack
-	 * frame. The result may be stored (0), thrown away (1) or pushed on
-	 * the stack (2). In the latter case a direct call has been finished
-	 * and we must exit the interpreter loop.
-	 */
-	void ret(zword value);
-
-	/**
-	 * Take a jump after an instruction based on the flag, either true or
-	 * false. The branch can be short or long; it is encoded in one or two
-	 * bytes respectively. When bit 7 of the first byte is set, the jump
-	 * takes place if the flag is true; otherwise it is taken if the flag
-	 * is false. When bit 6 of the first byte is set, the branch is short;
-	 * otherwise it is long. The offset occupies the bottom 6 bits of the
-	 * first byte plus all the bits in the second byte for long branches.
-	 * Uniquely, an offset of 0 means return false, and an offset of 1 is
-	 * return true.
-	 */
-	void branch(bool flag);
-
-	/**
-	 * Store an operand, either as a variable or pushed on the stack.
-	 */
-	void store(zword value);
-
-	/*
-	 * Call the interpreter loop directly. This is necessary when
-	 *
-	 * - a sound effect has been finished
-	 * - a read instruction has timed out
-	 * - a newline countdown has hit zero
-	 *
-	 * The interpreter returns the result value on the stack.
-	 */
-	int direct_call(zword addr);
-
-	/**
-	 * Set the seed value for the random number generator.
-	 */
-	void seed_random(int value);
-
-	/**@}*/
-
-	/**
-	 * \defgroup Input support methods
-	 * @{
-	 */
-
-	/**
-	 * Copy the contents of the text buffer to the output streams.
-	 */
-	void flush_buffer();
-
-	 /**
-	  * High level output function.
-	  */
-	void print_char(zchar c);
-
-	/**
-	 * Print a string of ASCII characters.
-	 */
-	void print_string(const char *s);
-
-	/**
-	 * Print an unsigned 32bit number in decimal or hex.
-	 */
-	void print_long(uint value, int base);
-
-	 /**
-	  * High level newline function.
-	  */
-	void new_line();
-
-	/**
-	 * Returns true if the buffer is empty
-	 */
-	bool bufferEmpty() const { return !_bufPos; }
-
-	/**
-	 * An error has occurred. Ignore it, pass it to os_fatal or report
-	 * it according to err_report_mode.
-	 * @param errNum		Numeric code for error (1 to ERR_NUM_ERRORS)
-	 */
-	void runtimeError(ErrorCode errNum);
-
-	/**@}*/
-
-	/**
-	 * \defgroup Input support methods
-	 * @{
-	 */
-
-	/**
-	 * Check if the given key is an input terminator.
-	 */
-	bool is_terminator(zchar key);
-
-	/**
-	 * Ask the user a question; return true if the answer is yes.
-	 */
-	bool read_yes_or_no(const char *s);
-
-	/**
-	 * Read a string from the current input stream.
-	 */
-	void read_string(int max, zchar *buffer);
-
-	/**
-	 * Ask the user to type in a number and return it.
-	 */
-	int read_number();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Memory support methods
-	 * @{
-	 */
-
-	/**
-	 * Called when the H_FLAGS field of the header has changed
-	 */
-	virtual void flagsChanged(zbyte value) override;
-
-	/**
-	 * This function does the dirty work for z_save_undo.
-	 */
-	int save_undo();
-
-	/**
-	 * This function does the dirty work for z_restore_undo.
-	 */
-	int restore_undo();
-
-	/**
-	 * Begin output redirection to the memory of the Z-machine.
-	 */
-	void memory_open(zword table, zword xsize, bool buffering);
-
-	/**
-	 * End of output redirection.
-	 */
-	void memory_close();
-
-	/**
-	 * Redirect a newline to the memory of the Z-machine.
-	 */
-	void memory_new_line();
-
-	/**
-	 * Redirect a string of characters to the memory of the Z-machine.
-	 */
-	void memory_word(const zchar *s);
-
-	/**@}*/
-
-	/**
-	 * \defgroup Object support methods
-	 * @{
-	 */
-
-	/**
-	 * Calculate the address of an object.
-	 */
-	zword object_address(zword obj);
-
-	/**
-	 * Return the address of the given object's name.
-	 */
-	zword object_name(zword object);
-
-	/**
-	 * Calculate the start address of the property list associated with an object.
-	 */
-	zword first_property(zword obj);
-
-	/**
-	 * Calculate the address of the next property in a property list.
-	 */
-	zword next_property(zword prop_addr);
-
-	/**
-	 * Unlink an object from its parent and siblings.
-	 */
-	void unlink_object(zword object);
-
-	/**@}*/
-
-	/**
-	 * \defgroup Screen support methods
-	 * @{
-	 */
-
-	void screen_char(zchar c);
-	void screen_new_line();
-	void screen_word(const zchar *s);
-	void screen_mssg_on();
-	void screen_mssg_off();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Stream support methods
-	 * @{
-	 */
-
-	/**
-	 * Waits for the user to type an input line
-	 */
-	zchar console_read_input(int max, zchar *buf, zword timeout, bool continued);
-
-	/**
-	 * Waits for a keypress
-	 */
-	zchar console_read_key(zword timeout);
-
-	/**
-	 * Write a single character to the scrollback buffer.
-	 *
-	 */
-	void scrollback_char(zchar c);
-
-	/**
-	 * Write a string to the scrollback buffer.
-	 */
-	void scrollback_word(const zchar *s);
-
-	/**
-	 * Send an input line to the scrollback buffer.
-	 */
-	void scrollback_write_input(const zchar *buf, zchar key);
-
-	/**
-	 * Remove an input line from the scrollback buffer.
-	 */
-	void scrollback_erase_input(const zchar *buf);
-
-	/**
-	 * Start printing a "debugging" message.
-	 */
-	void stream_mssg_on();
-
-	/**
-	 * Stop printing a "debugging" message.
-	 */
-	void stream_mssg_off();
-
-	/**
-	 * Send a single character to the output stream.
-	 */
-	void stream_char(zchar c);
-
-	/**
-	 * Send a string of characters to the output streams.
-	 */
-	void stream_word(const zchar *s);
-
-	/**
-	 * Send a newline to the output streams.
-	 */
-	void stream_new_line();
-
-	/**
-	 * Read a single keystroke from the current input stream.
-	 */
-	zchar stream_read_key(zword timeout, zword routine, bool hot_keys);
-
-	/**
-	 * Read a line of input from the current input stream.
-	 */
-	zchar stream_read_input(int max, zchar *buf, zword timeout, zword routine,
-		bool hot_keys, bool no_scripting);
-
-	/*
-	 * script_open
-	 *
-	 * Open the transscript file. 'AMFV' makes this more complicated as it
-	 * turns transscription on/off several times to exclude some text from
-	 * the transscription file. This wasn't a problem for the original V4
-	 * interpreters which always sent transscription to the printer, but it
-	 * means a problem to modern interpreters that offer to open a new file
-	 * every time transscription is turned on. Our solution is to append to
-	 * the old transscription file in V1 to V4, and to ask for a new file
-	 * name in V5+.
-	 *
-	 */
-	void script_open();
-
-	/*
-	 * Stop transscription.
-	 */
-	void script_close();
-
-	/**
-	 * Write a newline to the transscript file.
-	 */
-	void script_new_line();
-
-	/**
-	 * Write a single character to the transscript file.
-	 */
-	void script_char(zchar c);
-
-	/**
-	 * Write a string to the transscript file.
-	 */
-	void script_word(const zchar *s);
-
-	/**
-	 * Send an input line to the transscript file.
-	 */
-	void script_write_input(const zchar *buf, zchar key);
-
-	/**
-	 * Remove an input line from the transscript file.
-	 */
-	void script_erase_input(const zchar *buf);
-
-	/**
-	 * Start sending a "debugging" message to the transscript file.
-	 */
-	void script_mssg_on();
-
-	/**
-	 * Stop writing a "debugging" message.
-	 */
-	void script_mssg_off();
-
-	/**
-	 * Open a file to record the player's input.
-	 */
-	void record_open();
-
-	/**
-	 * Stop recording the player's input.
-	 */
-	void record_close();
-
-	/**
-	 * Helper function for record_char.
-	 */
-	void record_code(int c, bool force_encoding);
-
-	/**
-	 * Write a character to the command file.
-	 */
-	void record_char(zchar c);
-
-	/**
-	 * Copy a keystroke to the command file.
-	 */
-	void record_write_key(zchar key);
-
-	/**
-	 * Copy a line of input to a command file.
-	 */
-	void record_write_input(const zchar *buf, zchar key);
-
-	/**
-	 * Open a file of commands for playback.
-	 */
-	void replay_open();
-
-	/**
-	 * Stop playback of commands.
-	 */
-	void replay_close();
-
-	/*
-	 * Helper function for replay_key and replay_line.
-	 */
-	int replay_code();
-
-	/**
-	 * Read a character from the command file.
-	 */
-	zchar replay_char();
-
-	/**
-	 * Read a keystroke from a command file.
-	 */
-	zchar replay_read_key();
-
-	/*
-	 * Read a line of input from a command file.
-	 */
-	zchar replay_read_input(zchar *buf);
-
-	/**@}*/
-
-	/**
-	 * \defgroup Text support methods
-	 * @{
-	 */
-
-	/**
-	 * Map a ZSCII character into Unicode.
-	 */
-	zchar translate_from_zscii(zbyte c);
-
-	/**
-	 * Convert a Unicode character to ZSCII, returning 0 on failure.
-	 */
-	zbyte unicode_to_zscii(zchar c);
-
-	/**
-	 * Map a Unicode character onto the ZSCII alphabet.
-	 *
-	 */
-	zbyte translate_to_zscii(zchar c);
-
-	/**
-	 * Return a character from one of the three character sets.
-	 */
-	zchar alphabet(int set, int index);
-
-	/**
-	 * Find the number of bytes used for dictionary resolution.
-	 */
-	void find_resolution();
-
-	/**
-	 * Copy a ZSCII string from the memory to the global "decoded" string.
-	 */
-	void load_string(zword addr, zword length);
-
-	/**
-	 * Encode the Unicode text in the global "decoded" string then write
-	 * the result to the global "encoded" array. (This is used to look up
-	 * words in the dictionary.) Up to V3 the vocabulary resolution is
-	 * two, from V4 it is three, and from V9 it is any number of words.
-	 * Because each word contains three Z-characters, that makes six or
-	 * nine Z-characters respectively. Longer words are chopped to the
-	 * proper size, shorter words are are padded out with 5's. For word
-	 * completion we pad with 0s and 31s, the minimum and maximum
-	 * Z-characters.
-	 */
-	void encode_text(int padding);
-
-	/**
-	 * Convert _encoded text to Unicode. The _encoded text consists of 16bit
-	 * words. Every word holds 3 Z-characters (5 bits each) plus a spare
-	 * bit to mark the last word. The Z-characters translate to ZSCII by
-	 * looking at the current current character set. Some select another
-	 * character set, others refer to abbreviations.
-	 *
-	 * There are several different string types:
-	 *
-	 *    LOW_STRING - from the lower 64KB (byte address)
-	 *    ABBREVIATION - from the abbreviations table (word address)
-	 *    HIGH_STRING - from the end of the memory map (packed address)
-	 *    EMBEDDED_STRING - from the instruction stream (at PC)
-	 *    VOCABULARY - from the dictionary (byte address)
-	 *
-	 * The last type is only used for word completion.
-	 */
-	void decode_text(string_type st, zword addr);
-
-	/**
-	 * Print a signed 16bit number.
-	 */
-	void print_num(zword value);
-
-	/**
-	 * print_object
-	 *
-	 * Print an object description.
-	 *
-	 */
-	void print_object(zword object);
-
-	/**
-	 * Scan a dictionary searching for the given word. The first argument
-	 * can be
-	 *
-	 * 0x00 - find the first word which is >= the given one
-	 * 0x05 - find the word which exactly matches the given one
-	 * 0x1f - find the last word which is <= the given one
-	 *
-	 * The return value is 0 if the search fails.
-	 */
-	zword lookup_text(int padding, zword dct);
-
-	/**
-	 * tokenise_text
-	 *
-	 * Translate a single word to a token and append it to the token
-	 * buffer. Every token consists of the address of the dictionary
-	 * entry, the length of the word and the offset of the word from
-	 * the start of the text buffer. Unknown words cause empty slots
-	 * if the flag is set (such that the text can be scanned several
-	 * times with different dictionaries); otherwise they are zero.
-	 *
-	 */
-	void tokenise_text(zword text, zword length, zword from, zword parse, zword dct, bool flag);
-
-	/**
-	 * Split an input line into words and translate the words to tokens.
-	 */
-	void tokenise_line(zword text, zword token, zword dct, bool flag);
-
-	/**
-	 * Scan the vocabulary to complete the last word on the input line
-	 * (similar to "tcsh" under Unix). The return value is
-	 *
-	 *    2 ==> completion is impossible
-	 *    1 ==> completion is ambiguous
-	 *    0 ==> completion is successful
-	 *
-	 * The function also returns a string in its second argument. In case
-	 * of 2, the string is empty; in case of 1, the string is the longest
-	 * extension of the last word on the input line that is common to all
-	 * possible completions (for instance, if the last word on the input
-	 * is "fo" and its only possible completions are "follow" and "folly"
-	 * then the string is "ll"); in case of 0, the string is an extension
-	 * to the last word that results in the only possible completion.
-	 */
-	int completion(const zchar *buffer, zchar *result);
-
-	 /**
-	  * Convert a Unicode character to lowercase.
-	  * Taken from Zip2000 by Kevin Bracey.
-	  */
-	zchar unicode_tolower(zchar c);
-
-	/**@}*/
-protected:
-	/**
-	 * \defgroup General Opcode methods
-	 * @{
-	 */
-
-	/*
-	 * Load and execute an extended opcode.
-	 */
-	void __extended__();
-
-	/*
-	 * Exit game because an unknown opcode has been hit.
-	 */
-	void __illegal__();
-
-	/*
-	 * Store the current _stack frame for later use with z_throw.
-	 *
-	 *	no zargs used
-	 */
-	void z_catch();
-
-	/**
-	 * Go back to the given _stack frame and return the given value.
-	 *
-	 *	zargs[0] = value to return
-	 *	zargs[1] = _stack frame
-	 */
-	void z_throw();
-
-	/*
-	 * Call a subroutine and discard its result.
-	 *
-	 * 	zargs[0] = packed address of subroutine
-	 *	zargs[1] = first argument (optional)
-	 *	...
-	 *	zargs[7] = seventh argument (optional)
-	 */
-	void z_call_n();
-
-	/**
-	 * Call a subroutine and store its result.
-	 *
-	 * 	zargs[0] = packed address of subroutine
-	 *	zargs[1] = first argument (optional)
-	 *	...
-	 *	zargs[7] = seventh argument (optional)
-	 */
-	void z_call_s();
-
-	/**
-	 * Branch if subroutine was called with >= n arg's.
-	 *
-	 * 	zargs[0] = number of arguments
-	 */
-	void z_check_arg_count();
-
-	/**
-	 * Jump unconditionally to the given address.
-	 *
-	 *	zargs[0] = PC relative address
-	 */
-	void z_jump();
-
-	/*
-	 * No operation.
-	 *
-	 *	no zargs used
-	 */
-	void z_nop();
-
-	/*
-	 * Stop game and exit interpreter.
-	 *
-	 *	no zargs used
-	 */
-	void z_quit();
-
-	/*
-	 * Return from a subroutine with the given value.
-	 *
-	 *	zargs[0] = value to return
-	 */
-	void z_ret();
-
-	/*
-	 * Return from a subroutine with a value popped off the stack.
-	 *
-	 *	no zargs used
-	 */
-	void z_ret_popped();
-
-	/*
-	 * Return from a subroutine with false (0).
-	 *
-	 * 	no zargs used
-	 */
-	void z_rfalse();
-
-	/*
-	 * Return from a subroutine with true (1).
-	 *
-	 * 	no zargs used
-	 */
-	void z_rtrue();
-
-	/**
-	 * Store a random number or set the random number seed.
-	 *
-	 *	zargs[0] = range (positive) or seed value (negative)
-	 */
-	void z_random();
-
-	/**
-	 * Load / play / stop / discard a sound effect.
-	 *
-	 *	zargs[0] = number of bleep (1 or 2) or sample
-	 *	zargs[1] = operation to perform (samples only)
-	 *	zargs[2] = repeats and volume (play sample only)
-	 *	zargs[3] = end-of-sound routine (play sample only, optional)
-	 *
-	 * Note: Volumes range from 1 to 8, volume 255 is the default volume.
-	 *	 Repeats are stored in the high byte, 255 is infinite loop.
-	 *
-	 */
-	void z_sound_effect();
-
-	/**
-	 * Branch if the story file is a legal copy
-	 */
-	void z_piracy();
-
-	/**
-	 * Save the current Z-machine state for a future undo.
-	 *
-	 *	no zargs used
-	 */
-	void z_save_undo();
-
-	/**
-	 * Restore a Z-machine state from memory.
-	 *
-	 *	no zargs used
-	 */
-	void z_restore_undo();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Input Opcode methods
-	 * @{
-	 */
-
-	 /**
-	  * Add or remove a menu and branch if successful.
-	  *
-	  * 	zargs[0] = number of menu
-	  *	zargs[1] = table of menu entries or 0 to remove menu
-	  */
-	void z_make_menu();
-
-	/**
-	 * Read a line of input and (in V5+) store the terminating key.
-	 *
-	 *	zargs[0] = address of text buffer
-	 *	zargs[1] = address of token buffer
-	 *	zargs[2] = timeout in tenths of a second (optional)
-	 *	zargs[3] = packed address of routine to be called on timeout
-	 */
-	void z_read();
-
-	/**
-	 * Read and store a key.
-	 *
-	 *	zargs[0] = input device (must be 1)
-	 *	zargs[1] = timeout in tenths of a second (optional)
-	 *	zargs[2] = packed address of routine to be called on timeout
-	 */
-	void z_read_char();
-
-	/**
-	 * z_read_mouse, write the current mouse status into a table.
-	 *
-	 *	zargs[0] = address of table
-	 */
-	void z_read_mouse();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Math Opcode methods
-	 * @{
-	 */
-
-	/**
-	 * 16 bit addition.
-	 *
-	 *	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_add();
-
-	/**
-	 * Bitwise AND operation.
-	 *
-	 *	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_and();
-
-	/**
-	 * Arithmetic SHIFT operation.
-	 *
-	 *	zargs[0] = value
-	 *	zargs[1] = #positions to shift left (positive) or right
-	 */
-	void z_art_shift();
-
-	/**
-	 * Signed 16bit division.
-	 *
-	 *	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_div();
-
-	/**
-	 * B ranch if the first value equals any of the following.
-	 *
-	 *	zargs[0] = first value
-	 *	zargs[1] = second value (optional)
-	 *	...
-	 *	zargs[3] = fourth value (optional)
-	 */
-	void z_je();
-
-	/**
-	 * Branch if the first value is greater than the second.
-	 *
-	 *	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_jg();
-
-	/**
-	 * Branch if the first value is less than the second.
-	 *
-	 *	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_jl();
-
-	/**
-	 * Branch if value is zero.
-	 *
-	 * 	zargs[0] = value
-	 */
-	void z_jz();
-
-	/**
-	 * Logical SHIFT operation.
-	 *
-	 * 	zargs[0] = value
-	 *	zargs[1] = #positions to shift left (positive) or right (negative)
-	 */
-	void z_log_shift();
-
-	/*
-	 * Remainder after signed 16bit division.
-	 *
-	 * 	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_mod();
-
-	/**
-	 * 16 bit multiplication.
-	 *
-	 * 	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_mul();
-
-	/**
-	 * Bitwise NOT operation.
-	 *
-	 * 	zargs[0] = value
-	 */
-	void z_not();
-
-	/**
-	 * Bitwise OR operation.
-	 *
-	 *	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_or();
-
-	/**
-	 * 16 bit substraction.
-	 *
-	 *	zargs[0] = first value
-	 *	zargs[1] = second value
-	 */
-	void z_sub();
-
-	/**
-	 * Branch if all the flags of a bit mask are set in a value.
-	 *
-	 *	zargs[0] = value to be examined
-	 *	zargs[1] = bit mask
-	 */
-	void z_test();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Object Opcode methods
-	 * @{
-	 */
-	
-	/**
-	 * Branch if the first object is inside the second.
-	 *
-	 *        zargs[0] = first object
-	 *        zargs[1] = second object
-	 */
-	void z_jin();
-
-	/**
-	 * Store the child of an object.
-	 *
-	 *        zargs[0] = object
-	 */
-	void z_get_child();
-
-	/**
-	 * Store the number of the first or next property.
-	 *
-	 *        zargs[0] = object
-	 *        zargs[1] = address of current property (0 gets the first property)
-	 */
-	void z_get_next_prop();
-
-	/**
-	 * Store the parent of an object.
-	 *
-	 *        zargs[0] = object
-	 */
-	void z_get_parent();
-
-	/**
-	 * Store the value of an object property.
-	 *
-	 *        zargs[0] = object
-	 *        zargs[1] = number of property to be examined
-	 */
-	void z_get_prop();
-
-	/**
-	 * Store the address of an object property.
-	 *
-	 *        zargs[0] = object
-	 *        zargs[1] = number of property to be examined
-	 */
-	void z_get_prop_addr();
-
-	/**
-	 * Store the length of an object property.
-	 *
-	 *         zargs[0] = address of property to be examined
-	 */
-	void z_get_prop_len();
-
-	/**
-	 * Store the sibling of an object.
-	 *
-	 *        zargs[0] = object
-	 */
-	void z_get_sibling();
-	
-	/**
-	 * Make an object the first child of another object.
-	 *
-	 *        zargs[0] = object to be moved
-	 *        zargs[1] = destination object
-	 */
-	void z_insert_obj();
-
-	/**
-	 * Set the value of an object property.
-	 *
-	 *        zargs[0] = object
-	 *        zargs[1] = number of property to set
-	 *        zargs[2] = value to set property to
-	 */
-	void z_put_prop();
-
-	/**
-	 * Unlink an object from its parent and siblings.
-	 *
-	 *        zargs[0] = object
-	 */
-	void z_remove_obj();
-
-	/**
-	 * Set an object attribute.
-	 *
-	 *        zargs[0] = object
-	 *        zargs[1] = number of attribute to set
-	 */
-	void z_set_attr();
-
-	/**
-	 * Branch if an object attribute is set.
-	 *
-	 *        zargs[0] = object
-	 *        zargs[1] = number of attribute to test
-	 */
-	void z_test_attr();
-
-	/**
-	 * Clear an object attribute.
-	 *
-	 *        zargs[0] = object
-	 *        zargs[1] = number of attribute to be cleared
-	 */
-	void z_clear_attr();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Screen Opcode methods
-	 * @{
-	 */
-
-	/**
-	 * Turn text buffering on/off.
-	 *
-	 *		zargs[0] = new text buffering flag (0 or 1)
-	 */
-	void z_buffer_mode();
-
-	/**
-	 * Set the screen buffering mode.
-	 *
-	 *	zargs[0] = mode
-	 */
-	void z_buffer_screen();
-
-	/**
-	 * Erase the line starting at the cursor position.
-	 *
-	 *		zargs[0] = 1 + #units to erase (1 clears to the end of the line)
-	 */
-	void z_erase_line();
-
-	/**
-	 * Erase a window or the screen to background colour.
-	 *
-	 *		zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit)
-	 */
-	void z_erase_window();
-
-	/**
-	 * Write the cursor coordinates into a table.
-	 *
-	 *		zargs[0] = address to write information to
-	 */
-	void z_get_cursor();
-
-	/**
-	 * Print ASCII text in a rectangular area.
-	 *
-	 *		zargs[0] = address of text to be printed
-	 *		zargs[1] = width of rectangular area
-	 *		zargs[2] = height of rectangular area (optional)
-	 *		zargs[3] = number of char's to skip between lines (optional)
-	 */
-	void z_print_table();
-
-	/**
-	 * Set the foreground and background colours
-	 * to specific RGB colour values.
-	 *
-	 *	zargs[0] = foreground colour
-	 *	zargs[1] = background colour
-	 *	zargs[2] = window (-3 is the current one, optional)
-	 */
-	void z_set_true_colour();
-
-	/**
-	 * Set the foreground and background colours.
-	 *
-	 *		zargs[0] = foreground colour
-	 *		zargs[1] = background colour
-	 *		zargs[2] = window (-3 is the current one, optional)
-	 */
-	void z_set_colour();
-
-	/**
-	 * Set the font for text output and store the previous font.
-	 *
-	 *		 zargs[0] = number of font or 0 to keep current font
-	 */
-	void z_set_font();
-
-	/**
-	 * Set the cursor position or turn the cursor on/off.
-	 *
-	 *		zargs[0] = y-coordinate or -2/-1 for cursor on/off
-	 *		zargs[1] = x-coordinate
-	 *		zargs[2] = window (-3 is the current one, optional)
-	 */
-	void z_set_cursor();
-
-	/**
-	 * z_set_text_style, set the style for text output.
-	 *
-	 *		 zargs[0] = style flags to set or 0 to reset text style
-	 */
-	void z_set_text_style();
-
-	/**
-	 * Select the current window.
-	 *
-	 *		zargs[0] = window to be selected (-3 is the current one)
-	 */
-	void z_set_window();
-
-	/**
-	 * Display the status line for V1 to V3 games.
-	 *
-	 *		no zargs used
-	 */
-	void pad_status_line(int column);
-
-	/**
-	 * Display the status line for V1 to V3 games.
-	 *
-	 *		no zargs used
-	 */
-	void z_show_status();
-
-	/**
-	 * Split the screen into an upper (1) and lower (0) window.
-	 *
-	 *		zargs[0] = height of upper window in screen units (V6) or #lines
-	 */
-	void z_split_window();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Stream Opcode methods
-	 * @{
-	 */
-
-	/**
-	 * Select an input stream.
-	 *
-	 *	zargs[0] = input stream to be selected
-	 */
-	void z_input_stream();
-
-	/**
-	 * Open or close an output stream.
-	 *
-	 *	zargs[0] = stream to open (positive) or close (negative)
-	 *	zargs[1] = address to redirect output to (stream 3 only)
-	 *	zargs[2] = width of redirected output (stream 3 only, optional)
-	 */
-	void z_output_stream();
-
-	/**
-	 * Re-load dynamic area, clear the stack and set the PC.
-	 *
-	 * 	no zargs used
-	 */
-	void z_restart();
-
-	/**
-	 * Save [a part of] the Z-machine state to disk.
-	 *
-	 *	zargs[0] = address of memory area to save (optional)
-	 *	zargs[1] = number of bytes to save
-	 *	zargs[2] = address of suggested file name
-	 */
-	void z_save();
-
-	/**
-	 * Restore [a part of] a Z-machine state from disk
-	 *
-	 *	zargs[0] = address of area to restore (optional)
-	 *	zargs[1] = number of bytes to restore
-	 *	zargs[2] = address of suggested file name
-	 */
-	void z_restore();
-
-	/**
-	 * Check the story file integrity.
-	 *
-	 *	no zargs used
-	 */
-	void z_verify();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Table Opcode methods
-	 * @{
-	 */
-
-	/**
-	 * Copy a table or fill it with zeroes.
-	 *
-	 *	zargs[0] = address of table
-	 * 	zargs[1] = destination address or 0 for fill
-	 *	zargs[2] = size of table
-	 *
-	 * Note: Copying is safe even when source and destination overlap; but
-	 *       if zargs[1] is negative the table _must_ be copied forwards.
-	 */
-	void z_copy_table();
-
-	/**
-	 * Store a value from a table of bytes.
-	 *
-	 *	zargs[0] = address of table
-	 *	zargs[1] = index of table entry to store
-	 */
-	void z_loadb();
-
-	/**
-	 * Store a value from a table of words.
-	 *
-	 *	zargs[0] = address of table
-	 *	zargs[1] = index of table entry to store
-	 */
-	void z_loadw();
-
-	/**
-	 * Find and store the address of a target within a table.
-	 *
-	 *	zargs[0] = target value to be searched for
-	 *	zargs[1] = address of table
-	 *	zargs[2] = number of table entries to check value against
-	 *	zargs[3] = type of table (optional, defaults to 0x82)
-	 *
-	 * Note: The table is a word array if bit 7 of zargs[3] is set; otherwise
-	 *       it's a byte array. The lower bits hold the address step.
-	 */
-	void z_scan_table();
-
-	/**
-	 * Write a byte into a table of bytes.
-	 *
-	 *	zargs[0] = address of table
-	 *	zargs[1] = index of table entry
-	 *	zargs[2] = value to be written
-	 */
-	void z_storeb();
-
-	/**
-	 * Write a word into a table of words.
-	 *
-	 *	zargs[0] = address of table
-	 *	zargs[1] = index of table entry
-	 *	zargs[2] = value to be written
-	 */
-	void z_storew();
-
-	/**@}*/
-
-	/**
-	 * \defgroup Text Opcode methods
-	 * @{
-	 */
-
-	/**
-	 * Test if a unicode character can be printed (bit 0) and read (bit 1).
-	 *
-	 * 	zargs[0] = Unicode
-	 */
-	void z_check_unicode();
-
-	/**
-	 * Encode a ZSCII string for use in a dictionary.
-	 *
-	 *	zargs[0] = address of text buffer
-	 *	zargs[1] = length of ASCII string
-	 *	zargs[2] = offset of ASCII string within the text buffer
-	 *	zargs[3] = address to store encoded text in
-	 *
-	 * This is a V5+ opcode and therefore the dictionary resolution must be
-	 * three 16bit words.
-	 */
-	void z_encode_text();
-
-	/**
-	 * Print a new line.
-	 *
-	 * 	no zargs used
-	 *
-	 */
-	void z_new_line();
-
-	/**
-	 * Print a string embedded in the instruction stream.
-	 *
-	 *	no zargs used
-	 */
-	void z_print();
-
-	/**
-	 * Print a string from the lower 64KB.
-	 *
-	 *	zargs[0] = address of string to print
-	 */
-	void z_print_addr();
-
-	/**
-	 * Print a single ZSCII character.
-	 *
-	 *	zargs[0] = ZSCII character to be printed
-	 */
-	void z_print_char();
-
-	/**
-	 * Print a formatted table.
-	 *
-	 *	zargs[0] = address of formatted table to be printed
-	 */
-	void z_print_form();
-
-	/**
-	 * Print a signed number.
-	 *
-	 * 	zargs[0] = number to print
-	 */
-	void z_print_num();
-
-	/**
-	 * Print an object description.
-	 *
-	 * 	zargs[0] = number of object to be printed
-	 */
-	void z_print_obj();
-
-	/**
-	 * Print the string at the given packed address.
-	 *
-	 * 	zargs[0] = packed address of string to be printed
-	 */
-	void z_print_paddr();
-
-	/*
-	 * Print the string at PC, print newline then return true.
-	 *
-	 * 	no zargs used
-	 */
-	void z_print_ret();
-
-	/**
-	 * Print unicode character
-	 *
-	 * 	zargs[0] = Unicode
-	 */
-	void z_print_unicode();
-
-	/**
-	 * Make a lexical analysis of a ZSCII string.
-	 *
-	 *	zargs[0] = address of string to analyze
-	 *	zargs[1] = address of token buffer
-	 *	zargs[2] = address of dictionary (optional)
-	 *	zargs[3] = set when unknown words cause empty slots (optional)
-	 */
-	void z_tokenise();
-
-	 /**@}*/
-
-	/**
-	 * \defgroup Variable Opcode methods
-	 * @{
-	 */
-
-	/**
-	 * Decrement a variable.
-	 *
-	 * 	zargs[0] = variable to decrement
-	 */
-	void z_dec();
-
-	/**
-	 * Decrement a variable and branch if now less than value.
-	 *
-	 * 	zargs[0] = variable to decrement
-	 * 	zargs[1] = value to check variable against
-	 */
-	void z_dec_chk();
-
-	/**
-	 * Increment a variable.
-	 *
-	 * 	zargs[0] = variable to increment
-	 */
-	void z_inc();
-
-	/**
-	 * Increment a variable and branch if now greater than value.
-	 *
-	 * 	zargs[0] = variable to increment
-	 * 	zargs[1] = value to check variable against
-	 */
-	void z_inc_chk();
-
-	/**
-	 * Store the value of a variable.
-	 *
-	 *	zargs[0] = variable to store
-	 */
-	void z_load();
-
-	/**
-	 * Pop a value off the game stack and discard it.
-	 *
-	 *	no zargs used
-	 */
-	void z_pop();
-
-	/**
-	 * Pop n values off the game or user stack and discard them.
-	 *
-	 *	zargs[0] = number of values to discard
-	 *	zargs[1] = address of user stack (optional)
-	 */
-	void z_pop_stack();
-
-	/**
-	 * Pop a value off...
-	 *
-	 * a) ...the game or a user stack and store it (V6)
-	 *
-	 *	zargs[0] = address of user stack (optional)
-	 *
-	 * b) ...the game stack and write it to a variable (other than V6)
-	 *
-	 *	zargs[0] = variable to write value to
-	 */
-	void z_pull();
-
-	/**
-	 * Push a value onto the game stack.
-	 *
-	 *	zargs[0] = value to push onto the stack
-	 */
-	void z_push();
-
-	/**
-	 * Push a value onto a user stack then branch if successful.
-	 *
-	 *	zargs[0] = value to push onto the stack
-	 *	zargs[1] = address of user stack
-	 */
-	void z_push_stack();
-
-	/**
-	 * Write a value to a variable.
-	 *
-	 * 	zargs[0] = variable to be written to
-	 *      zargs[1] = value to write
-	 */
-	void z_store();
-
-	/**@}*/
-public:
-	/**
-	 * Constructor
-	 */
-	Processor(OSystem *syst, const GargoyleGameDescription *gameDesc);
-
-	/**
-	 * Initialization
-	 */
-	void initialize();
-
-	/**
-	 * Z-code interpreter main loop
-	 */
-	void interpret();
-
-	/**
-	 * \defgroup Memory access methods
-	 * @{
-	 */
-
-	/**
-	 * Square brackets operator
-	 */
-	zbyte &operator[](uint addr) { return zmp[addr]; }
-
-	/**
-	 * Read a code byte
-	 */
-	zbyte codeByte() { return *pcp++; }
-
-	/**
-	 * Read a code word
-	 */
-	zword codeWord() {
-		zword v = READ_BE_UINT16(pcp);
-		pcp += 2;
-		return v;
-	}
-
-	/**
-	 * Return a code word at a given address
-	 */
-	zword codeWordIdx(uint addr) const {
-		return READ_BE_UINT16(pcp + addr);
-	}
-
-	/**
-	 * Return the current program execution offset
-	 */
-	uint getPC() const { return pcp - zmp; }
-
-	/**
-	 * Set the program execution offset
-	 */
-	void setPC(uint addr) { pcp = zmp + addr; }
-
-	 /**@}*/
-};
-
-} // End of namespace Frotz
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/gargoyle/frotz/processor_buffer.cpp b/engines/gargoyle/frotz/processor_buffer.cpp
deleted file mode 100644
index 62980fb..0000000
--- a/engines/gargoyle/frotz/processor_buffer.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-#include "common/algorithm.h"
-#include "common/textconsole.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-const char *const Processor::ERR_MESSAGES[ERR_NUM_ERRORS] = {
-    "Text buffer overflow",
-    "Store out of dynamic memory",
-    "Division by zero",
-    "Illegal object",
-    "Illegal attribute",
-    "No such property",
-    "Stack overflow",
-    "Call to illegal address",
-    "Call to non-routine",
-    "Stack underflow",
-    "Illegal opcode",
-    "Bad stack frame",
-    "Jump to illegal address",
-    "Can't save while in interrupt",
-    "Nesting stream #3 too deep",
-    "Illegal window",
-    "Illegal window property",
-    "Print at illegal address",
-    "Illegal dictionary word length",
-    "@jin called with object 0",
-    "@get_child called with object 0",
-    "@get_parent called with object 0",
-    "@get_sibling called with object 0",
-    "@get_prop_addr called with object 0",
-    "@get_prop called with object 0",
-    "@put_prop called with object 0",
-    "@clear_attr called with object 0",
-    "@set_attr called with object 0",
-    "@test_attr called with object 0",
-    "@move_object called moving object 0",
-    "@move_object called moving into object 0",
-    "@remove_object called with object 0",
-    "@get_next_prop called with object 0"
-};
-
-void Processor::flush_buffer() {
-	/* Make sure we stop when flush_buffer is called from flush_buffer.
-	 * Note that this is difficult to avoid as we might print a newline
-	 * during flush_buffer, which might cause a newline interrupt, that
-	 * might execute any arbitrary opcode, which might flush the buffer.
-	 */
-	if (_locked || bufferEmpty())
-		return;
-
-	// Send the buffer to the output streams
-	_buffer[_bufPos] = '\0';
-	
-	_locked = true;
-	stream_word(_buffer);
-	_locked = false;
-
-	// Reset the buffer
-	_bufPos = 0;
-	_prevC = '\0';
-}
-
-void Processor::print_char(zchar c) {
-	static bool flag = false;
-
-	if (message || ostream_memory || enable_buffering) {
-		if (!flag) {
-			// Characters 0 and ZC_RETURN are special cases
-			if (c == ZC_RETURN) {
-				new_line();
-				return;
-			}
-			if (c == 0)
-				return;
-
-			// Flush the buffer before a whitespace or after a hyphen
-			if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (_prevC == '-' && c != '-'))
-				flush_buffer();
-
-			// Set the flag if this is part one of a style or font change
-			if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE)
-				flag = true;
-
-			// Remember the current character code
-			_prevC = c;
-		} else {
-			flag = false;
-		}
-
-		// Insert the character into the buffer
-		_buffer[_bufPos++] = c;
-
-		if (_bufPos == TEXT_BUFFER_SIZE)
-			error("Text buffer overflow");
-	} else {
-		stream_char(c);
-	}
-}
-
-void Processor::print_string(const char *s) {
-	char c;
-
-	while ((c = *s++) != 0) {
-		if (c == '\n')
-			new_line();
-		else
-			print_char(c);
-	}
-}
-
-void Processor::print_long(uint value, int base) {
-	unsigned long i;
-	char c;
-
-	for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base) {
-		if (value >= i || i == 1) {
-			c = (value / i) % base;
-			print_char(c + (c <= 9 ? '0' : 'a' - 10));
-		}
-	}
-}
-
-void Processor::new_line()  {
-	flush_buffer();
-	stream_new_line();
-}
-
-void Processor::runtimeError(ErrorCode errNum) {
-	int wasfirst;
-
-	if (errNum <= 0 || errNum > ERR_NUM_ERRORS)
-		return;
-
-	if (_err_report_mode == ERR_REPORT_FATAL
-		|| (!_ignore_errors && errNum <= ERR_MAX_FATAL)) {
-		flush_buffer();
-		error(ERR_MESSAGES[errNum - 1]);
-		return;
-	}
-
-	wasfirst = (_errorCount[errNum - 1] == 0);
-	_errorCount[errNum - 1]++;
-
-	if ((_err_report_mode == ERR_REPORT_ALWAYS)
-		|| (_err_report_mode == ERR_REPORT_ONCE && wasfirst)) {
-		long pc;
-		GET_PC(pc);
-		print_string("Warning: ");
-		print_string(ERR_MESSAGES[errNum - 1]);
-		print_string(" (PC = ");
-		print_long(pc, 16);
-		print_char(')');
-
-		if (_err_report_mode == ERR_REPORT_ONCE) {
-			print_string(" (will ignore further occurrences)");
-		}
-		else {
-			print_string(" (occurence ");
-			print_long(_errorCount[errNum - 1], 10);
-			print_char(')');
-		}
-
-		new_line();
-	}
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp
deleted file mode 100644
index fa9da21..0000000
--- a/engines/gargoyle/frotz/processor_input.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-#define INPUT_BUFFER_SIZE 200
-
-bool Processor::read_yes_or_no(const char *s) {
-    zchar key;
-
-    print_string(s);
-    print_string("? (y/n) >");
-
-    key = stream_read_key(0, 0, false);
-
-    if (key == 'y' || key == 'Y') {
-		print_string("y\n");
-		return true;
-    } else {
-		print_string("n\n");
-		return false;
-    }
-}
-
-void Processor::read_string(int max, zchar *buffer) {
-    zchar key;
-
-    buffer[0] = 0;
-
-    do {
-		key = stream_read_input(max, buffer, 0, 0, false, false);
-    } while (key != ZC_RETURN);
-}
-
-bool Processor::is_terminator(zchar key) {
-	if (key == ZC_TIME_OUT)
-		return true;
-	if (key == ZC_RETURN)
-		return true;
-	if (key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX)
-		return true;
-
-	if (h_terminating_keys != 0) {
-		if (key >= ZC_ARROW_MIN && key <= ZC_MENU_CLICK) {
-
-			zword addr = h_terminating_keys;
-			zbyte c;
-
-			do {
-				LOW_BYTE(addr, c);
-				if (c == 255 || key == translate_from_zscii(c))
-					return true;
-				addr++;
-			} while (c != 0);
-		}
-	}
-
-	return false;
-}
-
-void Processor::z_make_menu() {
-	// This opcode was only used for the Macintosh version of Journey.
-	// It controls menus with numbers greater than 2 (menus 0, 1 and 2
-	// are system menus).
-	branch(false);
-}
-
-int Processor::read_number() {
-    zchar buffer[6];
-    int value = 0;
-    int i;
-
-    read_string(5, buffer);
-
-    for (i = 0; buffer[i] != 0; i++)
-	if (buffer[i] >= '0' && buffer[i] <= '9')
-	    value = 10 * value + buffer[i] - '0';
-
-    return value;
-}
-
-void Processor::z_read() {
-    zchar buffer[INPUT_BUFFER_SIZE];
-    zword addr;
-    zchar key;
-    zbyte max, size;
-    zbyte c;
-    int i;
-
-    // Supply default arguments
-    if (zargc < 3)
-		zargs[2] = 0;
-
-    // Get maximum input size
-    addr = zargs[0];
-
-	LOW_BYTE(addr, max);
-
-    if (h_version <= V4)
-		max--;
-
-    if (max >= INPUT_BUFFER_SIZE)
-		max = INPUT_BUFFER_SIZE - 1;
-
-    // Get initial input size
-    if (h_version >= V5) {
-		addr++;
-		LOW_BYTE(addr, size);
-	} else {
-		size = 0;
-	}
-
-    // Copy initial input to local buffer
-    for (i = 0; i < size; i++) {
-		addr++;
-		LOW_BYTE(addr, c);
-		buffer[i] = translate_from_zscii(c);
-    }
-    buffer[i] = 0;
-
-    // Draw status line for V1 to V3 games
-    if (h_version <= V3)
-		z_show_status();
-
-    // Read input from current input stream
-    key = stream_read_input(
-		max, buffer,		// buffer and size
-		zargs[2],			// timeout value
-		zargs[3],			// timeout routine
-		false,				// enable hot keys
-		h_version == V6		// no script in V6
-	);
-
-	if (key == ZC_BAD)
-		return;
-
-    // Perform save_undo for V1 to V4 games
-    if (h_version <= V4)
-		save_undo();
-
-    // Copy local buffer back to dynamic memory
-    for (i = 0; buffer[i] != 0; i++) {
-		if (key == ZC_RETURN) {
-			buffer[i] = unicode_tolower (buffer[i]);
-		}
-
-		storeb((zword) (zargs[0] + ((h_version <= V4) ? 1 : 2) + i), translate_to_zscii (buffer[i]));
-    }
-
-    // Add null character (V1-V4) or write input length into 2nd byte
-    if (h_version <= V4)
-		storeb((zword) (zargs[0] + 1 + i), 0);
-    else
-		storeb((zword) (zargs[0] + 1), i);
-
-    // Tokenise line if a token buffer is present
-    if (key == ZC_RETURN && zargs[1] != 0)
-		tokenise_line (zargs[0], zargs[1], 0, false);
-
-    // Store key
-    if (h_version >= V5)
-		store(translate_to_zscii(key));
-}
-
-void Processor::z_read_char() {
-    zchar key;
-
-    // Supply default arguments
-    if (zargc < 2)
-		zargs[1] = 0;
-
-    // Read input from the current input stream
-    key = stream_read_key(
-		zargs[1],	// timeout value
-		zargs[2],	// timeout routine
-		false  		// enable hot keys
-	);
-    
-	if (key == ZC_BAD)
-		return;
-
-    // Store key
-    store (translate_to_zscii (key));
-}
-
-void Processor::z_read_mouse(){
-    zword btn;
-
-    // Read the mouse position, the last menu click and which buttons are down
-    btn = os_read_mouse();
-    hx_mouse_y = mouse_y;
-    hx_mouse_x = mouse_x;
-
-    storew((zword) (zargs[0] + 0), hx_mouse_y);
-    storew((zword) (zargs[0] + 2), hx_mouse_x);
-    storew((zword) (zargs[0] + 4), btn);			// mouse button bits
-    storew((zword) (zargs[0] + 6), menu_selected);	// menu selection
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_maths.cpp b/engines/gargoyle/frotz/processor_maths.cpp
deleted file mode 100644
index e64dd55..0000000
--- a/engines/gargoyle/frotz/processor_maths.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-void Processor::z_add() {
-    store((zword)((short)zargs[0] + (short)zargs[1]));
-}
-
-void Processor::z_and() {
-    store((zword)(zargs[0] & zargs[1]));
-}
-
-void Processor::z_art_shift() {
-    if ((short)zargs[1] > 0)
-		store((zword)((short)zargs[0] << (short)zargs[1]));
-    else
-		store((zword)((short)zargs[0] >> - (short)zargs[1]));
-}
-
-void Processor::z_div() {
-    if (zargs[1] == 0)
-		runtimeError(ERR_DIV_ZERO);
-
-    store((zword)((short)zargs[0] / (short)zargs[1]));
-}
-
-void Processor::z_je() {
-    branch(
-		zargc > 1 && (zargs[0] == zargs[1] || (
-		zargc > 2 && (zargs[0] == zargs[2] || (
-		zargc > 3 && (zargs[0] == zargs[3])))))
-	);
-}
-
-void Processor::z_jg() {
-    branch((short)zargs[0] > (short)zargs[1]);
-}
-
-void Processor::z_jl() {
-    branch((short)zargs[0] < (short)zargs[1]);
-}
-
-void Processor::z_jz() {
-    branch((short)zargs[0] == 0);
-}
-
-void Processor::z_log_shift() {
-    if ((short)zargs[1] > 0)
-		store((zword)(zargs[0] << (short)zargs[1]));
-    else
-		store((zword)(zargs[0] >> - (short)zargs[1]));
-}
-
-void Processor::z_mod() {
-    if (zargs[1] == 0)
-		runtimeError(ERR_DIV_ZERO);
-
-    store((zword)((short)zargs[0] % (short)zargs[1]));
-}
-
-void Processor::z_mul() {
-    store((zword)((short)zargs[0] * (short)zargs[1]));
-}
-
-void Processor::z_not() {
-    store((zword)~zargs[0]);
-}
-
-void Processor::z_or() {
-    store((zword)(zargs[0] | zargs[1]));
-}
-
-void Processor::z_sub() {
-    store((zword)((short)zargs[0] - (short)zargs[1]));
-}
-
-void Processor::z_test() {
-	branch((zargs[0] & zargs[1]) == zargs[1]);
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_mem.cpp b/engines/gargoyle/frotz/processor_mem.cpp
deleted file mode 100644
index d46402c..0000000
--- a/engines/gargoyle/frotz/processor_mem.cpp
+++ /dev/null
@@ -1,218 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-void Processor::flagsChanged(zbyte value) {
-	if (value & SCRIPTING_FLAG) {
-		if (!ostream_script)
-			script_open();
-	} else {
-		if (ostream_script)
-			script_close();
-	}
-}
-
-int Processor::save_undo() {
-	long diff_size;
-	zword stack_size;
-	undo_t *p;
-
-	if (_undo_slots == 0)
-		// undo feature unavailable
-		return -1;
-
-	// save undo possible
-	while (last_undo != curr_undo) {
-		p = last_undo;
-		last_undo = last_undo->prev;
-		delete p;
-		undo_count--;
-	}
-	if (last_undo)
-		last_undo->next = nullptr;
-	else
-		first_undo = nullptr;
-
-	if (undo_count == _undo_slots)
-		free_undo(1);
-
-	diff_size = mem_diff(zmp, prev_zmp, h_dynamic_size, undo_diff);
-	stack_size = _stack + STACK_SIZE - _sp;
-	do {
-		p = (undo_t *) malloc(sizeof(undo_t) + diff_size + stack_size * sizeof(*_sp));
-		if (p == nullptr)
-			free_undo(1);
-	} while (!p && undo_count);
-	if (p == nullptr)
-		return -1;
-
-	GET_PC(p->pc);
-	p->frame_count = _frameCount;
-	p->diff_size = diff_size;
-	p->stack_size = stack_size;
-	p->frame_offset = _fp - _stack;
-	memcpy(p + 1, undo_diff, diff_size);
-	memcpy((zbyte *)(p + 1) + diff_size, _sp, stack_size * sizeof(*_sp));
-
-	if (!first_undo) {
-		p->prev = nullptr;
-		first_undo = p;
-	} else {
-		last_undo->next = p;
-		p->prev = last_undo;
-	}
-
-	p->next = nullptr;
-	curr_undo = last_undo = p;
-	undo_count++;
-
-	return 1;
-}
-
-int Processor::restore_undo(void) {
-	if (_undo_slots == 0)
-		// undo feature unavailable
-		return -1;
-
-	if (curr_undo == nullptr)
-		// no saved game state
-		return 0;
-
-	// undo possible
-	memcpy(zmp, prev_zmp, h_dynamic_size);
-	SET_PC(curr_undo->pc);
-	_sp = _stack + STACK_SIZE - curr_undo->stack_size;
-	_fp = _stack + curr_undo->frame_offset;
-	_frameCount = curr_undo->frame_count;
-	mem_undiff((zbyte *)(curr_undo + 1), curr_undo->diff_size, prev_zmp);
-	memcpy(_sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
-		curr_undo->stack_size * sizeof(*_sp));
-
-	curr_undo = curr_undo->prev;
-
-	restart_header();
-
-	return 2;
-}
-
-/**
- * TOR: glkify -- this is for V6 only
- */
-static zword get_max_width(zword win) { return 80; }
-
-void Processor::memory_open(zword table, zword xsize, bool buffering) {
-	if (_redirect.size() < MAX_NESTING) {
-		if (!buffering)
-			xsize = 0xffff;
-		if (buffering && (short)xsize <= 0)
-			xsize = get_max_width((zword)(-(short)xsize));
-
-		storew(table, 0);
-
-		_redirect.push(Redirect(xsize, table));
-		ostream_memory = true;
-	} else {
-		runtimeError(ERR_STR3_NESTING);
-	}
-}
-
-void Processor::memory_new_line() {
-	zword size;
-	zword addr;
-
-	Redirect &r = _redirect.top();
-	r._total += r._width;
-	r._width = 0;
-
-	addr = r._table;
-
-	LOW_WORD(addr, size);
-	addr += 2;
-
-	if (r._xSize != 0xffff) {
-		r._table = addr + size;
-		size = 0;
-	} else {
-		storeb((zword)(addr + (size++)), 13);
-	}
-
-	storew(r._table, size);
-}
-
-void Processor::memory_word(const zchar *s) {
-	zword size;
-	zword addr;
-	zchar c;
-
-	Redirect &r = _redirect.top();
-	if (h_version == V6) {
-		int width = os_string_width(s);
-
-		if (r._xSize != 0xffff) {
-			if (r._width + width > r._xSize) {
-
-				if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
-					width = os_string_width(++s);
-
-				memory_new_line();
-			}
-		}
-
-		r._width += width;
-	}
-
-	addr = r._table;
-
-	LOW_WORD(addr, size);
-	addr += 2;
-
-	while ((c = *s++) != 0)
-		storeb((zword)(addr + (size++)), translate_to_zscii(c));
-
-	storew(r._table, size);
-}
-
-void Processor::memory_close(void) {
-	if (!_redirect.empty()) {
-		Redirect &r = _redirect.top();
-
-		if (r._xSize != 0xffff)
-			memory_new_line();
-
-		if (h_version == V6) {
-			h_line_width = (r._xSize != 0xffff) ? r._total : r._width;
-
-			SET_WORD(H_LINE_WIDTH, h_line_width);
-		}
-
-		_redirect.pop();
-		if (_redirect.empty())
-			ostream_memory = false;
-	}
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_objects.cpp b/engines/gargoyle/frotz/processor_objects.cpp
deleted file mode 100644
index 986bf87..0000000
--- a/engines/gargoyle/frotz/processor_objects.cpp
+++ /dev/null
@@ -1,732 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-#define MAX_OBJECT 2000
-
-enum O1 {
-	O1_PARENT          = 4,
-	O1_SIBLING         = 5,
-	O1_CHILD           = 6,
-	O1_PROPERTY_OFFSET = 7,
-	O1_SIZE            = 9
-};
-
-enum O4 {
-	O4_PARENT          = 6,
-	O4_SIBLING         = 8,
-	O4_CHILD           = 10,
-	O4_PROPERTY_OFFSET = 12,
-	O4_SIZE            = 14
-};
-
-zword Processor::object_address(zword obj) {
-    // Check object number
-    if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) {
-        print_string("@Attempt to address illegal object ");
-        print_num(obj);
-        print_string(".  This is normally fatal.");
-        new_line();
-        runtimeError(ERR_ILL_OBJ);
-    }
-
-    // Return object address
-    if (h_version <= V3)
-        return h_objects + ((obj - 1) * O1_SIZE + 62);
-    else
-        return h_objects + ((obj - 1) * O4_SIZE + 126);
-}
-
-zword Processor::object_name(zword object) {
-    zword obj_addr;
-    zword name_addr;
-
-    obj_addr = object_address(object);
-
-    // The object name address is found at the start of the properties
-    if (h_version <= V3)
-        obj_addr += O1_PROPERTY_OFFSET;
-    else
-        obj_addr += O4_PROPERTY_OFFSET;
-
-	LOW_WORD(obj_addr, name_addr);
-
-    return name_addr;
-}
-
-zword Processor::first_property(zword obj) {
-    zword prop_addr;
-    zbyte size;
-
-    // Fetch address of object name
-    prop_addr = object_name (obj);
-
-    // Get length of object name
-	LOW_BYTE(prop_addr, size);
-
-    // Add name length to pointer
-    return prop_addr + 1 + 2 * size;
-}
-
-zword Processor::next_property(zword prop_addr) {
-    zbyte value;
-
-    // Load the current property id
-	LOW_BYTE(prop_addr, value);
-    prop_addr++;
-
-    // Calculate the length of this property
-    if (h_version <= V3)
-        value >>= 5;
-    else if (!(value & 0x80))
-        value >>= 6;
-    else {
-		LOW_BYTE(prop_addr, value);
-        value &= 0x3f;
-
-        if (value == 0)
-			// demanded by Spec 1.0
-			value = 64;
-    }
-
-    // Add property length to current property pointer
-    return prop_addr + value + 1;
-}
-
-void Processor::unlink_object(zword object) {
-    zword obj_addr;
-    zword parent_addr;
-    zword sibling_addr;
-
-    if (object == 0) {
-        runtimeError(ERR_REMOVE_OBJECT_0);
-        return;
-    }
-
-    obj_addr = object_address(object);
-
-    if (h_version <= V3) {
-
-        zbyte parent;
-        zbyte younger_sibling;
-        zbyte older_sibling;
-        zbyte zero = 0;
-
-        // Get parent of object, and return if no parent
-        obj_addr += O1_PARENT;
-		LOW_BYTE(obj_addr, parent);
-        if (!parent)
-            return;
-
-        // Get (older) sibling of object and set both parent and sibling pointers to 0
-		SET_BYTE(obj_addr, zero);
-        obj_addr += O1_SIBLING - O1_PARENT;
-		LOW_BYTE(obj_addr, older_sibling);
-		SET_BYTE(obj_addr, zero);
-
-        // Get first child of parent (the youngest sibling of the object)
-        parent_addr = object_address(parent) + O1_CHILD;
-		LOW_BYTE(parent_addr, younger_sibling);
-
-        // Remove object from the list of siblings
-		if (younger_sibling == object)
-			SET_BYTE(parent_addr, older_sibling);
-        else {
-            do {
-                sibling_addr = object_address(younger_sibling) + O1_SIBLING;
-				LOW_BYTE(sibling_addr, younger_sibling);
-            } while (younger_sibling != object);
-			SET_BYTE(sibling_addr, older_sibling);
-        }
-    } else {
-        zword parent;
-        zword younger_sibling;
-        zword older_sibling;
-        zword zero = 0;
-
-        // Get parent of object, and return if no parent
-        obj_addr += O4_PARENT;
-		LOW_WORD(obj_addr, parent);
-        if (!parent)
-            return;
-
-        // Get (older) sibling of object and set both parent and sibling pointers to 0
-		SET_WORD(obj_addr, zero);
-        obj_addr += O4_SIBLING - O4_PARENT;
-		LOW_WORD(obj_addr, older_sibling);
-		SET_WORD(obj_addr, zero);
-
-        // Get first child of parent (the youngest sibling of the object)
-        parent_addr = object_address(parent) + O4_CHILD;
-		LOW_WORD(parent_addr, younger_sibling);
-
-        // Remove object from the list of siblings
-		if (younger_sibling == object) {
-			SET_WORD(parent_addr, older_sibling);
-		} else {
-            do {
-                sibling_addr = object_address(younger_sibling) + O4_SIBLING;
-				LOW_WORD(sibling_addr, younger_sibling);
-            } while (younger_sibling != object);
-			SET_WORD(sibling_addr, older_sibling);
-        }
-    }
-}
-
-void Processor::z_clear_attr() {
-    zword obj_addr;
-    zbyte value;
-
-    if (_storyId == SHERLOCK)
-        if (zargs[1] == 48)
-            return;
-
-    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
-        runtimeError(ERR_ILL_ATTR);
-
-    // If we are monitoring attribute assignment display a short note
-    if (_attribute_assignment) {
-        stream_mssg_on();
-        print_string("@clear_attr ");
-        print_object(zargs[0]);
-        print_string(" ");
-        print_num(zargs[1]);
-        stream_mssg_off();
-    }
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_CLEAR_ATTR_0);
-        return;
-    }
-
-    // Get attribute address
-    obj_addr = object_address(zargs[0]) + zargs[1] / 8;
-
-    // Clear attribute bit
-	LOW_BYTE(obj_addr, value);
-    value &= ~(0x80 >> (zargs[1] & 7));
-	SET_BYTE(obj_addr, value);
-}
-
-void Processor::z_jin() {
-    zword obj_addr;
-
-    // If we are monitoring object locating display a short note
-    if (_object_locating) {
-        stream_mssg_on();
-        print_string("@jin ");
-        print_object(zargs[0]);
-        print_string(" ");
-        print_object(zargs[1]);
-        stream_mssg_off();
-    }
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_JIN_0);
-        branch(0 == zargs[1]);
-        return;
-    }
-
-    obj_addr = object_address(zargs[0]);
-
-    if (h_version <= V3) {
-        zbyte parent;
-
-        // Get parent id from object
-        obj_addr += O1_PARENT;
-		LOW_BYTE(obj_addr, parent);
-
-        // Branch if the parent is obj2
-        branch(parent == zargs[1]);
-
-    } else {
-        zword parent;
-
-        // Get parent id from object
-        obj_addr += O4_PARENT;
-		LOW_WORD(obj_addr, parent);
-
-        // Branch if the parent is obj2
-        branch(parent == zargs[1]);
-    }
-}
-
-void Processor::z_get_child() {
-    zword obj_addr;
-
-    // If we are monitoring object locating display a short note
-    if (_object_locating) {
-        stream_mssg_on();
-        print_string("@get_child ");
-        print_object(zargs[0]);
-        stream_mssg_off();
-    }
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_GET_CHILD_0);
-        store(0);
-        branch(false);
-        return;
-    }
-
-    obj_addr = object_address(zargs[0]);
-
-    if (h_version <= V3) {
-        zbyte child;
-
-        // Get child id from object
-        obj_addr += O1_CHILD;
-		LOW_BYTE(obj_addr, child);
-
-        // Store child id and branch
-        store(child);
-        branch(child);
-    } else {
-        zword child;
-
-        // Get child id from object
-        obj_addr += O4_CHILD;
-		LOW_WORD(obj_addr, child);
-
-        // Store child id and branch
-        store(child);
-        branch(child);
-    }
-}
-
-void Processor::z_get_next_prop() {
-    zword prop_addr;
-    zbyte value;
-    zbyte mask;
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_GET_NEXT_PROP_0);
-        store(0);
-        return;
-    }
-
-    // Property id is in bottom five (six) bits
-    mask = (h_version <= V3) ? 0x1f : 0x3f;
-
-    // Load address of first property
-    prop_addr = first_property(zargs[0]);
-
-    if (zargs[1] != 0) {
-        // Scan down the property list
-        do {
-			LOW_BYTE(prop_addr, value);
-            prop_addr = next_property(prop_addr);
-        } while ((value & mask) > zargs[1]);
-
-        // Exit if the property does not exist
-        if ((value & mask) != zargs[1])
-            runtimeError(ERR_NO_PROP);
-    }
-
-    // Return the property id
-	LOW_BYTE(prop_addr, value);
-    store((zword) (value & mask));
-}
-
-void Processor::z_get_parent() {
-    zword obj_addr;
-
-    // If we are monitoring object locating display a short note
-    if (_object_locating) {
-        stream_mssg_on();
-        print_string("@get_parent ");
-        print_object(zargs[0]);
-        stream_mssg_off();
-    }
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_GET_PARENT_0);
-        store(0);
-        return;
-    }
-
-    obj_addr = object_address(zargs[0]);
-
-    if (h_version <= V3) {
-        zbyte parent;
-
-        // Get parent id from object
-        obj_addr += O1_PARENT;
-		LOW_BYTE(obj_addr, parent);
-
-        // Store parent
-        store(parent);
-
-	} else {
-        zword parent;
-
-        // Get parent id from object
-        obj_addr += O4_PARENT;
-		LOW_WORD(obj_addr, parent);
-
-        // Store parent
-        store(parent);
-    }
-}
-
-void Processor::z_get_prop() {
-    zword prop_addr;
-    zword wprop_val;
-    zbyte bprop_val;
-    zbyte value;
-    zbyte mask;
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_GET_PROP_0);
-        store(0);
-        return;
-    }
-
-    // Property id is in bottom five (six) bits
-    mask = (h_version <= V3) ? 0x1f : 0x3f;
-
-    // Load address of first property
-    prop_addr = first_property(zargs[0]);
-
-    // Scan down the property list
-    for (;;) {
-		LOW_BYTE(prop_addr, value);
-        if ((value & mask) <= zargs[1])
-            break;
-        prop_addr = next_property(prop_addr);
-    }
-
-    if ((value & mask) == zargs[1]) {
-		// property found
-
-        // Load property(byte or word sized)
-        prop_addr++;
-
-        if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
-			LOW_BYTE(prop_addr, bprop_val);
-            wprop_val = bprop_val;
-		} else {
-			LOW_WORD(prop_addr, wprop_val);
-		}
-    } else {
-		// property not found
-
-        // Load default value
-        prop_addr = h_objects + 2 * (zargs[1] - 1);
-		LOW_WORD(prop_addr, wprop_val);
-    }
-
-    // Store the property value
-    store(wprop_val);
-}
-
-void Processor::z_get_prop_addr() {
-    zword prop_addr;
-    zbyte value;
-    zbyte mask;
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_GET_PROP_ADDR_0);
-        store(0);
-        return;
-    }
-
-    if (_storyId == BEYOND_ZORK)
-        if (zargs[0] > MAX_OBJECT)
-            { store(0); return; }
-
-    // Property id is in bottom five (six) bits
-    mask = (h_version <= V3) ? 0x1f : 0x3f;
-
-    // Load address of first property
-    prop_addr = first_property(zargs[0]);
-
-    // Scan down the property list
-    for (;;) {
-		LOW_BYTE(prop_addr, value);
-        if ((value & mask) <= zargs[1])
-            break;
-        prop_addr = next_property(prop_addr);
-    }
-
-    // Calculate the property address or return zero
-    if ((value & mask) == zargs[1]) {
-
-        if (h_version >= V4 && (value & 0x80))
-            prop_addr++;
-        store((zword) (prop_addr + 1));
-
-    } else {
-		store(0);
-	}
-}
-
-void Processor::z_get_prop_len() {
-    zword addr;
-    zbyte value;
-
-    // Back up the property pointer to the property id
-    addr = zargs[0] - 1;
-	LOW_BYTE(addr, value);
-
-    // Calculate length of property
-    if (h_version <= V3)
-        value = (value >> 5) + 1;
-    else if (!(value & 0x80))
-        value = (value >> 6) + 1;
-    else {
-        value &= 0x3f;
-
-        if (value == 0)
-			value = 64;        // demanded by Spec 1.0
-    }
-
-    // Store length of property
-    store(value);
-}
-
-void Processor::z_get_sibling() {
-    zword obj_addr;
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_GET_SIBLING_0);
-        store(0);
-        branch(false);
-        return;
-    }
-
-    obj_addr = object_address(zargs[0]);
-
-    if (h_version <= V3) {
-        zbyte sibling;
-
-        // Get sibling id from object
-        obj_addr += O1_SIBLING;
-		LOW_BYTE(obj_addr, sibling);
-
-        // Store sibling and branch
-        store(sibling);
-        branch(sibling);
-
-	} else {
-        zword sibling;
-
-        // Get sibling id from object
-        obj_addr += O4_SIBLING;
-		LOW_WORD(obj_addr, sibling);
-
-        // Store sibling and branch
-        store(sibling);
-        branch(sibling);
-    }
-}
-
-void Processor::z_insert_obj() {
-    zword obj1 = zargs[0];
-    zword obj2 = zargs[1];
-    zword obj1_addr;
-    zword obj2_addr;
-
-    // If we are monitoring object movements display a short note
-    if (_object_movement) {
-        stream_mssg_on();
-        print_string("@move_obj ");
-        print_object(obj1);
-        print_string(" ");
-        print_object(obj2);
-        stream_mssg_off();
-    }
-
-    if (obj1 == 0) {
-        runtimeError(ERR_MOVE_OBJECT_0);
-        return;
-    }
-
-    if (obj2 == 0) {
-        runtimeError(ERR_MOVE_OBJECT_TO_0);
-        return;
-    }
-
-    // Get addresses of both objects
-    obj1_addr = object_address(obj1);
-    obj2_addr = object_address(obj2);
-
-    // Remove object 1 from current parent
-    unlink_object(obj1);
-
-    // Make object 1 first child of object 2
-    if (h_version <= V3) {
-        zbyte child;
-
-        obj1_addr += O1_PARENT;
-		SET_BYTE(obj1_addr, obj2);
-        obj2_addr += O1_CHILD;
-		LOW_BYTE(obj2_addr, child);
-		SET_BYTE(obj2_addr, obj1);
-        obj1_addr += O1_SIBLING - O1_PARENT;
-		SET_BYTE(obj1_addr, child);
-
-    } else {
-        zword child;
-
-        obj1_addr += O4_PARENT;
-		SET_WORD(obj1_addr, obj2);
-        obj2_addr += O4_CHILD;
-		LOW_WORD(obj2_addr, child);
-		SET_WORD(obj2_addr, obj1);
-        obj1_addr += O4_SIBLING - O4_PARENT;
-		SET_WORD(obj1_addr, child);
-    }
-}
-
-void Processor::z_put_prop() {
-    zword prop_addr;
-    zword value;
-    zbyte mask;
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_PUT_PROP_0);
-        return;
-    }
-
-    // Property id is in bottom five or six bits
-    mask = (h_version <= V3) ? 0x1f : 0x3f;
-
-    // Load address of first property
-    prop_addr = first_property(zargs[0]);
-
-    // Scan down the property list
-    for (;;) {
-		LOW_BYTE(prop_addr, value);
-        if ((value & mask) <= zargs[1])
-            break;
-
-        prop_addr = next_property(prop_addr);
-    }
-
-    // Exit if the property does not exist
-    if ((value & mask) != zargs[1])
-        runtimeError(ERR_NO_PROP);
-
-    // Store the new property value (byte or word sized)
-    prop_addr++;
-
-    if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) {
-        zbyte v = zargs[2];
-		SET_BYTE(prop_addr, v);
-    } else {
-        zword v = zargs[2];
-		SET_WORD(prop_addr, v);
-    }
-}
-
-void Processor::z_remove_obj() {
-    // If we are monitoring object movements display a short note
-    if (_object_movement) {
-        stream_mssg_on();
-        print_string("@remove_obj ");
-        print_object(zargs[0]);
-        stream_mssg_off();
-    }
-
-    // Call unlink_object to do the job
-    unlink_object(zargs[0]);
-}
-
-void Processor::z_set_attr() {
-    zword obj_addr;
-    zbyte value;
-
-    if (_storyId == SHERLOCK)
-        if (zargs[1] == 48)
-            return;
-
-    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
-        runtimeError(ERR_ILL_ATTR);
-
-    // If we are monitoring attribute assignment display a short note
-    if (_attribute_assignment) {
-        stream_mssg_on();
-        print_string("@set_attr ");
-        print_object(zargs[0]);
-        print_string(" ");
-        print_num(zargs[1]);
-        stream_mssg_off();
-    }
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_SET_ATTR_0);
-        return;
-    }
-
-    // Get attribute address
-    obj_addr = object_address(zargs[0]) + zargs[1] / 8;
-
-    // Load attribute byte
-	LOW_BYTE(obj_addr, value);
-
-    // Set attribute bit
-    value |= 0x80 >> (zargs[1] & 7);
-
-    // Store attribute byte
-	SET_BYTE(obj_addr, value);
-}
-
-void Processor::z_test_attr() {
-    zword obj_addr;
-    zbyte value;
-
-    if (zargs[1] > ((h_version <= V3) ? 31 : 47))
-        runtimeError(ERR_ILL_ATTR);
-
-    // If we are monitoring attribute testing display a short note
-    if (_attribute_testing) {
-        stream_mssg_on();
-        print_string("@test_attr ");
-        print_object(zargs[0]);
-        print_string(" ");
-        print_num(zargs[1]);
-        stream_mssg_off();
-    }
-
-    if (zargs[0] == 0) {
-        runtimeError(ERR_TEST_ATTR_0);
-        branch(false);
-        return;
-    }
-
-    // Get attribute address
-    obj_addr = object_address(zargs[0]) + zargs[1] / 8;
-
-    // Load attribute byte
-	LOW_BYTE(obj_addr, value);
-
-    // Test attribute
-    branch(value & (0x80 >> (zargs[1] & 7)));
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_screen.cpp b/engines/gargoyle/frotz/processor_screen.cpp
deleted file mode 100644
index 77e2e94..0000000
--- a/engines/gargoyle/frotz/processor_screen.cpp
+++ /dev/null
@@ -1,528 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-void Processor::screen_mssg_on() {
-	if (gos_curwin == gos_lower) {
-		oldstyle = curstyle;
-		glk_set_style(style_Preformatted);
-		glk_put_string("\n    ");
-	}
-}
-
-void Processor::screen_mssg_off() {
-	if (gos_curwin == gos_lower) {
-		glk_put_char('\n');
-		zargs[0] = 0;
-		z_set_text_style();
-		zargs[0] = oldstyle;
-		z_set_text_style();
-	}
-}
-
-void Processor::screen_char(zchar c) {
-	if (gos_linepending && (gos_curwin == gos_linewin)) {
-		gos_cancel_pending_line();
-		if (gos_curwin == gos_upper) {
-			curx = 1;
-			cury ++;
-		}
-		if (c == '\n')
-			return;
-	}
-
-	// check fixed flag in header, game can change it at whim
-	int forcefix = ((h_flags & FIXED_FONT_FLAG) != 0);
-	int curfix = ((curstyle & FIXED_WIDTH_STYLE) != 0);
-	if (forcefix && !curfix) {
-		zargs[0] = 0xf000;	// tickle tickle!
-		z_set_text_style();
-		fixforced = true;
-	} else if (!forcefix && fixforced) {
-		zargs[0] = 0xf000;	// tickle tickle!
-		z_set_text_style();
-		fixforced = false;
-	}
-
-	if (gos_upper && gos_curwin == gos_upper) {
-		if (c == '\n' || c == ZC_RETURN) {
-			glk_put_char('\n');
-			curx = 1;
-			cury ++;
-		} else {
-			if (cury == 1) {
-				if (curx <= ((sizeof statusline / sizeof(zchar)) - 1)) {
-					statusline[curx - 1] = c;
-					statusline[curx] = 0;
-				}
-				if (curx < h_screen_cols) {
-					glk_put_char_uni(c);
-				} else if (curx == h_screen_cols) {
-					glk_put_char_uni(c);
-					glk_window_move_cursor(gos_curwin, curx-1, cury-1);
-				} else {
-					smartstatusline();
-				}
-
-				curx++;
-			} else {
-				if (curx < h_screen_cols) {
-					glk_put_char_uni(c);
-				} else if (curx == (h_screen_cols)) {
-					glk_put_char_uni(c);
-					glk_window_move_cursor(gos_curwin, curx-1, cury-1);
-				}
-
-				curx++;
-			}
-		}
-	} else if (gos_curwin == gos_lower) {
-		if (c == ZC_RETURN)
-			glk_put_char('\n');
-		else glk_put_char_uni(c);
-	}
-}
-
-void Processor::screen_new_line() {
-	screen_char('\n');
-}
-
-void Processor::screen_word(const zchar *s) {
-	zchar c;
-	while ((c = *s++) != 0) {
-		if (c == ZC_NEW_FONT)
-			s++;
-		else if (c == ZC_NEW_STYLE)
-			s++;
-		else
-			screen_char(c);
-	}
-}
-
-
-void Processor::z_buffer_mode() {
-	// No implementation
-}
-
-void Processor::z_buffer_screen() {
-	store(0);
-}
-
-void Processor::z_erase_line() {
-	int i;
-
-	if (gos_upper && gos_curwin == gos_upper) {
-		for (i = 0; i < h_screen_cols + 1 - curx; i++)
-			glk_put_char(' ');
-		glk_window_move_cursor(gos_curwin, curx - 1, cury - 1);
-	}
-}
-
-void Processor::z_erase_window() {
-	short w = zargs[0];
-	if (w == -2)
-	{
-		if (gos_upper) {
-			glk_set_window(gos_upper);
-#ifdef GARGLK
-			garglk_set_zcolors(curr_fg, curr_bg);
-#endif /* GARGLK */
-			glk_window_clear(gos_upper);
-			glk_set_window(gos_curwin);
-		}
-		glk_window_clear(gos_lower);
-	}
-	if (w == -1)
-	{
-		if (gos_upper) {
-			glk_set_window(gos_upper);
-#ifdef GARGLK
-			garglk_set_zcolors(curr_fg, curr_bg);
-#endif /* GARGLK */
-			glk_window_clear(gos_upper);
-		}
-		glk_window_clear(gos_lower);
-		split_window(0);
-		glk_set_window(gos_lower);
-		gos_curwin = gos_lower;
-	}
-	if (w == 0)
-		glk_window_clear(gos_lower);
-	if (w == 1 && gos_upper)
-		glk_window_clear(gos_upper);
-}
-
-void Processor::z_get_cursor() {
-	storew((zword) (zargs[0] + 0), cury);
-	storew((zword) (zargs[0] + 2), curx);
-}
-
-void Processor::z_print_table() {
-	zword addr = zargs[0];
-	zword x;
-	int i, j;
-
-	// Supply default arguments
-	if (zargc < 3)
-		zargs[2] = 1;
-	if (zargc < 4)
-		zargs[3] = 0;
-
-	// Write text in width x height rectangle
-	x = curx;
-
-	for (i = 0; i < zargs[2]; i++) {
-		if (i != 0) {
-			cury += 1;
-			curx = x;
-		}
-
-		for (j = 0; j < zargs[1]; j++) {
-
-			zbyte c;
-
-			LOW_BYTE(addr, c);
-			addr++;
-
-			print_char(c);
-		}
-
-		addr += zargs[3];
-	}
-}
-
-#define zB(i) ((((i >> 10) & 0x1F) << 3) | (((i >> 10) & 0x1F) >> 2))
-#define zG(i) ((((i >>  5) & 0x1F) << 3) | (((i >>  5) & 0x1F) >> 2))
-#define zR(i) ((((i      ) & 0x1F) << 3) | (((i      ) & 0x1F) >> 2))
-
-#define zRGB(i) (zR(i) << 16 | zG(i) << 8 | zB(i))
-
-void Processor::z_set_true_colour() {
-	int zfore = zargs[0];
-	int zback = zargs[1];
-
-	if (!(zfore < 0))
-		zfore = zRGB(zargs[0]);
-
-	if (!(zback < 0))
-		zback = zRGB(zargs[1]);
-
-#ifdef GARGLK
-	garglk_set_zcolors(zfore, zback);
-#endif /* GARGLK */
-
-	curr_fg = zfore;
-	curr_bg = zback;
-}
-
-static const int zcolor_map[] = {
-	-2,						///<  0 = current
-	-1,						///<  1 = default
-	0x0000,					///<  2 = black
-	0x001D,					///<  3 = red
-	0x0340,					///<  4 = green
-	0x03BD,					///<  5 = yellow
-	0x59A0,					///<  6 = blue
-	0x7C1F,					///<  7 = magenta
-	0x77A0,					///<  8 = cyan
-	0x7FFF,					///<  9 = white
-	0x5AD6,					///< 10 = light grey
-	0x4631,					///< 11 = medium grey
-	0x2D6B,					///< 12 = dark grey
-};
-
-#define zcolor_NUMCOLORS    (13)
-
-void Processor::z_set_colour() {
-	int zfore = zargs[0];
-	int zback = zargs[1];
-
-	switch (zfore) {
-	case -1:
-		zfore = -3;
-		break;
-
-	case 0:
-	case 1:
-		zfore = zcolor_map[zfore];
-		break;
-
-	default:
-		if (zfore < zcolor_NUMCOLORS)
-			zfore = zRGB(zcolor_map[zfore]);
-		break;
-	}
-
-	switch (zback) {
-	case -1:
-		zback = -3;
-
-	case 0:
-	case 1:
-		zback = zcolor_map[zback];
-		break;
-
-	default:
-		if (zback < zcolor_NUMCOLORS)
-			zback = zRGB(zcolor_map[zback]);
-		break;
-	}
-
-#ifdef GARGLK
-	garglk_set_zcolors(zfore, zback);
-#endif /* GARGLK */
-
-	curr_fg = zfore;
-	curr_bg = zback;
-}
-
-void Processor::z_set_font() {
-	zword font = zargs[0];
-
-	switch (font) {
-		case 0:
-			// previous font
-			temp_font = curr_font;
-			curr_font = prev_font;
-			prev_font = temp_font;
-			zargs[0] = 0xf000;	// tickle tickle!
-			z_set_text_style();
-			store (curr_font);
-			break;
-
-		case 1: /* normal font */
-			prev_font = curr_font;
-			curr_font = 1;
-			zargs[0] = 0xf000;	// tickle tickle!
-			z_set_text_style();
-			store (prev_font);
-			break; 
-
-		case 4: /* fixed-pitch font*/
-			prev_font = curr_font;
-			curr_font = 4;
-			zargs[0] = 0xf000;	// tickle tickle!
-			z_set_text_style();
-			store (prev_font);
-			break;
-
-		case 2: // picture font, undefined per 1.1
-		case 3: // character graphics font
-		default: // unavailable
-			store (0);
-			break;
-	}
-}
-
-void Processor::z_set_cursor() {
-	cury = zargs[0];
-	curx = zargs[1];
-
-	if (gos_upper) {
-		if (cury > mach_status_ht) {
-			mach_status_ht = cury;
-			reset_status_ht();
-		}
-
-		glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
-	}
-}
-
-void Processor::z_set_text_style() {
-	int style;
-
-	if (zargs[0] == 0)
-		curstyle = 0;
-	else if (zargs[0] != 0xf000) /* not tickle time */
-		curstyle |= zargs[0];
-
-	if (h_flags & FIXED_FONT_FLAG || curr_font == 4)
-		style = curstyle | FIXED_WIDTH_STYLE;
-	else
-		style = curstyle;
-
-	if (gos_linepending && gos_curwin == gos_linewin)
-		return;
-
-	if (style & REVERSE_STYLE) {
-#ifdef GARGLK
-		garglk_set_reversevideo(true);
-#endif /* GARGLK */
-	}
-
-	if (style & FIXED_WIDTH_STYLE) {
-		if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
-			glk_set_style(style_BlockQuote);	// monoz
-		else if (style & EMPHASIS_STYLE)
-			glk_set_style(style_Alert);			// monoi
-		else if (style & BOLDFACE_STYLE)
-			glk_set_style(style_Subheader);		// monob
-		else
-			glk_set_style(style_Preformatted);	// monor
-	} else {
-		if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
-			glk_set_style(style_Note);			// propz
-		else if (style & EMPHASIS_STYLE)
-			glk_set_style(style_Emphasized);	// propi
-		else if (style & BOLDFACE_STYLE)
-			glk_set_style(style_Header);		// propb
-		else
-			glk_set_style(style_Normal);		// propr
-	}
-
-	if (curstyle == 0) {
-#ifdef GARGLK
-		garglk_set_reversevideo(false);
-#endif /* GARGLK */
-	}
-}
-
-void Processor::z_set_window() {
-	int win = zargs[0];
-
-	if (win == 0) {
-		glk_set_window(gos_lower);
-		gos_curwin = gos_lower;
-	} else {
-		if (gos_upper)
-			glk_set_window(gos_upper);
-		gos_curwin = gos_upper;
-	}
-
-	if (win == 0)
-		enable_scripting = true;
-	else
-		enable_scripting = false;
-
-	zargs[0] = 0xf000;	// tickle tickle!
-	z_set_text_style();
-}
-
-void Processor::pad_status_line(int column) {
-	int spaces;
-	spaces = (h_screen_cols + 1 - curx) - column;
-	while (spaces-- > 0)
-		print_char(' ');
-}
-
-void Processor::z_show_status() {
-	zword global0;
-	zword global1;
-	zword global2;
-	zword addr;
-
-	bool brief = false;
-
-	if (!gos_upper)
-		return;
-
-	// One V5 game (Wishbringer Solid Gold) contains this opcode by accident,
-	// so just return if the version number does not fit
-	if (h_version >= V4)
-		return;
-
-	// Read all relevant global variables from the memory of the Z-machine
-	// into local variables
-
-	addr = h_globals;
-	LOW_WORD(addr, global0);
-	addr += 2;
-	LOW_WORD(addr, global1);
-	addr += 2;
-	LOW_WORD(addr, global2);
-
-	// Move to top of the status window, and print in reverse style.
-	glk_set_window(gos_upper);
-	gos_curwin = gos_upper;
-
-#ifdef GARGLK
-	garglk_set_reversevideo(true);
-#endif /* GARGLK */
-
-	curx = cury = 1;
-	glk_window_move_cursor(gos_upper, 0, 0);
-
-	// If the screen width is below 55 characters then we have to use
-	// the brief status line format
-	if (h_screen_cols < 55)
-		brief = true;
-
-	// Print the object description for the global variable 0
-	print_char (' ');
-	print_object (global0);
-
-	// A header flag tells us whether we have to display the current
-	// time or the score/moves information
-	if (h_config & CONFIG_TIME) {
-		// print hours and minutes
-		zword hours = (global1 + 11) % 12 + 1;
-
-		pad_status_line (brief ? 15 : 20);
-
-		print_string ("Time: ");
-
-		if (hours < 10)
-			print_char (' ');
-		print_num (hours);
-
-		print_char (':');
-
-		if (global2 < 10)
-			print_char ('0');
-		print_num (global2);
-
-		print_char (' ');
-
-		print_char ((global1 >= 12) ? 'p' : 'a');
-		print_char ('m');
-
-	} else {
-		// print score and moves
-		pad_status_line (brief ? 15 : 30);
-
-		print_string (brief ? "S: " : "Score: ");
-		print_num (global1);
-
-		pad_status_line (brief ? 8 : 14);
-
-		print_string (brief ? "M: " : "Moves: ");
-		print_num (global2);
-	}
-
-	// Pad the end of the status line with spaces
-	pad_status_line (0);
-
-	// Return to the lower window
-	glk_set_window(gos_lower);
-	gos_curwin = gos_lower;
-}
-
-void Processor::z_split_window() {
-	split_window(zargs[0]);
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_streams.cpp b/engines/gargoyle/frotz/processor_streams.cpp
deleted file mode 100644
index 50d6425..0000000
--- a/engines/gargoyle/frotz/processor_streams.cpp
+++ /dev/null
@@ -1,786 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-#include "gargoyle/frotz/quetzal.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-zchar Processor::console_read_input(int max, zchar *buf, zword timeout, bool continued) {
-	return os_read_line(max, buf, timeout, max, continued);
-}
-
-zchar Processor::console_read_key(zword timeout) {
-	return os_read_key(timeout, 0);
-}
-
-void Processor::scrollback_char(zchar c) {
-    if (c == ZC_INDENT)
-        { scrollback_char (' '); scrollback_char (' '); scrollback_char (' '); return; }
-    if (c == ZC_GAP)
-        { scrollback_char (' '); scrollback_char (' '); return; }
-
-    os_scrollback_char(c);
-}
-
-void Processor::scrollback_word(const zchar *s) {
-    int i;
-
-	for (i = 0; s[i] != 0; i++) {
-		if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
-			i++;
-		else
-			scrollback_char(s[i]);
-	}
-}
-
-void Processor::scrollback_write_input(const zchar *buf, zchar key) {
-    int i;
-
-    for (i = 0; buf[i] != 0; i++)
-        scrollback_char (buf[i]);
-
-    if (key == ZC_RETURN)
-        scrollback_char ('\n');
-}
-
-void Processor::scrollback_erase_input(const zchar *buf) {
-    int width;
-    int i;
-
-    for (i = 0, width = 0; buf[i] != 0; i++)
-        width++;
-
-    os_scrollback_erase(width);
-
-}
-
-void Processor::stream_mssg_on() {
-    flush_buffer();
-
-    if (ostream_screen)
-	    screen_mssg_on();
-    if (ostream_script && enable_scripting)
-	script_mssg_on();
-
-    message = true;
-}
-
-void Processor::stream_mssg_off() {
-    flush_buffer();
-
-    if (ostream_screen)
-		screen_mssg_off();
-    if (ostream_script && enable_scripting)
-		script_mssg_off();
-
-    message = false;
-}
-
-void Processor::stream_char(zchar c) {
-    if (ostream_screen)
-		screen_char(c);
-    if (ostream_script && enable_scripting)
-		script_char(c);
-    if (enable_scripting)
-		scrollback_char(c);
-}
-
-void Processor::stream_word(const zchar *s) {
-    if (ostream_memory && !message)
-		memory_word(s);
-    else {
-		if (ostream_screen)
-			screen_word(s);
-		if (ostream_script && enable_scripting)
-			script_word(s);
-		if (enable_scripting)
-			scrollback_word(s);
-    }
-}
-
-void Processor::stream_new_line() {
-    if (ostream_memory && !message)
-		memory_new_line();
-    else {
-		if (ostream_screen)
-			screen_new_line();
-		if (ostream_script && enable_scripting)
-			script_new_line();
-		if (enable_scripting)
-			os_scrollback_char ('\n');
-    }
-}
-
-zchar Processor::stream_read_key(zword timeout, zword routine, bool hot_keys) {
-    zchar key = ZC_BAD;
-
-    flush_buffer();
-
-    // Read key from current input stream
-continue_input:
-
-    do {
-		if (istream_replay)
-			key = replay_read_key();
-		else
-			key = console_read_key(timeout);
-    } while (key == ZC_BAD);
-
-    // Copy key to the command file
-    if (ostream_record && !istream_replay)
-		record_write_key(key);
-
-    // Handle timeouts
-    if (key == ZC_TIME_OUT)
-		if (direct_call (routine) == 0)
-			goto continue_input;
-
-    // Return key
-    return key;
-}
-
-zchar Processor::stream_read_input(int max, zchar *buf, zword timeout, zword routine,
-			  bool hot_keys, bool no_scripting) {
-    zchar key = ZC_BAD;
-    bool no_scrollback = no_scripting;
-
-    if (h_version == V6 && _storyId == UNKNOWN && !ostream_script)
-		no_scrollback = false;
-
-    flush_buffer();
-
-    // Remove initial input from the transscript file or from the screen
-    if (ostream_script && enable_scripting && !no_scripting)
-	script_erase_input(buf);
-
-    // Read input line from current input stream
-continue_input:
-
-    do {
-		if (istream_replay)
-			key = replay_read_input(buf);
-		else
-			key = console_read_input(max, buf, timeout, key != ZC_BAD);
-    } while (key == ZC_BAD);
-
-    // Copy input line to the command file
-    if (ostream_record && !istream_replay)
-		record_write_input(buf, key);
-
-    // Handle timeouts
-    if (key == ZC_TIME_OUT)
-	if (direct_call(routine) == 0)
-	    goto continue_input;
-
-    // Copy input line to transscript file or to the screen
-    if (ostream_script && enable_scripting && !no_scripting)
-		script_write_input(buf, key);
-
-    // Return terminating key
-    return key;
-}
-
-void Processor::script_open() {
-	h_flags &= ~SCRIPTING_FLAG;
-
-	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript,
-		filemode_WriteAppend);
-	sfp = glk_stream_open_file(fref, filemode_WriteAppend);
-
-	if (sfp != nullptr) {
-		sfp->setPosition(0, seekmode_End);
-
-		h_flags |= SCRIPTING_FLAG;
-
-		script_valid = true;
-		ostream_script = true;
-
-		script_width = 0;
-	} else {
-		print_string("Cannot open file\n");
-	}
-
-	SET_WORD(H_FLAGS, h_flags);
-}
-
-void Processor::script_close() {
-	h_flags &= ~SCRIPTING_FLAG;
-	SET_WORD(H_FLAGS, h_flags);
-
-	glk_stream_close(sfp);
-	ostream_script = false;
-}
-
-void Processor::script_new_line() {
-	script_char('\n');
-	script_width = 0;
-}
-
-void Processor::script_char(zchar c) {
-	if (c == ZC_INDENT && script_width != 0)
-		c = ' ';
-
-	if (c == ZC_INDENT) {
-		script_char(' ');
-		script_char(' ');
-		script_char(' ');
-		return;
-	}
-	if (c == ZC_GAP) {
-		script_char(' ');
-		script_char(' ');
-		return;
-	}
-
-	sfp->putCharUni(c);
-	script_width++;
-}
-
-void Processor::script_word(const zchar *s) {
-	int width;
-	int i;
-
-	if (*s == ZC_INDENT && script_width != 0)
-		script_char(*s++);
-
-	for (i = 0, width = 0; s[i] != 0; i++) {
-		if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT)
-			i++;
-		else if (s[i] == ZC_GAP)
-			width += 3;
-		else if (s[i] == ZC_INDENT)
-			width += 2;
-		else
-			width += 1;
-	}
-
-	if (_script_cols != 0 && script_width + width > _script_cols) {
-		if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
-			s++;
-
-		script_new_line();
-	}
-
-	for (i = 0; s[i] != 0; i++) {
-		if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
-			i++;
-		else
-			script_char(s[i]);
-	}
-}
-
-void Processor::script_write_input(const zchar *buf, zchar key) {
-	int width;
-	int i;
-
-	for (i = 0, width = 0; buf[i] != 0; i++)
-		width++;
-
-	if (_script_cols != 0 && script_width + width > _script_cols)
-		script_new_line();
-
-	for (i = 0; buf[i] != 0; i++)
-		script_char(buf[i]);
-
-	if (key == ZC_RETURN)
-		script_new_line();
-}
-
-void Processor::script_erase_input(const zchar *buf) {
-	int width;
-	int i;
-
-	for (i = 0, width = 0; buf[i] != 0; i++)
-		width++;
-
-	sfp->setPosition(-width, seekmode_Current);
-	script_width -= width;
-}
-
-void Processor::script_mssg_on() {
-	if (script_width != 0)
-		script_new_line();
-
-	script_char(ZC_INDENT);
-}
-
-void Processor::script_mssg_off() {
-	script_new_line();
-}
-
-void Processor::record_open() {
-	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Write);
-	if ((rfp = glk_stream_open_file(fref, filemode_Write)) != nullptr)
-		ostream_record = true;
-	else
-		print_string("Cannot open file\n");
-}
-
-void Processor::record_close() {
-	glk_stream_close(rfp);
-	ostream_record = false;
-}
-
-void Processor::record_code(int c, bool force_encoding) {
-	if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) {
-		int i;
-
-		rfp->putChar('[');
-
-		for (i = 10000; i != 0; i /= 10)
-			if (c >= i || i == 1)
-				rfp->putChar('0' + (c / i) % 10);
-
-		rfp->putChar(']');
-	} else {
-		rfp->putChar(c);
-	}
-}
-
-void Processor::record_char(zchar c) {
-	if (c != ZC_RETURN) {
-		if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) {
-			record_code(translate_to_zscii(c), false);
-			if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
-				record_code(mouse_x, true);
-				record_code(mouse_y, true);
-			}
-		} else {
-			record_code(1000 + c - ZC_HKEY_MIN, true);
-		}
-	}
-}
-
-void Processor::record_write_key(zchar key) {
-	record_char(key);
-	rfp->putChar('\n');
-}
-
-void Processor::record_write_input(const zchar *buf, zchar key) {
-	zchar c;
-
-	while ((c = *buf++) != 0)
-		record_char(c);
-
-	record_write_key(key);
-}
-
-void Processor::replay_open() {
-	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Read);
-	if ((pfp = glk_stream_open_file(fref, filemode_Read)) != nullptr)
-		istream_replay = true;
-	else
-		print_string("Cannot open file\n");
-}
-
-void Processor::replay_close() {
-	glk_stream_close(pfp);
-	istream_replay = false;
-}
-
-int Processor::replay_code() {
-	int c;
-
-	if ((c = pfp->getChar()) == '[') {
-		int c2;
-
-		c = 0;
-
-		while ((c2 = pfp->getChar()) != EOF && c2 >= '0' && c2 <= '9')
-			c = 10 * c + c2 - '0';
-
-		return (c2 == ']') ? c : EOF;
-	} else {
-		return c;
-	}
-}
-
-zchar Processor::replay_char() {
-	int c;
-
-	if ((c = replay_code()) != EOF) {
-		if (c != '\n') {
-			if (c < 1000) {
-
-				c = translate_from_zscii(c);
-
-				if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
-					mouse_x = replay_code();
-					mouse_y = replay_code();
-				}
-
-				return c;
-			} else {
-				return ZC_HKEY_MIN + c - 1000;
-			}
-		}
-
-		pfp->unputBuffer("\n", 1);
-		return ZC_RETURN;
-
-	} else {
-		return ZC_BAD;
-	}
-}
-
-zchar Processor::replay_read_key() {
-	zchar key = replay_char();
-
-	if (pfp->getChar() != '\n') {
-		replay_close();
-		return ZC_BAD;
-	} else {
-		return key;
-	}
-}
-
-zchar Processor::replay_read_input(zchar *buf) {
-	zchar c;
-
-	for (;;) {
-		c = replay_char();
-
-		if (c == ZC_BAD || is_terminator(c))
-			break;
-
-		*buf++ = c;
-	}
-
-	*buf = 0;
-
-	if (pfp->getChar() != '\n') {
-		replay_close();
-		return ZC_BAD;
-	} else {
-		return c;
-	}
-}
-
-
-void Processor::z_input_stream() {
-	flush_buffer();
-
-	if (zargs[0] == 0 && istream_replay)
-		replay_close();
-	if (zargs[0] == 1 && !istream_replay)
-		replay_open();
-}
-
-void Processor::z_output_stream() {
-    flush_buffer();
-
-    switch ((short) zargs[0]) {
-    case  1: ostream_screen = true;
-	     break;
-    case -1: ostream_screen = false;
-	     break;
-    case  2: if (!ostream_script) script_open();
-	     break;
-    case -2: if (ostream_script) script_close();
-	     break;
-    case  3: memory_open(zargs[1], zargs[2], zargc >= 3);
-	     break;
-    case -3: memory_close();
-	     break;
-    case  4: if (!ostream_record) record_open();
-	     break;
-    case -4: if (ostream_record) record_close();
-	     break;
-	default:
-		break;
-    }
-}
-
-void Processor::z_restart() {
-	flush_buffer();
-
-	os_restart_game(RESTART_BEGIN);
-
-	seed_random(0);
-
-	if (!first_restart) {
-		story_fp->seek(blorb_ofs);
-
-		if (story_fp->read(zmp, h_dynamic_size) != h_dynamic_size)
-			error("Story file read error");
-
-	} else {
-		first_restart = false;
-	}
-
-	restart_header();
-	restart_screen();
-
-	_sp = _fp = _stack + STACK_SIZE;
-	_frameCount = 0;
-
-	if (h_version != V6 && h_version != V9) {
-		long pc = (long)h_start_pc;
-		SET_PC(pc);
-	} else {
-		call(h_start_pc, 0, nullptr, 0);
-	}
-
-	os_restart_game(RESTART_END);
-}
-
-void Processor::z_save() {
-#ifdef TODO
-	bool success = false;
-
-	if (zargc != 0) {
-		// Open auxilary file
-		frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
-			filemode_Write, 0);
-		if (ref == nullptr)
-			goto finished;
-
-		// Write data
-		strid_t f = glk_stream_open_file(ref, filemode_Write);
-
-		glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]);
-
-		glk_stream_close(f);
-
-	} else {
-		long pc;
-		zword addr;
-		zword nsp, nfp;
-		int skip;
-		int i;
-
-		/* Open game file */
-
-		if ((gfp = frotzopenprompt (FILE_SAVE)) == nullptr)
-			goto finished;
-
-		if (_save_quetzal) {
-			success = save_quetzal (gfp, story_fp, blorb_ofs);
-		} else {
-			/* Write game file */
-
-			fputc ((int) hi (h_release), gfp);
-			fputc ((int) lo (h_release), gfp);
-			fputc ((int) hi (h_checksum), gfp);
-			fputc ((int) lo (h_checksum), gfp);
-
-			GET_PC (pc)
-
-				fputc ((int) (pc >> 16) & 0xff, gfp);
-			fputc ((int) (pc >> 8) & 0xff, gfp);
-			fputc ((int) (pc) & 0xff, gfp);
-
-			nsp = (int) (_sp - _stack);
-			nfp = (int) (_fp - _stack);
-
-			fputc ((int) hi (nsp), gfp);
-			fputc ((int) lo (nsp), gfp);
-			fputc ((int) hi (nfp), gfp);
-			fputc ((int) lo (nfp), gfp);
-
-			for (i = nsp; i < STACK_SIZE; i++) {
-				fputc ((int) hi (_stack[i]), gfp);
-				fputc ((int) lo (_stack[i]), gfp);
-			}
-
-			fseek (story_fp, blorb_ofs, SEEK_SET);
-
-			for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
-				if (zmp[addr] != fgetc (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
-					fputc (skip, gfp);
-					fputc (zmp[addr], gfp);
-					skip = 0;
-				} else skip++;
-		}
-
-		/* Close game file and check for errors */
-
-		if (fclose (gfp) == EOF || ferror (story_fp)) {
-			print_string ("Error writing save file\n");
-			goto finished;
-		}
-
-		/* Success */
-
-		success = 1;
-
-	}
-
-finished:
-
-	if (h_version <= V3)
-		branch (success);
-	else
-		store (success);
-#endif
-}
-
-void Processor::z_restore() {
-#ifdef TODO
-	FILE *gfp;
-
-	zword success = 0;
-
-	if (zargc != 0) {
-
-		/* Get the file name */
-
-		/* Open auxilary file */
-
-		if ((gfp = frotzopenprompt(FILE_LOAD_AUX)) == nullptr)
-			goto finished;
-
-		/* Load auxilary file */
-
-		success = fread (zmp + zargs[0], 1, zargs[1], gfp);
-
-		/* Close auxilary file */
-
-		fclose (gfp);
-
-	} else {
-
-		long pc;
-		zword release;
-		zword addr;
-		int i;
-
-		/* Open game file */
-
-		if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
-			goto finished;
-
-		if (_save_quetzal) {
-			success = restore_quetzal (gfp, story_fp, blorb_ofs);
-
-		} else {
-			/* Load game file */
-
-			release = (unsigned) fgetc (gfp) << 8;
-			release |= fgetc (gfp);
-
-			() fgetc (gfp);
-			() fgetc (gfp);
-
-			/* Check the release number */
-
-			if (release == h_release) {
-
-				pc = (long) fgetc (gfp) << 16;
-				pc |= (unsigned) fgetc (gfp) << 8;
-				pc |= fgetc (gfp);
-
-				SET_PC (pc);
-
-				_sp = _stack + (fgetc (gfp) << 8);
-				_sp += fgetc (gfp);
-				_fp = _stack + (fgetc (gfp) << 8);
-				_fp += fgetc (gfp);
-
-				for (i = (int) (_sp - _stack); i < STACK_SIZE; i++) {
-					_stack[i] = (unsigned) fgetc (gfp) << 8;
-					_stack[i] |= fgetc (gfp);
-				}
-
-				fseek (story_fp, blorb_ofs, SEEK_SET);
-
-				for (addr = 0; addr < h_dynamic_size; addr++) {
-					int skip = fgetc (gfp);
-					for (i = 0; i < skip; i++)
-						zmp[addr++] = fgetc (story_fp);
-					zmp[addr] = fgetc (gfp);
-					() fgetc (story_fp);
-				}
-
-				/* Check for errors */
-
-				if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
-					success = -1;
-				else
-
-					/* Success */
-
-					success = 2;
-
-			} else print_string ("Invalid save file\n");
-		}
-
-		if ((short) success >= 0) {
-
-			/* Close game file */
-
-			fclose (gfp);
-
-			if ((short) success > 0) {
-				zbyte old_screen_rows;
-				zbyte old_screen_cols;
-
-				/* In V3, reset the upper window. */
-				if (h_version == V3)
-					split_window (0);
-
-				LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
-				LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
-
-				/* Reload cached header fields. */
-				restart_header ();
-
-				/*
-				 * Since QUETZAL files may be saved on many different machines,
-				 * the screen sizes may vary a lot. Erasing the status window
-				 * seems to cover up most of the resulting badness.
-				 */
-				if (h_version > V3 && h_version != V6
-						&& (h_screen_rows != old_screen_rows
-							|| h_screen_cols != old_screen_cols))
-					erase_window (1);
-			}
-		} else
-			os_fatal ("Error reading save file");
-	}
-
-finished:
-
-	if (h_version <= V3)
-		branch (success);
-	else
-		store (success);
-#endif
-}
-
-void Processor::z_verify() {
-	zword checksum = 0;
-
-	// Sum all bytes in story file except header bytes
-	story_fp->seek(blorb_ofs + 64);
-
-	for (uint i = 64; i < story_size; i++)
-		checksum += story_fp->readByte();
-
-	// Branch if the checksums are equal
-	branch(checksum == h_checksum);
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_table.cpp b/engines/gargoyle/frotz/processor_table.cpp
deleted file mode 100644
index d449062..0000000
--- a/engines/gargoyle/frotz/processor_table.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-void Processor::z_copy_table() {
-    zword addr;
-    zword size = zargs[2];
-    zbyte value;
-    int i;
-
-    if (zargs[1] == 0)      				/* zero table */
-
-	for (i = 0; i < size; i++)
-	    storeb((zword) (zargs[0] + i), 0);
-
-    else if ((short) size < 0 || zargs[0] > zargs[1])	/* copy forwards */
-
-	for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) {
-	    addr = zargs[0] + i;
-		LOW_BYTE(addr, value);
-	    storeb((zword) (zargs[1] + i), value);
-	} else {
-		// copy backwards
-		for (i = size - 1; i >= 0; i--) {
-			addr = zargs[0] + i;
-			LOW_BYTE(addr, value);
-			storeb((zword) (zargs[1] + i), value);
-		}
-	}
-}
-
-void Processor::z_loadb() {
-    zword addr = zargs[0] + zargs[1];
-    zbyte value;
-
-	LOW_BYTE(addr, value);
-
-    store(value);
-}
-
-void Processor::z_loadw() {
-    zword addr = zargs[0] + 2 * zargs[1];
-    zword value;
-
-	LOW_WORD(addr, value);
-
-    store(value);
-}
-
-void Processor::z_scan_table() {
-    zword addr = zargs[1];
-    int i;
-
-    // Supply default arguments
-    if (zargc < 4)
-	zargs[3] = 0x82;
-
-    // Scan byte or word array
-    for (i = 0; i < zargs[2]; i++) {
-		if (zargs[3] & 0x80) {
-			// scan word array
-			zword wvalue;
-
-			LOW_WORD(addr, wvalue);
-
-			if (wvalue == zargs[0])
-				goto finished;
-		} else {
-			// scan byte array
-			zbyte bvalue;
-
-			LOW_BYTE(addr, bvalue);
-
-			if (bvalue == zargs[0])
-				goto finished;
-		}
-
-		addr += zargs[3] & 0x7f;
-    }
-
-    addr = 0;
-
-finished:
-    store(addr);
-    branch(addr);
-}
-
-void Processor::z_storeb() {
-    storeb((zword) (zargs[0] + zargs[1]), zargs[2]);
-}
-
-void Processor::z_storew() {
-    storew((zword)(zargs[0] + 2 * zargs[1]), zargs[2]);
-}
-
-} // End of namespace Scott
-} // End of namespace Gargoyle
diff --git a/engines/gargoyle/frotz/processor_text.cpp b/engines/gargoyle/frotz/processor_text.cpp
deleted file mode 100644
index 0852dc4..0000000
--- a/engines/gargoyle/frotz/processor_text.cpp
+++ /dev/null
@@ -1,886 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "gargoyle/frotz/processor.h"
-
-namespace Gargoyle {
-namespace Frotz {
-
-zchar Processor::ZSCII_TO_LATIN1[] = {
-    0x0e4, 0x0f6, 0x0fc, 0x0c4, 0x0d6, 0x0dc, 0x0df, 0x0bb,
-    0x0ab, 0x0eb, 0x0ef, 0x0ff, 0x0cb, 0x0cf, 0x0e1, 0x0e9,
-    0x0ed, 0x0f3, 0x0fa, 0x0fd, 0x0c1, 0x0c9, 0x0cd, 0x0d3,
-    0x0da, 0x0dd, 0x0e0, 0x0e8, 0x0ec, 0x0f2, 0x0f9, 0x0c0,
-    0x0c8, 0x0cc, 0x0d2, 0x0d9, 0x0e2, 0x0ea, 0x0ee, 0x0f4,
-    0x0fb, 0x0c2, 0x0ca, 0x0ce, 0x0d4, 0x0db, 0x0e5, 0x0c5,
-    0x0f8, 0x0d8, 0x0e3, 0x0f1, 0x0f5, 0x0c3, 0x0d1, 0x0d5,
-    0x0e6, 0x0c6, 0x0e7, 0x0c7, 0x0fe, 0x0f0, 0x0de, 0x0d0,
-    0x0a3, 0x153, 0x152, 0x0a1, 0x0bf
-};
-
-zchar Processor::translate_from_zscii(zbyte c) {
-    if (c == 0xfc)
-		return ZC_MENU_CLICK;
-    if (c == 0xfd)
-		return ZC_DOUBLE_CLICK;
-    if (c == 0xfe)
-		return ZC_SINGLE_CLICK;
-
-    if (c >= 0x9b && _storyId != BEYOND_ZORK) {
-		if (hx_unicode_table != 0) {
-			// game has its own Unicode table
-			zbyte N;
-			LOW_BYTE(hx_unicode_table, N);
-
-			if (c - 0x9b < N) {
-				zword addr = hx_unicode_table + 1 + 2 * (c - 0x9b);
-				zword unicode;
-
-				LOW_WORD(addr, unicode);
-
-				if (unicode < 0x20)
-					return '?';
-
-				return unicode;
-			} else {
-				return '?';
-			}
-		} else {
-			// game uses standard set
-			if (c <= 0xdf) {
-				return ZSCII_TO_LATIN1[c - 0x9b];
-			} else {
-				return '?';
-			}
-		}
-    }
-
-    return (zchar)c;
-}
-
-zbyte Processor::unicode_to_zscii(zchar c) {
-    int i;
-
-    if (c >= ZC_LATIN1_MIN) {
-		if (hx_unicode_table != 0) {
-			// game has its own Unicode table
-			zbyte N;
-			LOW_BYTE(hx_unicode_table, N);
-
-			for (i = 0x9b; i < 0x9b + N; i++) {
-				zword addr = hx_unicode_table + 1 + 2 * (i - 0x9b);
-				zword unicode;
-
-				LOW_WORD(addr, unicode);
-
-				if (c == unicode)
-					return (zbyte) i;


Commit: a2104deb4cefdcf0cd6bb3a66ef09bb46cba3f66
    https://github.com/scummvm/scummvm/commit/a2104deb4cefdcf0cd6bb3a66ef09bb46cba3f66
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Changing class and namespaces from Gargoyle to Glk

Changed paths:
  A engines/glk/glk_api.cpp
  A engines/glk/glk_api.h
  R engines/glk/gargoyle.cpp
  R engines/glk/gargoyle.h
    engines/glk/blorb.cpp
    engines/glk/blorb.h
    engines/glk/conf.cpp
    engines/glk/conf.h
    engines/glk/detection.cpp
    engines/glk/detection_tables.h
    engines/glk/events.cpp
    engines/glk/events.h
    engines/glk/fonts.cpp
    engines/glk/fonts.h
    engines/glk/frotz/detection.cpp
    engines/glk/frotz/detection.h
    engines/glk/frotz/detection_tables.cpp
    engines/glk/frotz/detection_tables.h
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/frotz.h
    engines/glk/frotz/frotz_types.h
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/glk_interface.h
    engines/glk/frotz/mem.cpp
    engines/glk/frotz/mem.h
    engines/glk/frotz/processor.cpp
    engines/glk/frotz/processor.h
    engines/glk/frotz/processor_buffer.cpp
    engines/glk/frotz/processor_input.cpp
    engines/glk/frotz/processor_maths.cpp
    engines/glk/frotz/processor_mem.cpp
    engines/glk/frotz/processor_objects.cpp
    engines/glk/frotz/processor_screen.cpp
    engines/glk/frotz/processor_streams.cpp
    engines/glk/frotz/processor_table.cpp
    engines/glk/frotz/processor_text.cpp
    engines/glk/frotz/processor_variables.cpp
    engines/glk/frotz/quetzal.cpp
    engines/glk/frotz/quetzal.h
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/glk_types.h
    engines/glk/module.mk
    engines/glk/picture.cpp
    engines/glk/picture.h
    engines/glk/scott/detection.cpp
    engines/glk/scott/detection.h
    engines/glk/scott/scott.cpp
    engines/glk/scott/scott.h
    engines/glk/screen.cpp
    engines/glk/screen.h
    engines/glk/selection.cpp
    engines/glk/selection.h
    engines/glk/speech.h
    engines/glk/streams.cpp
    engines/glk/streams.h
    engines/glk/time.cpp
    engines/glk/time.h
    engines/glk/unicode.cpp
    engines/glk/unicode.h
    engines/glk/unicode_gen.cpp
    engines/glk/unicode_gen.h
    engines/glk/utils.cpp
    engines/glk/utils.h
    engines/glk/window_graphics.cpp
    engines/glk/window_graphics.h
    engines/glk/window_pair.cpp
    engines/glk/window_pair.h
    engines/glk/window_text_buffer.cpp
    engines/glk/window_text_buffer.h
    engines/glk/window_text_grid.cpp
    engines/glk/window_text_grid.h
    engines/glk/windows.cpp
    engines/glk/windows.h


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index 24340fa..e298e1c 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/blorb.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 #define giblorb_Inited_Magic 0xB7012BEDU
 
@@ -540,4 +540,4 @@ bool Blorb::giblorb_is_resource_map(void) const {
 	return _map != nullptr;
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/blorb.h b/engines/glk/blorb.h
index 75f811c..352d01a 100644
--- a/engines/glk/blorb.h
+++ b/engines/glk/blorb.h
@@ -26,7 +26,7 @@
 #include "glk/glk_types.h"
 #include "glk/streams.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 
 /**
@@ -141,6 +141,6 @@ public:
 		glui32 usage, glui32 *num, glui32 *min, glui32 *max);
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/conf.cpp b/engines/glk/conf.cpp
index 45d6168..ae99e6b 100644
--- a/engines/glk/conf.cpp
+++ b/engines/glk/conf.cpp
@@ -27,7 +27,7 @@
 #include "common/config-manager.h"
 #include "common/system.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 const byte WHITE[3] = { 0xff, 0xff, 0xff };
 const byte BLUE[3] = { 0x00, 0x00, 0x60 };
@@ -251,4 +251,4 @@ void Conf::parseColor(const Common::String &str, byte *color) {
 	}
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/conf.h b/engines/glk/conf.h
index 6c9dbf9..fb68427 100644
--- a/engines/glk/conf.h
+++ b/engines/glk/conf.h
@@ -27,7 +27,7 @@
 #include "glk/fonts.h"
 #include "glk/windows.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 class Conf {
 private:
@@ -131,6 +131,6 @@ public:
 
 extern Conf *g_conf;
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 9266825..893ed87 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -20,7 +20,7 @@
  *
  */
 
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 
 #include "base/plugins.h"
 #include "common/md5.h"
@@ -34,44 +34,44 @@
 
 #define MAX_SAVES 99
 
-namespace Gargoyle {
+namespace Glk {
 
-struct GargoyleGameDescription {
+struct GlkGameDescription {
 	ADGameDescription _desc;
 	Common::String _filename;
 	InterpreterType _interpType;
 	Common::String _md5;
 };
 
-const Common::String &GargoyleEngine::getFilename() const {
+const Common::String &GlkEngine::getFilename() const {
 	return _gameDescription->_filename;
 }
-uint32 GargoyleEngine::getFeatures() const {
+uint32 GlkEngine::getFeatures() const {
 	return _gameDescription->_desc.flags;
 }
 
-bool GargoyleEngine::isDemo() const {
+bool GlkEngine::isDemo() const {
 	return (bool)(_gameDescription->_desc.flags & ADGF_DEMO);
 }
 
-Common::Language GargoyleEngine::getLanguage() const {
+Common::Language GlkEngine::getLanguage() const {
 	return _gameDescription->_desc.language;
 }
 
-InterpreterType GargoyleEngine::getInterpreterType() const {
+InterpreterType GlkEngine::getInterpreterType() const {
 	return _gameDescription->_interpType;
 }
 
-const Common::String &GargoyleEngine::getGameMD5() const {
+const Common::String &GlkEngine::getGameMD5() const {
 	return _gameDescription->_md5;
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #include "glk/frotz/detection_tables.h"
-#define ZCODE(ID, NAME) { ID, Gargoyle::Frotz::NAME##_DESC }
+#define ZCODE(ID, NAME) { ID, Glk::Frotz::NAME##_DESC }
 
-static const PlainGameDescriptor gargoyleGames[] = {
+static const PlainGameDescriptor glkGames[] = {
 	{"zcode", "Zcode Games" },
 	{"scottadams", "Scott Adams Games"},
 
@@ -146,9 +146,9 @@ static const PlainGameDescriptor gargoyleGames[] = {
 #include "glk/scott/detection.h"
 #include "glk/scott/scott.h"
 
-class GargoyleMetaEngine : public AdvancedMetaEngine {
+class GlkMetaEngine : public AdvancedMetaEngine {
 public:
-	GargoyleMetaEngine() : AdvancedMetaEngine(Gargoyle::gameDescriptions, sizeof(Gargoyle::GargoyleGameDescription), gargoyleGames) {
+	GlkMetaEngine() : AdvancedMetaEngine(Glk::gameDescriptions, sizeof(Glk::GlkGameDescription), glkGames) {
 		_maxScanDepth = 3;
 	}
 
@@ -172,7 +172,7 @@ public:
 	virtual ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const override;
 };
 
-bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
+bool GlkMetaEngine::hasFeature(MetaEngineFeature f) const {
 	return
 	    (f == kSupportsListSaves) ||
 	    (f == kSupportsLoadingDuringStartup) ||
@@ -183,22 +183,22 @@ bool GargoyleMetaEngine::hasFeature(MetaEngineFeature f) const {
 	    (f == kSimpleSavesNames);
 }
 
-bool Gargoyle::GargoyleEngine::hasFeature(EngineFeature f) const {
+bool Glk::GlkEngine::hasFeature(EngineFeature f) const {
 	return
 	    (f == kSupportsRTL) ||
 	    (f == kSupportsLoadingDuringRuntime) ||
 	    (f == kSupportsSavingDuringRuntime);
 }
 
-bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
-	const Gargoyle::GargoyleGameDescription *gd = (const Gargoyle::GargoyleGameDescription *)desc;
+bool GlkMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+	const Glk::GlkGameDescription *gd = (const Glk::GlkGameDescription *)desc;
 
 	switch (gd->_interpType) {
-	case Gargoyle::INTERPRETER_FROTZ:
-		*engine = new Gargoyle::Frotz::Frotz(syst, gd);
+	case Glk::INTERPRETER_FROTZ:
+		*engine = new Glk::Frotz::Frotz(syst, gd);
 		break;
-	case Gargoyle::INTERPRETER_SCOTT:
-		*engine = new Gargoyle::Scott::Scott(syst, gd);
+	case Glk::INTERPRETER_SCOTT:
+		*engine = new Glk::Scott::Scott(syst, gd);
 		break;
 	default:
 		error("Unknown interpreter");
@@ -207,12 +207,12 @@ bool GargoyleMetaEngine::createInstance(OSystem *syst, Engine **engine, const AD
 	return gd != 0;
 }
 
-SaveStateList GargoyleMetaEngine::listSaves(const char *target) const {
+SaveStateList GlkMetaEngine::listSaves(const char *target) const {
 	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
 	Common::StringArray filenames;
 	Common::String saveDesc;
 	Common::String pattern = Common::String::format("%s.0##", target);
-	Gargoyle::SavegameHeader header;
+	Glk::SavegameHeader header;
 
 	filenames = saveFileMan->listSavefiles(pattern);
 
@@ -225,7 +225,7 @@ SaveStateList GargoyleMetaEngine::listSaves(const char *target) const {
 			Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file);
 
 			if (in) {
-				if (Gargoyle::FileStream::readSavegameHeader(in, header))
+				if (Glk::FileStream::readSavegameHeader(in, header))
 					saveList.push_back(SaveStateDescriptor(slot, header._saveName));
 
 				delete in;
@@ -238,22 +238,22 @@ SaveStateList GargoyleMetaEngine::listSaves(const char *target) const {
 	return saveList;
 }
 
-int GargoyleMetaEngine::getMaximumSaveSlot() const {
+int GlkMetaEngine::getMaximumSaveSlot() const {
 	return MAX_SAVES;
 }
 
-void GargoyleMetaEngine::removeSaveState(const char *target, int slot) const {
+void GlkMetaEngine::removeSaveState(const char *target, int slot) const {
 	Common::String filename = Common::String::format("%s.%03d", target, slot);
 	g_system->getSavefileManager()->removeSavefile(filename);
 }
 
-SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
+SaveStateDescriptor GlkMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
 	Common::String filename = Common::String::format("%s.%03d", target, slot);
 	Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename);
 
 	if (in) {
-		Gargoyle::SavegameHeader header;
-		if (Gargoyle::FileStream::readSavegameHeader(in, header)) {
+		Glk::SavegameHeader header;
+		if (Glk::FileStream::readSavegameHeader(in, header)) {
 			// Create the return descriptor
 			SaveStateDescriptor desc(slot, header._saveName);
 			desc.setSaveDate(header._year, header._month, header._day);
@@ -268,17 +268,17 @@ SaveStateDescriptor GargoyleMetaEngine::querySaveMetaInfos(const char *target, i
 	return SaveStateDescriptor();
 }
 
-DetectedGames GargoyleMetaEngine::detectGames(const Common::FSList &fslist) const {
+DetectedGames GlkMetaEngine::detectGames(const Common::FSList &fslist) const {
 	DetectedGames detectedGames;
-	Gargoyle::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
-	Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
+	Glk::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
+	Glk::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
 
 	return detectedGames;
 }
 
-static Gargoyle::GargoyleGameDescription gameDescription;
+static Glk::GlkGameDescription gameDescription;
 
-ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
+ADDetectedGames GlkMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
 	static char gameId[100];
 	strcpy(gameId, ConfMan.get("gameid").c_str());
 	Common::String filename = ConfMan.get("filename");
@@ -290,10 +290,10 @@ ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, con
 	Common::File f;
 
 	// Check each sub-engine for any detected games
-	if (Gargoyle::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames))
-		gameDescription._interpType = Gargoyle::INTERPRETER_FROTZ;
-	else if (Gargoyle::Scott::ScottMetaEngine::detectGames(fslist, detectedGames))
-		gameDescription._interpType = Gargoyle::INTERPRETER_SCOTT;
+	if (Glk::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames))
+		gameDescription._interpType = Glk::INTERPRETER_FROTZ;
+	else if (Glk::Scott::ScottMetaEngine::detectGames(fslist, detectedGames))
+		gameDescription._interpType = Glk::INTERPRETER_SCOTT;
 	else
 		// No match found, so return no results
 		return results;
@@ -317,7 +317,7 @@ ADDetectedGames GargoyleMetaEngine::detectGame(const Common::FSNode &parent, con
 }
 
 #if PLUGIN_ENABLED_DYNAMIC(GLK)
-REGISTER_PLUGIN_DYNAMIC(GLK, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
+REGISTER_PLUGIN_DYNAMIC(GLK, PLUGIN_TYPE_ENGINE, GlkMetaEngine);
 #else
-REGISTER_PLUGIN_STATIC(GLK, PLUGIN_TYPE_ENGINE, GargoyleMetaEngine);
+REGISTER_PLUGIN_STATIC(GLK, PLUGIN_TYPE_ENGINE, GlkMetaEngine);
 #endif
diff --git a/engines/glk/detection_tables.h b/engines/glk/detection_tables.h
index 30491f6..47a761e 100644
--- a/engines/glk/detection_tables.h
+++ b/engines/glk/detection_tables.h
@@ -20,10 +20,10 @@
  *
  */
 
-namespace Gargoyle {
+namespace Glk {
 
-static const GargoyleGameDescription gameDescriptions[] = {
+static const GlkGameDescription gameDescriptions[] = {
 	{ AD_TABLE_END_MARKER, "", (InterpreterType)0, "" }
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/events.cpp b/engines/glk/events.cpp
index 74a108e..8f3d5ef 100644
--- a/engines/glk/events.cpp
+++ b/engines/glk/events.cpp
@@ -22,13 +22,13 @@
 
 #include "glk/events.h"
 #include "glk/conf.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "glk/screen.h"
 #include "glk/selection.h"
 #include "glk/windows.h"
 #include "graphics/cursorman.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 const byte ARROW[] = {
 	// byte 1: number of skipped pixels
@@ -399,4 +399,4 @@ bool Events::isTimerExpired() const {
 	return _timerMilli && g_system->getMillis() >= _timerTimeExpiry;
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/events.h b/engines/glk/events.h
index 537813f..f68fca9 100644
--- a/engines/glk/events.h
+++ b/engines/glk/events.h
@@ -27,7 +27,7 @@
 #include "graphics/surface.h"
 #include "glk/utils.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 #define GAME_FRAME_RATE 100
 #define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE)
@@ -286,6 +286,6 @@ public:
 	bool isTimerExpired() const;
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/fonts.cpp b/engines/glk/fonts.cpp
index ee958b0..bc699af 100644
--- a/engines/glk/fonts.cpp
+++ b/engines/glk/fonts.cpp
@@ -23,13 +23,13 @@
 #include "glk/fonts.h"
 #include "glk/glk_types.h"
 #include "glk/conf.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "common/memstream.h"
 #include "common/unzip.h"
 #include "graphics/fonts/ttf.h"
 #include "graphics/fontman.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 #define FONTS_VERSION 1.0
 #define FONTS_FILENAME "fonts.dat"
@@ -154,4 +154,4 @@ size_t Fonts::stringWidthUni(int fontIdx, const Common::U32String &text, int spw
 	return font->getStringWidth(text) * GLI_SUBPIX;
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/fonts.h b/engines/glk/fonts.h
index db547aa..364b023 100644
--- a/engines/glk/fonts.h
+++ b/engines/glk/fonts.h
@@ -32,7 +32,7 @@
 #include "common/ustr.h"
 #include "graphics/font.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 #define FONTS_TOTAL 8
 
@@ -113,6 +113,6 @@ public:
 	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = 0);
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index 57965c8..36303d4 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -26,7 +26,7 @@
 
 #include "glk/frotz/detection_tables.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
@@ -77,4 +77,4 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 }
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/detection.h b/engines/glk/frotz/detection.h
index 7630cb3..9cd9373 100644
--- a/engines/glk/frotz/detection.h
+++ b/engines/glk/frotz/detection.h
@@ -26,7 +26,7 @@
 #include "common/fs.h"
 #include "engines/game.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 class FrotzMetaEngine {
@@ -38,6 +38,6 @@ public:
 };
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/frotz/detection_tables.cpp b/engines/glk/frotz/detection_tables.cpp
index 3f619a5..f52b099 100644
--- a/engines/glk/frotz/detection_tables.cpp
+++ b/engines/glk/frotz/detection_tables.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/detection_tables.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 const char *const AMFV_DESC = "A Mind Forever Voyaging";
@@ -82,4 +82,4 @@ const FrotzGameDescription FROTZ_GAMES[] = {
 
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
index 48e0eb0..51994ed 100644
--- a/engines/glk/frotz/detection_tables.h
+++ b/engines/glk/frotz/detection_tables.h
@@ -23,7 +23,7 @@
 #include "engines/advancedDetector.h"
 #include "common/language.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 /**
@@ -82,4 +82,4 @@ extern const char *const ZORK3_DESC;
 extern const char *const ZTUU_DESC;
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 0e516f0..f30acd0 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -24,12 +24,12 @@
 #include "glk/frotz/frotz_types.h"
 #include "common/config-manager.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 Frotz *g_vm;
 
-Frotz::Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc) :
+Frotz::Frotz(OSystem *syst, const GlkGameDescription *gameDesc) :
 		Processor(syst, gameDesc) {
 	g_vm = this;
 }
@@ -95,4 +95,4 @@ Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/frotz.h b/engines/glk/frotz/frotz.h
index c1fe5dc..cecd886 100644
--- a/engines/glk/frotz/frotz.h
+++ b/engines/glk/frotz/frotz.h
@@ -25,7 +25,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 /**
@@ -36,7 +36,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Frotz(OSystem *syst, const GargoyleGameDescription *gameDesc);
+	Frotz(OSystem *syst, const GlkGameDescription *gameDesc);
 
 	/**
 	 * Destructor
@@ -67,6 +67,6 @@ public:
 extern Frotz *g_vm;
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/frotz/frotz_types.h b/engines/glk/frotz/frotz_types.h
index 5aae3d0..e68f55e 100644
--- a/engines/glk/frotz/frotz_types.h
+++ b/engines/glk/frotz/frotz_types.h
@@ -26,7 +26,7 @@
 #include "glk/glk_types.h"
 #include "common/algorithm.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 #define MAX_UNDO_SLOTS 500
@@ -292,6 +292,6 @@ struct Redirect {
 };
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index b253438..da27382 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -22,11 +22,11 @@
 
 #include "glk/frotz/glk_interface.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
-GlkInterface::GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-		Glk(syst, gameDesc),
+GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription *gameDesc) :
+		GlkAPI(syst, gameDesc),
 		oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
 		curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
 		curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
@@ -495,4 +495,4 @@ zchar GlkInterface::os_read_line(int max, zchar *buf, int timeout, int width, in
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index e3fa2c9..d78d49f 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -23,10 +23,10 @@
 #ifndef GLK_FROTZ_GLK_INTERFACE
 #define GLK_FROTZ_GLK_INTERFACE
 
-#include "glk/glk.h"
+#include "glk/glk_api.h"
 #include "glk/frotz/mem.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 enum SoundEffect {
@@ -47,7 +47,7 @@ enum RestartAction {
  * Implements an intermediate interface on top of the GLK layer, providing screen
  * and sound effect handling
  */
-class GlkInterface : public Glk, public virtual UserOptions, public virtual Mem {
+class GlkInterface : public GlkAPI, public virtual UserOptions, public virtual Mem {
 public:
 	zchar statusline[256];
 	int oldstyle;
@@ -181,7 +181,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	GlkInterface(OSystem *syst, const GargoyleGameDescription *gameDesc);
+	GlkInterface(OSystem *syst, const GlkGameDescription *gameDesc);
 
 	/**
 	 * Initialization
@@ -190,6 +190,6 @@ public:
 };
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index a7747f5..378e496 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -25,7 +25,7 @@
 #include "common/memstream.h"
 #include "common/textconsole.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 const Header::StoryEntry Header::RECORDS[25] = {
@@ -416,4 +416,4 @@ void Mem::mem_undiff(zbyte *diff, long diff_length, zbyte *dest) {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/mem.h b/engines/glk/frotz/mem.h
index de22006..43b2727 100644
--- a/engines/glk/frotz/mem.h
+++ b/engines/glk/frotz/mem.h
@@ -25,7 +25,7 @@
 
 #include "glk/frotz/frotz_types.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 #define SET_WORD(addr,v)  zmp[addr] = hi(v); zmp[addr+1] = lo(v)
@@ -275,6 +275,6 @@ public:
 };
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
index 1cdc67a..a7cbad0 100644
--- a/engines/glk/frotz/processor.cpp
+++ b/engines/glk/frotz/processor.cpp
@@ -23,7 +23,7 @@
 #include "glk/frotz/processor.h"
 #include "glk/frotz/frotz.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 // TODO: Stubs to replace with actual code
@@ -131,7 +131,7 @@ Opcode Processor::ext_opcodes[64] = {
 	&Processor::z_buffer_screen,	// spec 1.1
 };
 
-Processor::Processor(OSystem *syst, const GargoyleGameDescription *gameDesc) : 
+Processor::Processor(OSystem *syst, const GlkGameDescription *gameDesc) : 
 		GlkInterface(syst, gameDesc),
 		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
@@ -661,4 +661,4 @@ void Processor::z_restore_undo(void) {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index b5c113d..4bd9d2c 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -28,7 +28,7 @@
 #include "glk/frotz/frotz_types.h"
 #include "common/stack.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 #define TEXT_BUFFER_SIZE 200
@@ -1522,7 +1522,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Processor(OSystem *syst, const GargoyleGameDescription *gameDesc);
+	Processor(OSystem *syst, const GlkGameDescription *gameDesc);
 
 	/**
 	 * Initialization
@@ -1579,6 +1579,6 @@ public:
 };
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/frotz/processor_buffer.cpp b/engines/glk/frotz/processor_buffer.cpp
index faec6ef..c4eaeaa 100644
--- a/engines/glk/frotz/processor_buffer.cpp
+++ b/engines/glk/frotz/processor_buffer.cpp
@@ -24,7 +24,7 @@
 #include "common/algorithm.h"
 #include "common/textconsole.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 const char *const Processor::ERR_MESSAGES[ERR_NUM_ERRORS] = {
@@ -189,4 +189,4 @@ void Processor::runtimeError(ErrorCode errNum) {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_input.cpp b/engines/glk/frotz/processor_input.cpp
index 6dda853..af5c7dc 100644
--- a/engines/glk/frotz/processor_input.cpp
+++ b/engines/glk/frotz/processor_input.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 #define INPUT_BUFFER_SIZE 200
@@ -220,4 +220,4 @@ void Processor::z_read_mouse(){
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_maths.cpp b/engines/glk/frotz/processor_maths.cpp
index 90e09ca..5a2a522 100644
--- a/engines/glk/frotz/processor_maths.cpp
+++ b/engines/glk/frotz/processor_maths.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 void Processor::z_add() {
@@ -102,4 +102,4 @@ void Processor::z_test() {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_mem.cpp b/engines/glk/frotz/processor_mem.cpp
index 7c7af0d..dec7520 100644
--- a/engines/glk/frotz/processor_mem.cpp
+++ b/engines/glk/frotz/processor_mem.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 void Processor::flagsChanged(zbyte value) {
@@ -215,4 +215,4 @@ void Processor::memory_close(void) {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_objects.cpp b/engines/glk/frotz/processor_objects.cpp
index 7e1fe6d..13e8a11 100644
--- a/engines/glk/frotz/processor_objects.cpp
+++ b/engines/glk/frotz/processor_objects.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 #define MAX_OBJECT 2000
@@ -729,4 +729,4 @@ void Processor::z_test_attr() {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index 87c190f..6c25ef9 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 void Processor::screen_mssg_on() {
@@ -525,4 +525,4 @@ void Processor::z_split_window() {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
index b7b23b2..8413f4b 100644
--- a/engines/glk/frotz/processor_streams.cpp
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -23,7 +23,7 @@
 #include "glk/frotz/processor.h"
 #include "glk/frotz/quetzal.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 zchar Processor::console_read_input(int max, zchar *buf, zword timeout, bool continued) {
@@ -783,4 +783,4 @@ void Processor::z_verify() {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_table.cpp b/engines/glk/frotz/processor_table.cpp
index bb16389..c586355 100644
--- a/engines/glk/frotz/processor_table.cpp
+++ b/engines/glk/frotz/processor_table.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 void Processor::z_copy_table() {
@@ -117,4 +117,4 @@ void Processor::z_storew() {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_text.cpp b/engines/glk/frotz/processor_text.cpp
index e058793..3cfe50b 100644
--- a/engines/glk/frotz/processor_text.cpp
+++ b/engines/glk/frotz/processor_text.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 zchar Processor::ZSCII_TO_LATIN1[] = {
@@ -883,4 +883,4 @@ void Processor::z_tokenise() {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/processor_variables.cpp b/engines/glk/frotz/processor_variables.cpp
index 0162f84..6e6a191 100644
--- a/engines/glk/frotz/processor_variables.cpp
+++ b/engines/glk/frotz/processor_variables.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/frotz/processor.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 void Processor::z_dec() {
@@ -196,4 +196,4 @@ void Processor::z_store() {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/quetzal.cpp b/engines/glk/frotz/quetzal.cpp
index a7b48fc..9ff33ab 100644
--- a/engines/glk/frotz/quetzal.cpp
+++ b/engines/glk/frotz/quetzal.cpp
@@ -24,7 +24,7 @@
 #include "glk/frotz/processor.h"
 #include "common/memstream.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 /**
@@ -482,4 +482,4 @@ int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/frotz/quetzal.h b/engines/glk/frotz/quetzal.h
index bbd4496..9d382ab 100644
--- a/engines/glk/frotz/quetzal.h
+++ b/engines/glk/frotz/quetzal.h
@@ -26,7 +26,7 @@
 #include "glk/glk_types.h"
 #include "glk/frotz/frotz_types.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Frotz {
 
 enum QueztalTag {
@@ -92,6 +92,6 @@ public:
 };
 
 } // End of namespace Frotz
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/gargoyle.cpp b/engines/glk/gargoyle.cpp
deleted file mode 100644
index 702efe7..0000000
--- a/engines/glk/gargoyle.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "common/scummsys.h"
-#include "common/config-manager.h"
-#include "common/debug-channels.h"
-#include "common/events.h"
-#include "common/file.h"
-#include "engines/util.h"
-#include "graphics/scaler.h"
-#include "graphics/thumbnail.h"
-#include "glk/gargoyle.h"
-#include "glk/conf.h"
-#include "glk/events.h"
-#include "glk/picture.h"
-#include "glk/screen.h"
-#include "glk/selection.h"
-#include "glk/streams.h"
-#include "glk/windows.h"
-
-namespace Gargoyle {
-
-GargoyleEngine *g_vm;
-
-GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-	_gameDescription(gameDesc), Engine(syst), _random("Gargoyle"), _clipboard(nullptr),
-	_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr),
-	_selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
-	gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
-	g_vm = this;
-}
-
-GargoyleEngine::~GargoyleEngine() {
-	delete _clipboard;
-	delete _conf;
-	delete _events;
-	delete _picList;
-	delete _screen;
-	delete _selection;
-	delete _streams;
-	delete _windows;
-}
-
-void GargoyleEngine::initialize() {
-	// Set up debug channels
-	DebugMan.addDebugChannel(kDebugCore, "core", "Core engine debug level");
-	DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
-	DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling");
-	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
-
-	initGraphicsMode();
-	_conf = new Conf();
-	_screen = new Screen();
-
-	_clipboard = new Clipboard();
-	_events = new Events();
-	_picList = new PicList();
-	_selection = new Selection();
-	_streams = new Streams();
-	_windows = new Windows(_screen);
-}
-
-void GargoyleEngine::initGraphicsMode() {
-	uint width = ConfMan.hasKey("width") ? ConfMan.getInt("width") : 640;
-	uint height = ConfMan.hasKey("height") ? ConfMan.getInt("height") : 480;
-	Common::List<Graphics::PixelFormat> formats = g_system->getSupportedFormats();
-	Graphics::PixelFormat format = formats.front();
-
-	for (Common::List<Graphics::PixelFormat>::iterator i = formats.begin(); i != formats.end(); ++i) {
-		if ((*i).bytesPerPixel > 1) {
-			format = *i;
-			break;
-		}
-	}
-
-	initGraphics(width, height, &format);
-}
-
-Common::Error GargoyleEngine::run() {
-	initialize();
-
-	Common::File f;
-	if (f.open(getFilename()))
-		runGame(&f);
-
-	return Common::kNoError;
-}
-
-void GargoyleEngine::GUIError(const char *msg, ...) {
-	char buffer[STRINGBUFLEN];
-	va_list va;
-
-	// Generate the full error message
-	va_start(va, msg);
-	vsnprintf(buffer, STRINGBUFLEN, msg, va);
-	va_end(va);
-
-	GUIErrorMessage(buffer);
-}
-
-} // End of namespace Gargoyle
diff --git a/engines/glk/gargoyle.h b/engines/glk/gargoyle.h
deleted file mode 100644
index d3019ca..0000000
--- a/engines/glk/gargoyle.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GLK_GARGOLE_H
-#define GLK_GARGOLE_H
-
-#include "common/scummsys.h"
-#include "common/random.h"
-#include "common/system.h"
-#include "common/serializer.h"
-#include "engines/advancedDetector.h"
-#include "engines/engine.h"
-#include "glk/glk_types.h"
-
-namespace Gargoyle {
-
-class Clipboard;
-class Conf;
-class Events;
-class PicList;
-class Screen;
-class Selection;
-class Streams;
-class Windows;
-
-enum InterpreterType {
-	INTERPRETER_ADVSYS = 0,
-	INTERPRETER_AGILITY = 1,
-	INTERPRETER_ALAN2 = 2,
-	INTERPRETER_ALAN3 = 3,
-	INTERPRETER_BOCFEL = 4,
-	INTERPRETER_FROTZ = 5,
-	INTERPRETER_GEAS = 6,
-	INTERPRETER_HUGO = 7,
-	INTERPRETER_JACL = 8,
-	INTERPRETER_LEVEL9 = 9,
-	INTERPRETER_MAGNETIC = 10,
-	INTERPRETER_NITFOL = 11,
-	INTERPRETER_SCARE = 12,
-	INTERPRETER_SCOTT = 13,
-	INTERPRETER_TADS = 14
-};
-
-enum GargoyleDebugChannels {
-	kDebugCore      = 1 << 0,
-	kDebugScripts   = 1 << 1,
-	kDebugGraphics  = 1 << 2,
-	kDebugSound     = 1 << 3
-};
-
-
-#define GLK_SAVEGAME_VERSION 1
-
-struct GargoyleGameDescription;
-
-/**
- * Base class for the different interpreters
- */
-class GargoyleEngine : public Engine {
-private:
-	/**
-	 * Handles basic initialization
-	 */
-	void initialize();
-
-	/**
-	 * Setup the video mode
-	 */
-	void initGraphicsMode();
-protected:
-	const GargoyleGameDescription *_gameDescription;
-	Common::RandomSource _random;
-	int _loadSaveSlot;
-
-	// Engine APIs
-	virtual Common::Error run();
-
-	/**
-	  * Returns true whether a given feature is supported by the engine
-	  */
-	virtual bool hasFeature(EngineFeature f) const;
-
-	/**
-	 * Main game loop for the individual interpreters
-	 */
-	virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
-public:
-	Clipboard *_clipboard;
-	Conf *_conf;
-	Events *_events;
-	PicList *_picList;
-	Screen *_screen;
-	Selection *_selection;
-	Streams *_streams;
-	Windows *_windows;
-	bool _copySelect;
-	bool _terminated;
-	void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock);
-	gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, const char *typecode);
-	void (*gli_unregister_arr)(void *array, glui32 len, const char *typecode, gidispatch_rock_t objrock);
-
-public:
-	GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
-	virtual ~GargoyleEngine();
-
-	/**
-	 * Returns true if a savegame can be loaded
-	 */
-	virtual bool canLoadGameStateCurrently() override {
-		return true;
-	}
-
-	/**
-	 * Returns true if the game can be saved
-	 */
-	virtual bool canSaveGameStateCurrently() override {
-		return true;
-	}
-
-	/**
-	 * Returns the bitset of game features
-	 */
-	uint32 getFeatures() const;
-
-	/**
-	 * Returns whether the game is a demo
-	 */
-	bool isDemo() const;
-
-	/**
-	 * Returns the language
-	 */
-	Common::Language getLanguage() const;
-
-	/**
-	 * Returns the running interpreter type
-	 */
-	InterpreterType getInterpreterType() const;
-
-	/**
-	 * Returns the game's md5
-	 */
-	const Common::String &getGameMD5() const;
-
-	/**
-	 * Returns the primary filename for the game
-	 */
-	const Common::String &getFilename() const;
-
-	/**
-	 * Return the game engine's target name
-	 */
-	const Common::String &getTargetName() const {
-		return _targetName;
-	}
-
-	/**
-	 * Return the filename for a given save slot
-	 */
-	Common::String getSaveName(uint slot) const {
-		return Common::String::format("%s.%.3u", getTargetName().c_str(), slot);
-	}
-
-	/**
-	 * Display a message in a GUI dialog
-	 */
-	void GUIError(const char *msg, ...);
-};
-
-extern GargoyleEngine *g_vm;
-
-} // End of namespace Gargoyle
-
-#endif
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 77fb242..a35f242 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -4,1171 +4,117 @@
  * 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
+ * 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
+ * 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
+ * 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
+ * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  */
 
+#include "common/scummsys.h"
+#include "common/config-manager.h"
+#include "common/debug-channels.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "engines/util.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
 #include "glk/glk.h"
 #include "glk/conf.h"
 #include "glk/events.h"
 #include "glk/picture.h"
+#include "glk/screen.h"
+#include "glk/selection.h"
 #include "glk/streams.h"
-#include "glk/unicode.h"
 #include "glk/windows.h"
-#include "glk/window_graphics.h"
-#include "glk/window_text_buffer.h"
-#include "glk/window_pair.h"
 
-
-namespace Gargoyle {
-
-Glk::Glk(OSystem *syst, const GargoyleGameDescription *gameDesc) :
-	GargoyleEngine(syst, gameDesc), _gliFirstEvent(false) {
-	// Set uppercase/lowercase tables
-	int ix, res;
-	for (ix = 0; ix < 256; ix++) {
-		_charToupperTable[ix] = ix;
-		_charTolowerTable[ix] = ix;
-	}
-
-	for (ix = 0; ix < 256; ix++) {
-		if (ix >= 'A' && ix <= 'Z')
-			res = ix + ('a' - 'A');
-		else if (ix >= 0xC0 && ix <= 0xDE && ix != 0xD7)
-			res = ix + 0x20;
-		else
-			res = 0;
-
-		if (res) {
-			_charTolowerTable[ix] = res;
-			_charToupperTable[res] = ix;
+namespace Glk {
+
+GlkEngine *g_vm;
+
+GlkEngine::GlkEngine(OSystem *syst, const GlkGameDescription *gameDesc) :
+	_gameDescription(gameDesc), Engine(syst), _random("Glk"), _clipboard(nullptr),
+	_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr),
+	_selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
+	gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
+	g_vm = this;
+}
+
+GlkEngine::~GlkEngine() {
+	delete _clipboard;
+	delete _conf;
+	delete _events;
+	delete _picList;
+	delete _screen;
+	delete _selection;
+	delete _streams;
+	delete _windows;
+}
+
+void GlkEngine::initialize() {
+	// Set up debug channels
+	DebugMan.addDebugChannel(kDebugCore, "core", "Core engine debug level");
+	DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
+	DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling");
+	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
+
+	initGraphicsMode();
+	_conf = new Conf();
+	_screen = new Screen();
+
+	_clipboard = new Clipboard();
+	_events = new Events();
+	_picList = new PicList();
+	_selection = new Selection();
+	_streams = new Streams();
+	_windows = new Windows(_screen);
+}
+
+void GlkEngine::initGraphicsMode() {
+	uint width = ConfMan.hasKey("width") ? ConfMan.getInt("width") : 640;
+	uint height = ConfMan.hasKey("height") ? ConfMan.getInt("height") : 480;
+	Common::List<Graphics::PixelFormat> formats = g_system->getSupportedFormats();
+	Graphics::PixelFormat format = formats.front();
+
+	for (Common::List<Graphics::PixelFormat>::iterator i = formats.begin(); i != formats.end(); ++i) {
+		if ((*i).bytesPerPixel > 1) {
+			format = *i;
+			break;
 		}
 	}
-}
-
-void Glk::glk_exit(void) {
-	glk_put_string("[ press any key to exit ]");
-	_events->waitForPress();
-
-	quitGame();
-}
-
-void Glk::glk_set_interrupt_handler(void(*func)(void)) {
-	// This library doesn't handle interrupts.
-}
-
-void Glk::glk_tick(void) {
-	// Nothing needed
-}
-
-glui32 Glk::glk_gestalt(glui32 id, glui32 val) {
-	return glk_gestalt_ext(id, val, nullptr, 0);
-}
-
-glui32 Glk::glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen) {
-	switch (id) {
-	case gestalt_Version:
-		return 0x00000703;
-
-	case gestalt_LineInput:
-		if (val >= 32 && val < 0x10ffff)
-			return true;
-		else
-			return false;
-
-	case gestalt_CharInput:
-		if (val >= 32 && val < 0x10ffff)
-			return true;
-		else if (val == keycode_Return)
-			return true;
-		else
-			return false;
-
-	case gestalt_CharOutput:
-		if (val >= 32 && val < 0x10ffff) {
-			if (arr && arrlen >= 1)
-				arr[0] = 1;
-			return gestalt_CharOutput_ExactPrint;
-		} else {
-			// cheaply, we don't do any translation of printed characters,
-			// so the output is always one character even if it's wrong.
-			if (arr && arrlen >= 1)
-				arr[0] = 1;
-			return gestalt_CharOutput_CannotPrint;
-		}
-
-	case gestalt_MouseInput:
-		if (val == wintype_TextGrid)
-			return true;
-		if (val == wintype_Graphics)
-			return true;
-		return false;
-
-	case gestalt_Graphics:
-	case gestalt_GraphicsTransparency:
-		return g_conf->_graphics;
-
-	case gestalt_DrawImage:
-		if (val == wintype_TextBuffer)
-			return g_conf->_graphics;
-		if (val == wintype_Graphics)
-			return g_conf->_graphics;
-		return false;
-
-	case gestalt_Sound:
-	case gestalt_SoundVolume:
-	case gestalt_SoundMusic:
-	case gestalt_SoundNotify:
-		return g_conf->_sound;
-
-	case gestalt_LineTerminatorKey:
-		return Window::checkTerminator(val);
-
-	case gestalt_Timer:
-	case gestalt_Unicode:
-	case gestalt_UnicodeNorm:
-	case gestalt_Hyperlinks:
-	case gestalt_HyperlinkInput:
-	case gestalt_LineInputEcho:
-	case gestalt_LineTerminators:
-	case gestalt_DateTime:
-	case gestalt_GarglkText:
-		return true;
-
-	case gestalt_Sound2:
-	default:
-		return false;
-	}
-}
 
-unsigned char Glk::glk_char_to_lower(unsigned char ch) {
-	return _charTolowerTable[ch];
+	initGraphics(width, height, &format);
 }
 
-unsigned char Glk::glk_char_to_upper(unsigned char ch) {
-	return _charToupperTable[ch];
-}
+Common::Error GlkEngine::run() {
+	initialize();
 
-winid_t Glk::glk_window_get_root(void) const {
-	return _windows->getRoot();
-}
+	Common::File f;
+	if (f.open(getFilename()))
+		runGame(&f);
 
-winid_t Glk::glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, glui32 rock) const {
-	return _windows->windowOpen(split, method, size, wintype, rock);
+	return Common::kNoError;
 }
 
-void Glk::glk_window_close(winid_t win, stream_result_t *result) {
-	if (win) {
-		_windows->windowClose(win, result);
-	} else {
-		warning("glk_window_close: invalid ref");
-	}
-}
-
-void Glk::glk_window_get_size(winid_t win, glui32 *width, glui32 *height) {
-	if (win) {
-		win->getSize(width, height);
-	} else {
-		warning("window_get_size: invalid ref");
-	}
-}
+void GlkEngine::GUIError(const char *msg, ...) {
+	char buffer[STRINGBUFLEN];
+	va_list va;
 
-void Glk::glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin) {
-	if (win) {
-		win->setArrangement(method, size, keywin);
-	} else {
-		warning("window_set_arrangement: invalid ref");
-	}
-}
-
-void Glk::glk_window_get_arrangement(winid_t win, glui32 *method,
-                                     glui32 *size, winid_t *keyWin) {
-	if (win) {
-		win->getArrangement(method, size, keyWin);
-	} else {
-		warning("window_get_arrangement: invalid ref");
-	}
-}
-
-winid_t Glk::glk_window_iterate(winid_t win, glui32 *rock) {
-	win = win ? win->_next : _windows->getRoot();
-
-	if (win) {
-		if (rock)
-			*rock = win->_rock;
-		return win;
-	}
-
-	if (rock)
-		*rock = 0;
-
-	return nullptr;
-}
-
-glui32 Glk::glk_window_get_rock(winid_t win) {
-	if (win) {
-		return win->_rock;
-	} else {
-		warning("window_get_rock: invalid ref.");
-		return 0;
-	}
-}
-
-glui32 Glk::glk_window_get_type(winid_t win) {
-	if (win) {
-		return win->_type;
-	} else {
-		warning("window_get_parent: invalid ref");
-		return 0;
-	}
-}
-
-winid_t Glk::glk_window_get_parent(winid_t win) {
-	if (!win) {
-		warning("window_get_parent: invalid ref");
-		return 0;
-	}
-
-	return win->_parent;
-}
-
-winid_t Glk::glk_window_get_sibling(winid_t win) {
-	if (!win) {
-		warning("window_get_sibling: invalid ref");
-		return nullptr;
-	}
-
-	PairWindow *parentWin = dynamic_cast<PairWindow *>(win->_parent);
-	if (!parentWin)
-		return nullptr;
-
-	if (parentWin->_child1 == win)
-		return parentWin->_child2;
-	else if (parentWin->_child2 == win)
-		return parentWin->_child1;
-
-	return nullptr;
-}
-
-void Glk::glk_window_clear(winid_t win) {
-	if (!win) {
-		warning("window_clear: invalid ref");
-	} else {
-		if (win->_lineRequest || win->_lineRequestUni) {
-			if (g_conf->_safeClicks && _events->_forceClick) {
-				glk_cancel_line_event(win, nullptr);
-				_events->_forceClick = false;
-
-				win->clear();
-			} else {
-				warning("window_clear: window has pending line request");
-				return;
-			}
-		}
-
-		// Clear the window
-		win->clear();
-	}
-}
-
-void Glk::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
-	if (win) {
-		win->moveCursor(Point(xpos, ypos));
-	} else {
-		warning("window_move_cursor: invalid ref");
-	}
-}
-
-strid_t Glk::glk_window_get_stream(winid_t win) {
-	if (win) {
-		return win->_stream;
-	} else {
-		warning("window_get_stream: invalid ref");
-		return nullptr;
-	}
-}
-
-void Glk::glk_window_set_echo_stream(winid_t win, strid_t str) {
-	if (win) {
-		win->_echoStream = str;
-	} else {
-		warning("window_set_echo_stream: invalid window id");
-	}
-}
-
-strid_t Glk::glk_window_get_echo_stream(winid_t win) {
-	if (!win) {
-		warning("window_get_echo_stream: invalid ref");
-		return nullptr;
-	}
-
-	return win->_echoStream;
-}
-
-void Glk::glk_set_window(winid_t win) {
-	_streams->setCurrent(win ? win->_stream : nullptr);
-}
-
-strid_t Glk::glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock) {
-	return _streams->openFileStream(fileref, fmode, rock, false);
-}
-
-strid_t Glk::glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock) {
-	return _streams->openMemoryStream(buf, buflen, fmode, rock, false);
-}
-
-void Glk::glk_stream_close(strid_t str, stream_result_t *result) {
-	str->close(result);
-}
-
-strid_t Glk::glk_stream_iterate(strid_t str, glui32 *rockptr) const {
-	return str ? str->getNext(rockptr) : _streams->getFirst(rockptr);
-}
-
-glui32 Glk::glk_stream_get_rock(strid_t str) const {
-	if (!str) {
-		warning("stream_get_rock: invalid ref");
-		return 0;
-	}
-
-	return str->getRock();
-}
-
-void Glk::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode) {
-	if (str) {
-		str->setPosition(pos, seekMode);
-	} else {
-		warning("stream_set_position: invalid ref");
-	}
-}
+	// Generate the full error message
+	va_start(va, msg);
+	vsnprintf(buffer, STRINGBUFLEN, msg, va);
+	va_end(va);
 
-glui32 Glk::glk_stream_get_position(strid_t str) const {
-	if (str) {
-		return str->getPosition();
-	} else {
-		warning("stream_get_position: invalid ref");
-		return 0;
-	}
-}
-
-void Glk::glk_stream_set_current(strid_t str) {
-	_streams->setCurrent(str);
-}
-
-strid_t Glk::glk_stream_get_current(void) {
-	return _streams->getCurrent();
-}
-
-void Glk::glk_put_char(unsigned char ch) {
-	_streams->getCurrent()->putChar(ch);
-}
-
-void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
-	if (str) {
-		str->putChar(ch);
-	} else {
-		warning("put_char_stream: invalid ref");
-	}
-}
-
-void Glk::glk_put_string(const char *s) {
-	_streams->getCurrent()->putBuffer(s, strlen(s));
-}
-
-void Glk::glk_put_string_stream(strid_t str, const char *s) {
-	str->putBuffer(s, strlen(s));
-}
-
-void Glk::glk_put_buffer(char *buf, glui32 len) {
-	_streams->getCurrent()->putBuffer(buf, len);
-}
-
-void Glk::glk_put_buffer_stream(strid_t str, const char *buf, glui32 len) {
-	str->putBuffer(buf, len);
-}
-
-void Glk::glk_set_style(glui32 styl) {
-	_streams->getCurrent()->setStyle(styl);
-}
-
-void Glk::glk_set_style_stream(strid_t str, glui32 styl) {
-	if (str) {
-		str->setStyle(styl);
-	} else {
-		warning("set_style_stream: invalid ref");
-	}
-}
-
-glsi32 Glk::glk_get_char_stream(strid_t str) {
-	if (str) {
-		return str->getChar();
-	} else {
-		warning("get_char_stream: invalid ref");
-		return -1;
-	}
-}
-
-glui32 Glk::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
-	if (str) {
-		return str->getLine(buf, len);
-	} else {
-		warning("get_line_stream: invalid ref");
-		return 0;
-	}
-}
-
-glui32 Glk::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
-	if (str) {
-		return str->getBuffer(buf, len);
-	} else {
-		warning("get_line_stream: invalid ref");
-		return 0;
-	}
-}
-
-void Glk::glk_stylehint_set(glui32 wintype, glui32 style, glui32 hint, glsi32 val) {
-	WindowStyle *styles;
-	bool p, b, i;
-
-	if (wintype == wintype_AllTypes) {
-		glk_stylehint_set(wintype_TextGrid, style, hint, val);
-		glk_stylehint_set(wintype_TextBuffer, style, hint, val);
-		return;
-	}
-
-	if (wintype == wintype_TextGrid)
-		styles = g_conf->_gStyles;
-	else if (wintype == wintype_TextBuffer)
-		styles = g_conf->_tStyles;
-	else
-		return;
-
-	if (!g_conf->_styleHint)
-		return;
-
-	switch (hint) {
-	case stylehint_TextColor:
-		styles[style].fg[0] = (val >> 16) & 0xff;
-		styles[style].fg[1] = (val >> 8) & 0xff;
-		styles[style].fg[2] = (val) & 0xff;
-		break;
-
-	case stylehint_BackColor:
-		styles[style].bg[0] = (val >> 16) & 0xff;
-		styles[style].bg[1] = (val >> 8) & 0xff;
-		styles[style].bg[2] = (val) & 0xff;
-		break;
-
-	case stylehint_ReverseColor:
-		styles[style].reverse = (val != 0);
-		break;
-
-	case stylehint_Proportional:
-		if (wintype == wintype_TextBuffer) {
-			p = val > 0;
-			b = styles[style].isBold();
-			i = styles[style].isItalic();
-			styles[style].font = WindowStyle::makeFont(p, b, i);
-		}
-		break;
-
-	case stylehint_Weight:
-		p = styles[style].isProp();
-		b = val > 0;
-		i = styles[style].isItalic();
-		styles[style].font = WindowStyle::makeFont(p, b, i);
-		break;
-
-	case stylehint_Oblique:
-		p = styles[style].isProp();
-		b = styles[style].isBold();
-		i = val > 0;
-		styles[style].font = WindowStyle::makeFont(p, b, i);
-		break;
-	}
-
-	if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_BackColor) {
-		memcpy(g_conf->_windowColor, styles[style].bg, 3);
-	}
-
-	if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_TextColor) {
-		memcpy(g_conf->_moreColor, styles[style].fg, 3);
-		memcpy(g_conf->_caretColor, styles[style].fg, 3);
-	}
-}
-
-void Glk::glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint) {
-	WindowStyle *styles;
-	const WindowStyle *defaults;
-
-	if (wintype == wintype_AllTypes) {
-		glk_stylehint_clear(wintype_TextGrid, style, hint);
-		glk_stylehint_clear(wintype_TextBuffer, style, hint);
-		return;
-	}
-
-	if (wintype == wintype_TextGrid) {
-		styles = g_conf->_gStyles;
-		defaults = g_conf->_gStylesDefault;
-	} else if (wintype == wintype_TextBuffer) {
-		styles = g_conf->_tStyles;
-		defaults = g_conf->_tStylesDefault;
-	} else {
-		return;
-	}
-
-	if (!g_conf->_styleHint)
-		return;
-
-	switch (hint) {
-	case stylehint_TextColor:
-		styles[style].fg[0] = defaults[style].fg[0];
-		styles[style].fg[1] = defaults[style].fg[1];
-		styles[style].fg[2] = defaults[style].fg[2];
-		break;
-
-	case stylehint_BackColor:
-		styles[style].bg[0] = defaults[style].bg[0];
-		styles[style].bg[1] = defaults[style].bg[1];
-		styles[style].bg[2] = defaults[style].bg[2];
-		break;
-
-	case stylehint_ReverseColor:
-		styles[style].reverse = defaults[style].reverse;
-		break;
-
-	case stylehint_Proportional:
-	case stylehint_Weight:
-	case stylehint_Oblique:
-		styles[style].font = defaults[style].font;
-		break;
-	}
-}
-
-glui32 Glk::glk_style_distinguish(winid_t win, glui32 style1, glui32 style2) {
-	const WindowStyle *styles = win->getStyles();
-	if (!styles)
-		return false;
-
-	return styles[style1] == styles[style2] ? 0 : 1;
-}
-
-bool Glk::glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result) {
-	const WindowStyle *styles = win->getStyles();
-	if (!styles)
-		return false;
-
-	switch (hint) {
-	case stylehint_Indentation:
-	case stylehint_ParaIndentation:
-		*result = 0;
-		break;
-
-	case stylehint_Justification:
-		*result = stylehint_just_LeftFlush;
-		break;
-
-	case stylehint_Size:
-		*result = 1;
-		break;
-
-	case stylehint_Weight:
-		*result =
-		    (styles[style].font == PROPB || styles[style].font == PROPZ ||
-		     styles[style].font == MONOB || styles[style].font == MONOZ);
-		break;
-
-	case stylehint_Oblique:
-		*result =
-		    (styles[style].font == PROPI || styles[style].font == PROPZ ||
-		     styles[style].font == MONOI || styles[style].font == MONOZ);
-		break;
-
-	case stylehint_Proportional:
-		*result =
-		    (styles[style].font == PROPR || styles[style].font == PROPI ||
-		     styles[style].font == PROPB || styles[style].font == PROPZ);
-		break;
-
-	case stylehint_TextColor:
-		*result =
-		    (styles[style].fg[0] << 16) | (styles[style].fg[1] << 8) | (styles[style].fg[2]);
-		break;
-
-	case stylehint_BackColor:
-		*result =
-		    (styles[style].bg[0] << 16) | (styles[style].bg[1] << 8) | (styles[style].bg[2]);
-		break;
-
-	case stylehint_ReverseColor:
-		*result = styles[style].reverse;
-		break;
-
-	default:
-		return false;
-	}
-
-	return true;
-}
-
-frefid_t Glk::glk_fileref_create_temp(glui32 usage, glui32 rock) {
-	return _streams->createTemp(usage, rock);
-}
-
-frefid_t Glk::glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock) {
-	// Take out all dangerous characters
-	Common::String tempName(name);
-	for (uint idx = 0; idx < tempName.size(); ++idx) {
-		if (tempName[idx] == '/' || tempName[idx] == '\\' || tempName[idx] == ':')
-			tempName.setChar(idx, '-');
-	}
-
-	return _streams->createRef(tempName, usage, rock);
-}
-
-frefid_t Glk::glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock) {
-	return _streams->createByPrompt(usage, fmode, rock);
-}
-
-frefid_t Glk::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
-	if (!fref) {
-		warning("fileref_create_from_fileref: invalid ref");
-		return nullptr;
-	} else {
-		return _streams->createFromRef(fref, usage, rock);
-	}
-}
-
-void Glk::glk_fileref_destroy(frefid_t fref) {
-	_streams->deleteRef(fref);
-}
-
-frefid_t Glk::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
-	return _streams->iterate(fref, rockptr);
-}
-
-glui32 Glk::glk_fileref_get_rock(frefid_t fref) {
-	if (!fref) {
-		warning("fileref_get_rock: invalid ref.");
-		return 0;
-	} else {
-		return fref->_rock;
-	}
-}
-
-void Glk::glk_fileref_delete_file(frefid_t fref) {
-	fref->deleteFile();
-}
-
-glui32 Glk::glk_fileref_does_file_exist(frefid_t fref) {
-	return fref->exists();
-}
-
-void Glk::glk_select(event_t *event) {
-	if (!_gliFirstEvent) {
-		_windows->inputGuessFocus();
-		_gliFirstEvent = true;
-	}
-
-	_events->getEvent(event, false);
-}
-
-void Glk::glk_select_poll(event_t *event) {
-	if (!_gliFirstEvent) {
-		_windows->inputGuessFocus();
-		_gliFirstEvent = true;
-	}
-
-	_events->getEvent(event, true);
-}
-
-void Glk::glk_request_timer_events(glui32 millisecs) {
-	_events->setTimerInterval(millisecs);
-}
-
-void Glk::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
-	if (!win) {
-		warning("request_line_event: invalid ref");
-	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
-	           || win->_lineRequestUni) {
-		warning("request_line_event: window already has keyboard request");
-	} else {
-		win->requestLineEvent(buf, maxlen, initlen);
-	}
-}
-
-void Glk::glk_request_char_event(winid_t win) {
-	if (!win) {
-		warning("request_char_event: invalid ref");
-	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
-	           || win->_lineRequestUni) {
-		warning("request_char_event: window already has keyboard request");
-	} else {
-		win->requestCharEvent();
-	}
-}
-
-void Glk::glk_request_mouse_event(winid_t win) {
-	if (!win) {
-		warning("request_mouse_event: invalid ref");
-	} else {
-		win->requestMouseEvent();
-	}
-}
-
-void Glk::glk_cancel_line_event(winid_t win, event_t *event) {
-	if (!win) {
-		warning("cancel_line_event: invalid ref");
-	} else {
-		win->cancelLineEvent(event);
-	}
-}
-
-void Glk::glk_cancel_char_event(winid_t win) {
-	if (!win) {
-		warning("glk_cancel_char_event: invalid ref");
-	} else {
-		win->cancelCharEvent();
-	}
-}
-
-void Glk::glk_cancel_mouse_event(winid_t win) {
-	if (!win) {
-		warning("cancel_mouse_event: invalid ref");
-	} else {
-		win->cancelMouseEvent();
-	}
-}
-
-void Glk::glk_set_echo_line_event(winid_t win, glui32 val) {
-	if (!win) {
-		warning("set_echo_line_event: invalid ref");
-	} else {
-		win->setEchoLineEvent(val);
-	}
-}
-
-void Glk::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
-	if (!win) {
-		warning("set_terminators_line_event: invalid ref");
-	} else {
-		win->setTerminatorsLineEvent(keycodes, count);
-	}
-}
-
-glui32 Glk::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	return bufferChangeCase(buf, len, numchars, CASE_LOWER, COND_ALL, true);
-}
-
-glui32 Glk::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	return bufferChangeCase(buf, len, numchars, CASE_UPPER, COND_ALL, true);
-}
-
-glui32 Glk::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
-        glui32 numchars, glui32 lowerrest) {
-	return bufferChangeCase(buf, len, numchars, CASE_TITLE, COND_LINESTART, lowerrest);
-}
-
-void Glk::glk_put_char_uni(glui32 ch) {
-	_streams->getCurrent()->putCharUni(ch);
-}
-
-void Glk::glk_put_string_uni(glui32 *s) {
-	_streams->getCurrent()->putBufferUni(s, strlen_uni(s));
-}
-
-void Glk::glk_put_buffer_uni(glui32 *buf, glui32 len) {
-	_streams->getCurrent()->putBufferUni(buf, len);
-}
-
-void Glk::glk_put_char_stream_uni(strid_t str, glui32 ch) {
-	if (str) {
-		str->putCharUni(ch);
-	} else {
-		warning("put_char_stream_uni: invalid ref");
-	}
-}
-
-void Glk::glk_put_string_stream_uni(strid_t str, const glui32 *s) {
-	if (str) {
-		str->putBufferUni(s, strlen_uni(s));
-	} else {
-		warning("put_string_stream_uni: invalid ref");
-	}
-}
-
-void Glk::glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len) {
-	if (str) {
-		str->putBufferUni(buf, len);
-	} else {
-		warning("put_buffer_stream_uni: invalid ref");
-	}
-}
-
-glsi32 Glk::glk_get_char_stream_uni(strid_t str) {
-	if (str) {
-		return str->getCharUni();
-	} else {
-		warning("get_char_stream_uni: invalid ref");
-		return -1;
-	}
-}
-
-glui32 Glk::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	if (str) {
-		return str->getBufferUni(buf, len);
-	} else {
-		warning("get_buffer_stream_uni: invalid ref");
-		return 0;
-	}
-}
-
-glui32 Glk::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
-	if (str) {
-		return str->getLineUni(buf, len);
-	} else  {
-		warning("get_line_stream_uni: invalid ref");
-		return (glui32) - 1;
-	}
-}
-
-strid_t Glk::glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock) {
-	return _streams->openFileStream(fileref, fmode, rock, true);
-}
-
-strid_t Glk::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock) {
-	return _streams->openMemoryStream(buf, buflen, fmode, rock, true);
-}
-
-void Glk::glk_request_char_event_uni(winid_t win) {
-	if (!win) {
-		warning("request_char_event_uni: invalid ref");
-	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
-	           || win->_lineRequestUni) {
-		warning("request_char_event_uni: window already has keyboard request");
-	} else {
-		win->requestCharEvent();
-	}
-}
-
-void Glk::glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen) {
-	if (!win) {
-		warning("request_line_event_uni: invalid ref");
-	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
-	           || win->_lineRequestUni) {
-		warning("request_line_event_uni: window already has keyboard request");
-	} else {
-		win->requestLineEventUni(buf, maxlen, initlen);
-	}
-}
-
-glui32 Glk::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
-        glui32 numchars) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) {
-	return 0;
-}
-
-glui32 Glk::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
-	if (!win) {
-		warning("image_draw: invalid ref");
-	} else if (g_conf->_graphics) {
-		TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
-		GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
-
-		if (textWin)
-			textWin->drawPicture(image, val1, false, 0, 0);
-		else if (gfxWin)
-			gfxWin->drawPicture(image, val1, val2, false, 0, 0);
-	}
-
-	return false;
-}
-
-glui32 Glk::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2,
-                                  glui32 width, glui32 height) {
-	if (!win) {
-		warning("image_draw_scaled: invalid ref");
-	} else if (g_conf->_graphics) {
-		TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
-		GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
-
-		if (textWin)
-			textWin->drawPicture(image, val1, true, width, height);
-		else if (gfxWin)
-			gfxWin->drawPicture(image, val1, val2, true, width, height);
-	}
-
-	return false;
-}
-
-glui32 Glk::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
-	if (!g_conf->_graphics)
-		return false;
-
-	Picture *pic = Picture::load(image);
-	if (!pic)
-		return false;
-
-	if (width)
-		*width = pic->w;
-	if (height)
-		*height = pic->h;
-
-	return true;
-}
-
-void Glk::glk_window_flow_break(winid_t win) {
-	if (!win) {
-		warning("window_erase_rect: invalid ref");
-	} else {
-		win->flowBreak();
-	}
-}
-
-void Glk::glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height) {
-	if (!win) {
-		warning("window_erase_rect: invalid ref");
-	} else {
-		win->eraseRect(false, Rect(left, top, left + width, top + height));
-	}
-}
-
-void Glk::glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top,
-                               glui32 width, glui32 height) {
-	if (!win) {
-		warning("window_fill_rect: invalid ref");
-	} else {
-		win->eraseRect(color, Rect(left, top, left + width, top + height));
-	}
-}
-
-void Glk::glk_window_set_background_color(winid_t win, glui32 color) {
-	if (!win) {
-		warning("window_set_background_color: invalid ref");
-	} else {
-		win->setBackgroundColor(color);
-	}
-}
-
-schanid_t Glk::glk_schannel_create(glui32 rock) {
-	// TODO
-	return nullptr;
-}
-
-void Glk::glk_schannel_destroy(schanid_t chan) {
-	// TODO
-}
-
-schanid_t Glk::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
-	// TODO
-	return nullptr;
-}
-
-glui32 Glk::glk_schannel_get_rock(schanid_t chan) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_schannel_play(schanid_t chan, glui32 snd) {
-	// TODO
-	return 0;
-}
-
-glui32 Glk::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_schannel_stop(schanid_t chan) {
-	// TODO
-}
-
-void Glk::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
-	// TODO
-}
-
-void Glk::glk_sound_load_hint(glui32 snd, glui32 flag) {
-	// TODO
-}
-
-schanid_t Glk::glk_schannel_create_ext(glui32 rock, glui32 volume) {
-	// TODO
-	return nullptr;
-}
-
-glui32 Glk::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
-                                    glui32 *sndarray, glui32 soundcount, glui32 notify) {
-	// TODO
-	return 0;
-}
-
-void Glk::glk_schannel_pause(schanid_t chan) {
-	// TODO
-}
-
-void Glk::glk_schannel_unpause(schanid_t chan) {
-	// TODO
-}
-
-void Glk::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
-                                      glui32 duration, glui32 notify) {
-	// TODO
-}
-
-void Glk::glk_set_hyperlink(glui32 linkval) {
-	_streams->getCurrent()->setHyperlink(linkval);
-}
-
-void Glk::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
-	if (str)
-		str->setHyperlink(linkval);
-}
-
-void Glk::glk_request_hyperlink_event(winid_t win) {
-	if (!win) {
-		warning("request_hyperlink_event: invalid ref");
-	} else {
-		win->requestHyperlinkEvent();
-	}
-}
-
-void Glk::glk_cancel_hyperlink_event(winid_t win) {
-	if (win) {
-		win->cancelHyperlinkEvent();
-	} else {
-		warning("cancel_hyperlink_event: invalid ref");
-	}
-}
-
-/*--------------------------------------------------------------------------*/
-
-void Glk::glk_current_time(glktimeval_t *time) {
-	TimeAndDate td;
-	*time = td;
-}
-
-glsi32 Glk::glk_current_simple_time(glui32 factor) {
-	assert(factor);
-	TimeAndDate td;
-
-	return td / factor;
-}
-
-void Glk::glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date) {
-	// TODO: timezones aren't currently supported
-	*date = TimeAndDate(*time);
-}
-
-void Glk::glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date) {
-	*date = TimeAndDate(*time);
-}
-
-void Glk::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
-	TimeSeconds secs = (int64)time * factor;
-	*date = TimeAndDate(secs);
-}
-
-void Glk::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
-	TimeSeconds secs = (int64)time * factor;
-	*date = TimeAndDate(secs);
-}
-
-void Glk::glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time) {
-	// WORKAROUND: timezones aren't currently supported
-	*time = TimeAndDate(*date);
-}
-
-void Glk::glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time) {
-	*time = TimeAndDate(*date);
-}
-
-glsi32 Glk::glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor) {
-	// WORKAROUND: timezones aren't currently supported
-	assert(factor);
-	TimeSeconds ts = TimeAndDate(*date);
-	return ts / factor;
-}
-
-glsi32 Glk::glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor) {
-	assert(factor);
-	TimeSeconds ts = TimeAndDate(*date);
-	return ts / factor;
-}
-
-/*--------------------------------------------------------------------------*/
-
-/* XXX non-official Glk functions */
-
-const char *Glk::garglk_fileref_get_name(frefid_t fref) const {
-	return fref->_filename.c_str();
-}
-
-void Glk::garglk_set_program_name(const char *name) {
-	// Program name isn't displayed
-}
-
-void Glk::garglk_set_program_info(const char *info) {
-	// Program info isn't displayed
-}
-
-void Glk::garglk_set_story_name(const char *name) {
-	// Story name isn't displayed
-}
-
-void Glk::garglk_set_story_title(const char *title) {
-	// Story title isn't displayed
-}
-
-void Glk::garglk_set_config(const char *name) {
-	// No implementation
-}
-
-void Glk::garglk_unput_string(const char *str) {
-	_streams->getCurrent()->unputBuffer(str, strlen(str));
-}
-
-void Glk::garglk_unput_string_uni(const glui32 *str) {
-	_streams->getCurrent()->unputBufferUni(str, strlen_uni(str));
-}
-
-void Glk::garglk_set_zcolors(glui32 fg, glui32 bg) {
-	_streams->getCurrent()->setZColors(fg, bg);
-}
-
-void Glk::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
-	if (str) {
-		str->setZColors(fg, bg);
-	} else {
-		warning("set_style_stream: Invalid ref");
-	}
-}
-
-void Glk::garglk_set_reversevideo(glui32 reverse) {
-	_streams->getCurrent()->setReverseVideo(reverse != 0);
-}
-
-void Glk::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
-	if (str) {
-		str->setReverseVideo(reverse != 0);
-	} else {
-		warning("set_reversevideo: Invalid ref");
-	}
+	GUIErrorMessage(buffer);
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index f71ad76..92ea2ab 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -23,270 +23,171 @@
 #ifndef GLK_GLK_H
 #define GLK_GLK_H
 
-#include "glk/gargoyle.h"
+#include "common/scummsys.h"
+#include "common/random.h"
+#include "common/system.h"
+#include "common/serializer.h"
+#include "engines/advancedDetector.h"
+#include "engines/engine.h"
 #include "glk/glk_types.h"
-#include "glk/blorb.h"
-#include "glk/time.h"
-#include "glk/windows.h"
 
-namespace Gargoyle {
+namespace Glk {
+
+class Clipboard;
+class Conf;
+class Events;
+class PicList;
+class Screen;
+class Selection;
+class Streams;
+class Windows;
+
+enum InterpreterType {
+	INTERPRETER_ADVSYS = 0,
+	INTERPRETER_AGILITY = 1,
+	INTERPRETER_ALAN2 = 2,
+	INTERPRETER_ALAN3 = 3,
+	INTERPRETER_BOCFEL = 4,
+	INTERPRETER_FROTZ = 5,
+	INTERPRETER_GEAS = 6,
+	INTERPRETER_HUGO = 7,
+	INTERPRETER_JACL = 8,
+	INTERPRETER_LEVEL9 = 9,
+	INTERPRETER_MAGNETIC = 10,
+	INTERPRETER_NITFOL = 11,
+	INTERPRETER_SCARE = 12,
+	INTERPRETER_SCOTT = 13,
+	INTERPRETER_TADS = 14
+};
 
-/**
- * Implements the GLK interface
- */
-class Glk : public GargoyleEngine, public Blorb {
-private:
-	bool _gliFirstEvent;
-	unsigned char _charTolowerTable[256];
-	unsigned char _charToupperTable[256];
-public:
-	/**
-	 * Constructor
-	 */
-	Glk(OSystem *syst, const GargoyleGameDescription *gameDesc);
+enum GlkDebugChannels {
+	kDebugCore      = 1 << 0,
+	kDebugScripts   = 1 << 1,
+	kDebugGraphics  = 1 << 2,
+	kDebugSound     = 1 << 3
+};
 
-	void glk_exit(void);
-	void glk_set_interrupt_handler(void(*func)(void));
-	void glk_tick(void);
 
-	glui32 glk_gestalt(glui32 id, glui32 val);
-	glui32 glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen);
+#define GLK_SAVEGAME_VERSION 1
 
-	unsigned char glk_char_to_lower(unsigned char ch);
-	unsigned char glk_char_to_upper(unsigned char ch);
+struct GlkGameDescription;
 
+/**
+ * Base class for the different interpreters
+ */
+class GlkEngine : public Engine {
+private:
 	/**
-	 * Get the root window of the window hierarchy
+	 * Handles basic initialization
 	 */
-	winid_t glk_window_get_root(void) const;
+	void initialize();
 
 	/**
-	 * Open a new window
+	 * Setup the video mode
 	 */
-	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
-	                        glui32 wintype, glui32 rock = 0) const;
-
-	void glk_window_close(winid_t win, stream_result_t *result = nullptr);
-	void glk_window_get_size(winid_t win, glui32 *width, glui32 *height);
-	void glk_window_set_arrangement(winid_t win, glui32 method,
-	                                glui32 size, winid_t keyWin);
-	void glk_window_get_arrangement(winid_t win, glui32 *method,
-	                                glui32 *size, winid_t *keyWin);
-	winid_t glk_window_iterate(winid_t win, glui32 *rock = 0);
-	glui32 glk_window_get_rock(winid_t win);
-	glui32 glk_window_get_type(winid_t win);
-	winid_t glk_window_get_parent(winid_t win);
-	winid_t glk_window_get_sibling(winid_t win);
-	void glk_window_clear(winid_t win);
-	void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);
-
-	strid_t glk_window_get_stream(winid_t win);
-	void glk_window_set_echo_stream(winid_t win, strid_t str);
-	strid_t glk_window_get_echo_stream(winid_t win);
-	void glk_set_window(winid_t win);
-
-	strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock = 0);
-	strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
-	void glk_stream_close(strid_t str, stream_result_t *result = nullptr);
-	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const;
-	glui32 glk_stream_get_rock(strid_t str) const;
-	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode);
-	glui32 glk_stream_get_position(strid_t str) const;
-	void glk_stream_set_current(strid_t str);
-	strid_t glk_stream_get_current(void);
-
-	void glk_put_char(unsigned char ch);
-	void glk_put_char_stream(strid_t str, unsigned char ch);
-	void glk_put_string(const char *s);
-	void glk_put_string_stream(strid_t str, const char *s);
-	void glk_put_buffer(char *buf, glui32 len);
-	void glk_put_buffer_stream(strid_t str, const char *buf, glui32 len);
-	void glk_set_style(glui32 styl);
-	void glk_set_style_stream(strid_t str, glui32 styl);
-
-	glsi32 glk_get_char_stream(strid_t str);
-	glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);
-	glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
-
-	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
-	                       glsi32 val);
-	void glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint);
-	glui32 glk_style_distinguish(winid_t win, glui32 style1, glui32 style2);
-	bool glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result);
+	void initGraphicsMode();
+protected:
+	const GlkGameDescription *_gameDescription;
+	Common::RandomSource _random;
+	int _loadSaveSlot;
 
-	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock = 0);
-	frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock = 0);
-	frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock = 0);
-	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock = 0);
-	void glk_fileref_destroy(frefid_t fref);
-	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
-	glui32 glk_fileref_get_rock(frefid_t fref);
-	void glk_fileref_delete_file(frefid_t fref);
-	glui32 glk_fileref_does_file_exist(frefid_t fref);
+	// Engine APIs
+	virtual Common::Error run();
 
-	void glk_select(event_t *event);
-	void glk_select_poll(event_t *event);
-
-	void glk_request_timer_events(glui32 millisecs);
-
-	void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
-	                            glui32 initlen);
-	void glk_request_char_event(winid_t win);
-	void glk_request_mouse_event(winid_t win);
-
-	void glk_cancel_line_event(winid_t win, event_t *event);
-	void glk_cancel_char_event(winid_t win);
-	void glk_cancel_mouse_event(winid_t win);
-
-#ifdef GLK_MODULE_LINE_ECHO
-	void glk_set_echo_line_event(winid_t win, glui32 val);
-#endif /* GLK_MODULE_LINE_ECHO */
-
-#ifdef GLK_MODULE_LINE_TERMINATORS
-	void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
-	                                    glui32 count);
-#endif /* GLK_MODULE_LINE_TERMINATORS */
+	/**
+	  * Returns true whether a given feature is supported by the engine
+	  */
+	virtual bool hasFeature(EngineFeature f) const;
 
-	/** \addtogroup Unicode
-	 *  @{
+	/**
+	 * Main game loop for the individual interpreters
 	 */
+	virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
+public:
+	Clipboard *_clipboard;
+	Conf *_conf;
+	Events *_events;
+	PicList *_picList;
+	Screen *_screen;
+	Selection *_selection;
+	Streams *_streams;
+	Windows *_windows;
+	bool _copySelect;
+	bool _terminated;
+	void (*gli_unregister_obj)(void *obj, glui32 objclass, gidispatch_rock_t objrock);
+	gidispatch_rock_t (*gli_register_arr)(void *array, glui32 len, const char *typecode);
+	void (*gli_unregister_arr)(void *array, glui32 len, const char *typecode, gidispatch_rock_t objrock);
 
-	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
-	                                    glui32 numchars);
-	glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
-	                                    glui32 numchars);
-	glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
-	                                    glui32 numchars, glui32 lowerrest);
-
-	void glk_put_char_uni(glui32 ch);
-	void glk_put_string_uni(glui32 *s);
-	void glk_put_buffer_uni(glui32 *buf, glui32 len);
-	void glk_put_char_stream_uni(strid_t str, glui32 ch);
-	void glk_put_string_stream_uni(strid_t str, const glui32 *s);
-	void glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len);
-
-	glsi32 glk_get_char_stream_uni(strid_t str);
-	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
-	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
-
-	strid_t glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock = 0);
-	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
-
-	void glk_request_char_event_uni(winid_t win);
-	void glk_request_line_event_uni(winid_t win, glui32 *buf,
-	                                glui32 maxlen, glui32 initlen);
-
-	/** @}*/
-
-#ifdef GLK_MODULE_UNICODE_NORM
-
-	glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
-	                                      glui32 numchars);
-	glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
-	                                      glui32 numchars);
-
-#endif /* GLK_MODULE_UNICODE_NORM */
-
-#ifdef GLK_MODULE_IMAGE
-
-	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
-	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
-	                             glsi32 val1, glsi32 val2, glui32 width, glui32 height);
-	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
-
-	void glk_window_flow_break(winid_t win);
-
-	void glk_window_erase_rect(winid_t win,
-	                           glsi32 left, glsi32 top, glui32 width, glui32 height);
-	void glk_window_fill_rect(winid_t win, glui32 color,
-	                          glsi32 left, glsi32 top, glui32 width, glui32 height);
-	void glk_window_set_background_color(winid_t win, glui32 color);
-
-#endif /* GLK_MODULE_IMAGE */
-
-#ifdef GLK_MODULE_SOUND
-
-	schanid_t glk_schannel_create(glui32 rock = 0);
-	void glk_schannel_destroy(schanid_t chan);
-	schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
-	glui32 glk_schannel_get_rock(schanid_t chan);
-
-	glui32 glk_schannel_play(schanid_t chan, glui32 snd);
-	glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
-	                             glui32 notify);
-	void glk_schannel_stop(schanid_t chan);
-	void glk_schannel_set_volume(schanid_t chan, glui32 vol);
-
-	void glk_sound_load_hint(glui32 snd, glui32 flag);
-
-#ifdef GLK_MODULE_SOUND2
-	/* Note that this section is nested inside the #ifdef GLK_MODULE_SOUND.
-	GLK_MODULE_SOUND must be defined if GLK_MODULE_SOUND2 is. */
-
-	schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
-	glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
-	                               glui32 *sndarray, glui32 soundcount, glui32 notify);
-	void glk_schannel_pause(schanid_t chan);
-	void glk_schannel_unpause(schanid_t chan);
-	void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
-	                                 glui32 duration, glui32 notify);
-
-#endif /* GLK_MODULE_SOUND2 */
-#endif /* GLK_MODULE_SOUND */
-
-#ifdef GLK_MODULE_HYPERLINKS
+public:
+	GlkEngine(OSystem *syst, const GlkGameDescription *gameDesc);
+	virtual ~GlkEngine();
 
-	void glk_set_hyperlink(glui32 linkval);
-	void glk_set_hyperlink_stream(strid_t str, glui32 linkval);
-	void glk_request_hyperlink_event(winid_t win);
-	void glk_cancel_hyperlink_event(winid_t win);
+	/**
+	 * Returns true if a savegame can be loaded
+	 */
+	virtual bool canLoadGameStateCurrently() override {
+		return true;
+	}
 
-#endif /* GLK_MODULE_HYPERLINKS */
+	/**
+	 * Returns true if the game can be saved
+	 */
+	virtual bool canSaveGameStateCurrently() override {
+		return true;
+	}
 
-#ifdef GLK_MODULE_DATETIME
+	/**
+	 * Returns the bitset of game features
+	 */
+	uint32 getFeatures() const;
 
-	void glk_current_time(glktimeval_t *time);
-	glsi32 glk_current_simple_time(glui32 factor);
-	void glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date);
-	void glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date);
-	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date);
-	void glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date);
-	void glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time);
-	void glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time);
-	glsi32 glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor);
-	glsi32 glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor);
+	/**
+	 * Returns whether the game is a demo
+	 */
+	bool isDemo() const;
 
-#endif /* GLK_MODULE_DATETIME */
+	/**
+	 * Returns the language
+	 */
+	Common::Language getLanguage() const;
 
-	/* XXX non-official Glk functions that may or may not exist */
-#define GARGLK 1
+	/**
+	 * Returns the running interpreter type
+	 */
+	InterpreterType getInterpreterType() const;
 
-	const char *garglk_fileref_get_name(frefid_t fref) const;
+	/**
+	 * Returns the game's md5
+	 */
+	const Common::String &getGameMD5() const;
 
-	void garglk_set_program_name(const char *name);
-	void garglk_set_program_info(const char *info);
-	void garglk_set_story_name(const char *name);
-	void garglk_set_story_title(const char *title);
-	void garglk_set_config(const char *name);
+	/**
+	 * Returns the primary filename for the game
+	 */
+	const Common::String &getFilename() const;
 
 	/**
-	 * Removes the specified string from the end of the output buffer, if
-	 * indeed it is there.
+	 * Return the game engine's target name
 	 */
-	void garglk_unput_string(const char *str);
+	const Common::String &getTargetName() const {
+		return _targetName;
+	}
 
 	/**
-	 * Removes the specified string from the end of the output buffer, if
-	 * indeed it is there.
+	 * Return the filename for a given save slot
 	 */
-	void garglk_unput_string_uni(const glui32 *str);
+	Common::String getSaveName(uint slot) const {
+		return Common::String::format("%s.%.3u", getTargetName().c_str(), slot);
+	}
 
-	void garglk_set_zcolors(glui32 fg, glui32 bg);
-	void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
-	void garglk_set_reversevideo(glui32 reverse);
-	void garglk_set_reversevideo_stream(strid_t str, glui32 reverse);
+	/**
+	 * Display a message in a GUI dialog
+	 */
+	void GUIError(const char *msg, ...);
 };
 
-} // End of namespace Gargoyle
+extern GlkEngine *g_vm;
+
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
new file mode 100644
index 0000000..c219cd1
--- /dev/null
+++ b/engines/glk/glk_api.cpp
@@ -0,0 +1,1174 @@
+/* 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/glk_api.h"
+#include "glk/conf.h"
+#include "glk/events.h"
+#include "glk/picture.h"
+#include "glk/streams.h"
+#include "glk/unicode.h"
+#include "glk/windows.h"
+#include "glk/window_graphics.h"
+#include "glk/window_text_buffer.h"
+#include "glk/window_pair.h"
+
+
+namespace Glk {
+
+GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription *gameDesc) :
+	GlkEngine(syst, gameDesc), _gliFirstEvent(false) {
+	// Set uppercase/lowercase tables
+	int ix, res;
+	for (ix = 0; ix < 256; ix++) {
+		_charToupperTable[ix] = ix;
+		_charTolowerTable[ix] = ix;
+	}
+
+	for (ix = 0; ix < 256; ix++) {
+		if (ix >= 'A' && ix <= 'Z')
+			res = ix + ('a' - 'A');
+		else if (ix >= 0xC0 && ix <= 0xDE && ix != 0xD7)
+			res = ix + 0x20;
+		else
+			res = 0;
+
+		if (res) {
+			_charTolowerTable[ix] = res;
+			_charToupperTable[res] = ix;
+		}
+	}
+}
+
+void GlkAPI::glk_exit(void) {
+	glk_put_string("[ press any key to exit ]");
+	_events->waitForPress();
+
+	quitGame();
+}
+
+void GlkAPI::glk_set_interrupt_handler(void(*func)(void)) {
+	// This library doesn't handle interrupts.
+}
+
+void GlkAPI::glk_tick(void) {
+	// Nothing needed
+}
+
+glui32 GlkAPI::glk_gestalt(glui32 id, glui32 val) {
+	return glk_gestalt_ext(id, val, nullptr, 0);
+}
+
+glui32 GlkAPI::glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen) {
+	switch (id) {
+	case gestalt_Version:
+		return 0x00000703;
+
+	case gestalt_LineInput:
+		if (val >= 32 && val < 0x10ffff)
+			return true;
+		else
+			return false;
+
+	case gestalt_CharInput:
+		if (val >= 32 && val < 0x10ffff)
+			return true;
+		else if (val == keycode_Return)
+			return true;
+		else
+			return false;
+
+	case gestalt_CharOutput:
+		if (val >= 32 && val < 0x10ffff) {
+			if (arr && arrlen >= 1)
+				arr[0] = 1;
+			return gestalt_CharOutput_ExactPrint;
+		} else {
+			// cheaply, we don't do any translation of printed characters,
+			// so the output is always one character even if it's wrong.
+			if (arr && arrlen >= 1)
+				arr[0] = 1;
+			return gestalt_CharOutput_CannotPrint;
+		}
+
+	case gestalt_MouseInput:
+		if (val == wintype_TextGrid)
+			return true;
+		if (val == wintype_Graphics)
+			return true;
+		return false;
+
+	case gestalt_Graphics:
+	case gestalt_GraphicsTransparency:
+		return g_conf->_graphics;
+
+	case gestalt_DrawImage:
+		if (val == wintype_TextBuffer)
+			return g_conf->_graphics;
+		if (val == wintype_Graphics)
+			return g_conf->_graphics;
+		return false;
+
+	case gestalt_Sound:
+	case gestalt_SoundVolume:
+	case gestalt_SoundMusic:
+	case gestalt_SoundNotify:
+		return g_conf->_sound;
+
+	case gestalt_LineTerminatorKey:
+		return Window::checkTerminator(val);
+
+	case gestalt_Timer:
+	case gestalt_Unicode:
+	case gestalt_UnicodeNorm:
+	case gestalt_Hyperlinks:
+	case gestalt_HyperlinkInput:
+	case gestalt_LineInputEcho:
+	case gestalt_LineTerminators:
+	case gestalt_DateTime:
+	case gestalt_GarglkText:
+		return true;
+
+	case gestalt_Sound2:
+	default:
+		return false;
+	}
+}
+
+unsigned char GlkAPI::glk_char_to_lower(unsigned char ch) {
+	return _charTolowerTable[ch];
+}
+
+unsigned char GlkAPI::glk_char_to_upper(unsigned char ch) {
+	return _charToupperTable[ch];
+}
+
+winid_t GlkAPI::glk_window_get_root(void) const {
+	return _windows->getRoot();
+}
+
+winid_t GlkAPI::glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, glui32 rock) const {
+	return _windows->windowOpen(split, method, size, wintype, rock);
+}
+
+void GlkAPI::glk_window_close(winid_t win, stream_result_t *result) {
+	if (win) {
+		_windows->windowClose(win, result);
+	} else {
+		warning("glk_window_close: invalid ref");
+	}
+}
+
+void GlkAPI::glk_window_get_size(winid_t win, glui32 *width, glui32 *height) {
+	if (win) {
+		win->getSize(width, height);
+	} else {
+		warning("window_get_size: invalid ref");
+	}
+}
+
+void GlkAPI::glk_window_set_arrangement(winid_t win, glui32 method, glui32 size, winid_t keywin) {
+	if (win) {
+		win->setArrangement(method, size, keywin);
+	} else {
+		warning("window_set_arrangement: invalid ref");
+	}
+}
+
+void GlkAPI::glk_window_get_arrangement(winid_t win, glui32 *method,
+                                     glui32 *size, winid_t *keyWin) {
+	if (win) {
+		win->getArrangement(method, size, keyWin);
+	} else {
+		warning("window_get_arrangement: invalid ref");
+	}
+}
+
+winid_t GlkAPI::glk_window_iterate(winid_t win, glui32 *rock) {
+	win = win ? win->_next : _windows->getRoot();
+
+	if (win) {
+		if (rock)
+			*rock = win->_rock;
+		return win;
+	}
+
+	if (rock)
+		*rock = 0;
+
+	return nullptr;
+}
+
+glui32 GlkAPI::glk_window_get_rock(winid_t win) {
+	if (win) {
+		return win->_rock;
+	} else {
+		warning("window_get_rock: invalid ref.");
+		return 0;
+	}
+}
+
+glui32 GlkAPI::glk_window_get_type(winid_t win) {
+	if (win) {
+		return win->_type;
+	} else {
+		warning("window_get_parent: invalid ref");
+		return 0;
+	}
+}
+
+winid_t GlkAPI::glk_window_get_parent(winid_t win) {
+	if (!win) {
+		warning("window_get_parent: invalid ref");
+		return 0;
+	}
+
+	return win->_parent;
+}
+
+winid_t GlkAPI::glk_window_get_sibling(winid_t win) {
+	if (!win) {
+		warning("window_get_sibling: invalid ref");
+		return nullptr;
+	}
+
+	PairWindow *parentWin = dynamic_cast<PairWindow *>(win->_parent);
+	if (!parentWin)
+		return nullptr;
+
+	if (parentWin->_child1 == win)
+		return parentWin->_child2;
+	else if (parentWin->_child2 == win)
+		return parentWin->_child1;
+
+	return nullptr;
+}
+
+void GlkAPI::glk_window_clear(winid_t win) {
+	if (!win) {
+		warning("window_clear: invalid ref");
+	} else {
+		if (win->_lineRequest || win->_lineRequestUni) {
+			if (g_conf->_safeClicks && _events->_forceClick) {
+				glk_cancel_line_event(win, nullptr);
+				_events->_forceClick = false;
+
+				win->clear();
+			} else {
+				warning("window_clear: window has pending line request");
+				return;
+			}
+		}
+
+		// Clear the window
+		win->clear();
+	}
+}
+
+void GlkAPI::glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) {
+	if (win) {
+		win->moveCursor(Point(xpos, ypos));
+	} else {
+		warning("window_move_cursor: invalid ref");
+	}
+}
+
+strid_t GlkAPI::glk_window_get_stream(winid_t win) {
+	if (win) {
+		return win->_stream;
+	} else {
+		warning("window_get_stream: invalid ref");
+		return nullptr;
+	}
+}
+
+void GlkAPI::glk_window_set_echo_stream(winid_t win, strid_t str) {
+	if (win) {
+		win->_echoStream = str;
+	} else {
+		warning("window_set_echo_stream: invalid window id");
+	}
+}
+
+strid_t GlkAPI::glk_window_get_echo_stream(winid_t win) {
+	if (!win) {
+		warning("window_get_echo_stream: invalid ref");
+		return nullptr;
+	}
+
+	return win->_echoStream;
+}
+
+void GlkAPI::glk_set_window(winid_t win) {
+	_streams->setCurrent(win ? win->_stream : nullptr);
+}
+
+strid_t GlkAPI::glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock) {
+	return _streams->openFileStream(fileref, fmode, rock, false);
+}
+
+strid_t GlkAPI::glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock) {
+	return _streams->openMemoryStream(buf, buflen, fmode, rock, false);
+}
+
+void GlkAPI::glk_stream_close(strid_t str, stream_result_t *result) {
+	str->close(result);
+}
+
+strid_t GlkAPI::glk_stream_iterate(strid_t str, glui32 *rockptr) const {
+	return str ? str->getNext(rockptr) : _streams->getFirst(rockptr);
+}
+
+glui32 GlkAPI::glk_stream_get_rock(strid_t str) const {
+	if (!str) {
+		warning("stream_get_rock: invalid ref");
+		return 0;
+	}
+
+	return str->getRock();
+}
+
+void GlkAPI::glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode) {
+	if (str) {
+		str->setPosition(pos, seekMode);
+	} else {
+		warning("stream_set_position: invalid ref");
+	}
+}
+
+glui32 GlkAPI::glk_stream_get_position(strid_t str) const {
+	if (str) {
+		return str->getPosition();
+	} else {
+		warning("stream_get_position: invalid ref");
+		return 0;
+	}
+}
+
+void GlkAPI::glk_stream_set_current(strid_t str) {
+	_streams->setCurrent(str);
+}
+
+strid_t GlkAPI::glk_stream_get_current(void) {
+	return _streams->getCurrent();
+}
+
+void GlkAPI::glk_put_char(unsigned char ch) {
+	_streams->getCurrent()->putChar(ch);
+}
+
+void GlkAPI::glk_put_char_stream(strid_t str, unsigned char ch) {
+	if (str) {
+		str->putChar(ch);
+	} else {
+		warning("put_char_stream: invalid ref");
+	}
+}
+
+void GlkAPI::glk_put_string(const char *s) {
+	_streams->getCurrent()->putBuffer(s, strlen(s));
+}
+
+void GlkAPI::glk_put_string_stream(strid_t str, const char *s) {
+	str->putBuffer(s, strlen(s));
+}
+
+void GlkAPI::glk_put_buffer(char *buf, glui32 len) {
+	_streams->getCurrent()->putBuffer(buf, len);
+}
+
+void GlkAPI::glk_put_buffer_stream(strid_t str, const char *buf, glui32 len) {
+	str->putBuffer(buf, len);
+}
+
+void GlkAPI::glk_set_style(glui32 styl) {
+	_streams->getCurrent()->setStyle(styl);
+}
+
+void GlkAPI::glk_set_style_stream(strid_t str, glui32 styl) {
+	if (str) {
+		str->setStyle(styl);
+	} else {
+		warning("set_style_stream: invalid ref");
+	}
+}
+
+glsi32 GlkAPI::glk_get_char_stream(strid_t str) {
+	if (str) {
+		return str->getChar();
+	} else {
+		warning("get_char_stream: invalid ref");
+		return -1;
+	}
+}
+
+glui32 GlkAPI::glk_get_line_stream(strid_t str, char *buf, glui32 len) {
+	if (str) {
+		return str->getLine(buf, len);
+	} else {
+		warning("get_line_stream: invalid ref");
+		return 0;
+	}
+}
+
+glui32 GlkAPI::glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
+	if (str) {
+		return str->getBuffer(buf, len);
+	} else {
+		warning("get_line_stream: invalid ref");
+		return 0;
+	}
+}
+
+void GlkAPI::glk_stylehint_set(glui32 wintype, glui32 style, glui32 hint, glsi32 val) {
+	WindowStyle *styles;
+	bool p, b, i;
+
+	if (wintype == wintype_AllTypes) {
+		glk_stylehint_set(wintype_TextGrid, style, hint, val);
+		glk_stylehint_set(wintype_TextBuffer, style, hint, val);
+		return;
+	}
+
+	if (wintype == wintype_TextGrid)
+		styles = g_conf->_gStyles;
+	else if (wintype == wintype_TextBuffer)
+		styles = g_conf->_tStyles;
+	else
+		return;
+
+	if (!g_conf->_styleHint)
+		return;
+
+	switch (hint) {
+	case stylehint_TextColor:
+		styles[style].fg[0] = (val >> 16) & 0xff;
+		styles[style].fg[1] = (val >> 8) & 0xff;
+		styles[style].fg[2] = (val) & 0xff;
+		break;
+
+	case stylehint_BackColor:
+		styles[style].bg[0] = (val >> 16) & 0xff;
+		styles[style].bg[1] = (val >> 8) & 0xff;
+		styles[style].bg[2] = (val) & 0xff;
+		break;
+
+	case stylehint_ReverseColor:
+		styles[style].reverse = (val != 0);
+		break;
+
+	case stylehint_Proportional:
+		if (wintype == wintype_TextBuffer) {
+			p = val > 0;
+			b = styles[style].isBold();
+			i = styles[style].isItalic();
+			styles[style].font = WindowStyle::makeFont(p, b, i);
+		}
+		break;
+
+	case stylehint_Weight:
+		p = styles[style].isProp();
+		b = val > 0;
+		i = styles[style].isItalic();
+		styles[style].font = WindowStyle::makeFont(p, b, i);
+		break;
+
+	case stylehint_Oblique:
+		p = styles[style].isProp();
+		b = styles[style].isBold();
+		i = val > 0;
+		styles[style].font = WindowStyle::makeFont(p, b, i);
+		break;
+	}
+
+	if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_BackColor) {
+		memcpy(g_conf->_windowColor, styles[style].bg, 3);
+	}
+
+	if (wintype == wintype_TextBuffer && style == style_Normal && hint == stylehint_TextColor) {
+		memcpy(g_conf->_moreColor, styles[style].fg, 3);
+		memcpy(g_conf->_caretColor, styles[style].fg, 3);
+	}
+}
+
+void GlkAPI::glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint) {
+	WindowStyle *styles;
+	const WindowStyle *defaults;
+
+	if (wintype == wintype_AllTypes) {
+		glk_stylehint_clear(wintype_TextGrid, style, hint);
+		glk_stylehint_clear(wintype_TextBuffer, style, hint);
+		return;
+	}
+
+	if (wintype == wintype_TextGrid) {
+		styles = g_conf->_gStyles;
+		defaults = g_conf->_gStylesDefault;
+	} else if (wintype == wintype_TextBuffer) {
+		styles = g_conf->_tStyles;
+		defaults = g_conf->_tStylesDefault;
+	} else {
+		return;
+	}
+
+	if (!g_conf->_styleHint)
+		return;
+
+	switch (hint) {
+	case stylehint_TextColor:
+		styles[style].fg[0] = defaults[style].fg[0];
+		styles[style].fg[1] = defaults[style].fg[1];
+		styles[style].fg[2] = defaults[style].fg[2];
+		break;
+
+	case stylehint_BackColor:
+		styles[style].bg[0] = defaults[style].bg[0];
+		styles[style].bg[1] = defaults[style].bg[1];
+		styles[style].bg[2] = defaults[style].bg[2];
+		break;
+
+	case stylehint_ReverseColor:
+		styles[style].reverse = defaults[style].reverse;
+		break;
+
+	case stylehint_Proportional:
+	case stylehint_Weight:
+	case stylehint_Oblique:
+		styles[style].font = defaults[style].font;
+		break;
+	}
+}
+
+glui32 GlkAPI::glk_style_distinguish(winid_t win, glui32 style1, glui32 style2) {
+	const WindowStyle *styles = win->getStyles();
+	if (!styles)
+		return false;
+
+	return styles[style1] == styles[style2] ? 0 : 1;
+}
+
+bool GlkAPI::glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result) {
+	const WindowStyle *styles = win->getStyles();
+	if (!styles)
+		return false;
+
+	switch (hint) {
+	case stylehint_Indentation:
+	case stylehint_ParaIndentation:
+		*result = 0;
+		break;
+
+	case stylehint_Justification:
+		*result = stylehint_just_LeftFlush;
+		break;
+
+	case stylehint_Size:
+		*result = 1;
+		break;
+
+	case stylehint_Weight:
+		*result =
+		    (styles[style].font == PROPB || styles[style].font == PROPZ ||
+		     styles[style].font == MONOB || styles[style].font == MONOZ);
+		break;
+
+	case stylehint_Oblique:
+		*result =
+		    (styles[style].font == PROPI || styles[style].font == PROPZ ||
+		     styles[style].font == MONOI || styles[style].font == MONOZ);
+		break;
+
+	case stylehint_Proportional:
+		*result =
+		    (styles[style].font == PROPR || styles[style].font == PROPI ||
+		     styles[style].font == PROPB || styles[style].font == PROPZ);
+		break;
+
+	case stylehint_TextColor:
+		*result =
+		    (styles[style].fg[0] << 16) | (styles[style].fg[1] << 8) | (styles[style].fg[2]);
+		break;
+
+	case stylehint_BackColor:
+		*result =
+		    (styles[style].bg[0] << 16) | (styles[style].bg[1] << 8) | (styles[style].bg[2]);
+		break;
+
+	case stylehint_ReverseColor:
+		*result = styles[style].reverse;
+		break;
+
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+frefid_t GlkAPI::glk_fileref_create_temp(glui32 usage, glui32 rock) {
+	return _streams->createTemp(usage, rock);
+}
+
+frefid_t GlkAPI::glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock) {
+	// Take out all dangerous characters
+	Common::String tempName(name);
+	for (uint idx = 0; idx < tempName.size(); ++idx) {
+		if (tempName[idx] == '/' || tempName[idx] == '\\' || tempName[idx] == ':')
+			tempName.setChar(idx, '-');
+	}
+
+	return _streams->createRef(tempName, usage, rock);
+}
+
+frefid_t GlkAPI::glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock) {
+	return _streams->createByPrompt(usage, fmode, rock);
+}
+
+frefid_t GlkAPI::glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock) {
+	if (!fref) {
+		warning("fileref_create_from_fileref: invalid ref");
+		return nullptr;
+	} else {
+		return _streams->createFromRef(fref, usage, rock);
+	}
+}
+
+void GlkAPI::glk_fileref_destroy(frefid_t fref) {
+	_streams->deleteRef(fref);
+}
+
+frefid_t GlkAPI::glk_fileref_iterate(frefid_t fref, glui32 *rockptr) {
+	return _streams->iterate(fref, rockptr);
+}
+
+glui32 GlkAPI::glk_fileref_get_rock(frefid_t fref) {
+	if (!fref) {
+		warning("fileref_get_rock: invalid ref.");
+		return 0;
+	} else {
+		return fref->_rock;
+	}
+}
+
+void GlkAPI::glk_fileref_delete_file(frefid_t fref) {
+	fref->deleteFile();
+}
+
+glui32 GlkAPI::glk_fileref_does_file_exist(frefid_t fref) {
+	return fref->exists();
+}
+
+void GlkAPI::glk_select(event_t *event) {
+	if (!_gliFirstEvent) {
+		_windows->inputGuessFocus();
+		_gliFirstEvent = true;
+	}
+
+	_events->getEvent(event, false);
+}
+
+void GlkAPI::glk_select_poll(event_t *event) {
+	if (!_gliFirstEvent) {
+		_windows->inputGuessFocus();
+		_gliFirstEvent = true;
+	}
+
+	_events->getEvent(event, true);
+}
+
+void GlkAPI::glk_request_timer_events(glui32 millisecs) {
+	_events->setTimerInterval(millisecs);
+}
+
+void GlkAPI::glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) {
+	if (!win) {
+		warning("request_line_event: invalid ref");
+	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+	           || win->_lineRequestUni) {
+		warning("request_line_event: window already has keyboard request");
+	} else {
+		win->requestLineEvent(buf, maxlen, initlen);
+	}
+}
+
+void GlkAPI::glk_request_char_event(winid_t win) {
+	if (!win) {
+		warning("request_char_event: invalid ref");
+	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+	           || win->_lineRequestUni) {
+		warning("request_char_event: window already has keyboard request");
+	} else {
+		win->requestCharEvent();
+	}
+}
+
+void GlkAPI::glk_request_mouse_event(winid_t win) {
+	if (!win) {
+		warning("request_mouse_event: invalid ref");
+	} else {
+		win->requestMouseEvent();
+	}
+}
+
+void GlkAPI::glk_cancel_line_event(winid_t win, event_t *event) {
+	if (!win) {
+		warning("cancel_line_event: invalid ref");
+	} else {
+		win->cancelLineEvent(event);
+	}
+}
+
+void GlkAPI::glk_cancel_char_event(winid_t win) {
+	if (!win) {
+		warning("glk_cancel_char_event: invalid ref");
+	} else {
+		win->cancelCharEvent();
+	}
+}
+
+void GlkAPI::glk_cancel_mouse_event(winid_t win) {
+	if (!win) {
+		warning("cancel_mouse_event: invalid ref");
+	} else {
+		win->cancelMouseEvent();
+	}
+}
+
+void GlkAPI::glk_set_echo_line_event(winid_t win, glui32 val) {
+	if (!win) {
+		warning("set_echo_line_event: invalid ref");
+	} else {
+		win->setEchoLineEvent(val);
+	}
+}
+
+void GlkAPI::glk_set_terminators_line_event(winid_t win, glui32 *keycodes, glui32 count) {
+	if (!win) {
+		warning("set_terminators_line_event: invalid ref");
+	} else {
+		win->setTerminatorsLineEvent(keycodes, count);
+	}
+}
+
+glui32 GlkAPI::glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	return bufferChangeCase(buf, len, numchars, CASE_LOWER, COND_ALL, true);
+}
+
+glui32 GlkAPI::glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	return bufferChangeCase(buf, len, numchars, CASE_UPPER, COND_ALL, true);
+}
+
+glui32 GlkAPI::glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+        glui32 numchars, glui32 lowerrest) {
+	return bufferChangeCase(buf, len, numchars, CASE_TITLE, COND_LINESTART, lowerrest);
+}
+
+void GlkAPI::glk_put_char_uni(glui32 ch) {
+	_streams->getCurrent()->putCharUni(ch);
+}
+
+void GlkAPI::glk_put_string_uni(glui32 *s) {
+	_streams->getCurrent()->putBufferUni(s, strlen_uni(s));
+}
+
+void GlkAPI::glk_put_buffer_uni(glui32 *buf, glui32 len) {
+	_streams->getCurrent()->putBufferUni(buf, len);
+}
+
+void GlkAPI::glk_put_char_stream_uni(strid_t str, glui32 ch) {
+	if (str) {
+		str->putCharUni(ch);
+	} else {
+		warning("put_char_stream_uni: invalid ref");
+	}
+}
+
+void GlkAPI::glk_put_string_stream_uni(strid_t str, const glui32 *s) {
+	if (str) {
+		str->putBufferUni(s, strlen_uni(s));
+	} else {
+		warning("put_string_stream_uni: invalid ref");
+	}
+}
+
+void GlkAPI::glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len) {
+	if (str) {
+		str->putBufferUni(buf, len);
+	} else {
+		warning("put_buffer_stream_uni: invalid ref");
+	}
+}
+
+glsi32 GlkAPI::glk_get_char_stream_uni(strid_t str) {
+	if (str) {
+		return str->getCharUni();
+	} else {
+		warning("get_char_stream_uni: invalid ref");
+		return -1;
+	}
+}
+
+glui32 GlkAPI::glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	if (str) {
+		return str->getBufferUni(buf, len);
+	} else {
+		warning("get_buffer_stream_uni: invalid ref");
+		return 0;
+	}
+}
+
+glui32 GlkAPI::glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) {
+	if (str) {
+		return str->getLineUni(buf, len);
+	} else  {
+		warning("get_line_stream_uni: invalid ref");
+		return (glui32) - 1;
+	}
+}
+
+strid_t GlkAPI::glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock) {
+	return _streams->openFileStream(fileref, fmode, rock, true);
+}
+
+strid_t GlkAPI::glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock) {
+	return _streams->openMemoryStream(buf, buflen, fmode, rock, true);
+}
+
+void GlkAPI::glk_request_char_event_uni(winid_t win) {
+	if (!win) {
+		warning("request_char_event_uni: invalid ref");
+	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+	           || win->_lineRequestUni) {
+		warning("request_char_event_uni: window already has keyboard request");
+	} else {
+		win->requestCharEvent();
+	}
+}
+
+void GlkAPI::glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen) {
+	if (!win) {
+		warning("request_line_event_uni: invalid ref");
+	} else if (win->_charRequest || win->_lineRequest || win->_charRequestUni
+	           || win->_lineRequestUni) {
+		warning("request_line_event_uni: window already has keyboard request");
+	} else {
+		win->requestLineEventUni(buf, maxlen, initlen);
+	}
+}
+
+glui32 GlkAPI::glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+        glui32 numchars) {
+	// TODO
+	return 0;
+}
+
+glui32 GlkAPI::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 numchars) {
+	return 0;
+}
+
+glui32 GlkAPI::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
+	if (!win) {
+		warning("image_draw: invalid ref");
+	} else if (g_conf->_graphics) {
+		TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
+		GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
+
+		if (textWin)
+			textWin->drawPicture(image, val1, false, 0, 0);
+		else if (gfxWin)
+			gfxWin->drawPicture(image, val1, val2, false, 0, 0);
+	}
+
+	return false;
+}
+
+glui32 GlkAPI::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2,
+                                  glui32 width, glui32 height) {
+	if (!win) {
+		warning("image_draw_scaled: invalid ref");
+	} else if (g_conf->_graphics) {
+		TextBufferWindow *textWin = dynamic_cast<TextBufferWindow *>(win);
+		GraphicsWindow *gfxWin = dynamic_cast<GraphicsWindow *>(win);
+
+		if (textWin)
+			textWin->drawPicture(image, val1, true, width, height);
+		else if (gfxWin)
+			gfxWin->drawPicture(image, val1, val2, true, width, height);
+	}
+
+	return false;
+}
+
+glui32 GlkAPI::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
+	if (!g_conf->_graphics)
+		return false;
+
+	Picture *pic = Picture::load(image);
+	if (!pic)
+		return false;
+
+	if (width)
+		*width = pic->w;
+	if (height)
+		*height = pic->h;
+
+	return true;
+}
+
+void GlkAPI::glk_window_flow_break(winid_t win) {
+	if (!win) {
+		warning("window_erase_rect: invalid ref");
+	} else {
+		win->flowBreak();
+	}
+}
+
+void GlkAPI::glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height) {
+	if (!win) {
+		warning("window_erase_rect: invalid ref");
+	} else {
+		win->eraseRect(false, Rect(left, top, left + width, top + height));
+	}
+}
+
+void GlkAPI::glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top,
+                               glui32 width, glui32 height) {
+	if (!win) {
+		warning("window_fill_rect: invalid ref");
+	} else {
+		win->eraseRect(color, Rect(left, top, left + width, top + height));
+	}
+}
+
+void GlkAPI::glk_window_set_background_color(winid_t win, glui32 color) {
+	if (!win) {
+		warning("window_set_background_color: invalid ref");
+	} else {
+		win->setBackgroundColor(color);
+	}
+}
+
+schanid_t GlkAPI::glk_schannel_create(glui32 rock) {
+	// TODO
+	return nullptr;
+}
+
+void GlkAPI::glk_schannel_destroy(schanid_t chan) {
+	// TODO
+}
+
+schanid_t GlkAPI::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
+	// TODO
+	return nullptr;
+}
+
+glui32 GlkAPI::glk_schannel_get_rock(schanid_t chan) {
+	// TODO
+	return 0;
+}
+
+glui32 GlkAPI::glk_schannel_play(schanid_t chan, glui32 snd) {
+	// TODO
+	return 0;
+}
+
+glui32 GlkAPI::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
+	// TODO
+	return 0;
+}
+
+void GlkAPI::glk_schannel_stop(schanid_t chan) {
+	// TODO
+}
+
+void GlkAPI::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
+	// TODO
+}
+
+void GlkAPI::glk_sound_load_hint(glui32 snd, glui32 flag) {
+	// TODO
+}
+
+schanid_t GlkAPI::glk_schannel_create_ext(glui32 rock, glui32 volume) {
+	// TODO
+	return nullptr;
+}
+
+glui32 GlkAPI::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+                                    glui32 *sndarray, glui32 soundcount, glui32 notify) {
+	// TODO
+	return 0;
+}
+
+void GlkAPI::glk_schannel_pause(schanid_t chan) {
+	// TODO
+}
+
+void GlkAPI::glk_schannel_unpause(schanid_t chan) {
+	// TODO
+}
+
+void GlkAPI::glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+                                      glui32 duration, glui32 notify) {
+	// TODO
+}
+
+void GlkAPI::glk_set_hyperlink(glui32 linkval) {
+	_streams->getCurrent()->setHyperlink(linkval);
+}
+
+void GlkAPI::glk_set_hyperlink_stream(strid_t str, glui32 linkval) {
+	if (str)
+		str->setHyperlink(linkval);
+}
+
+void GlkAPI::glk_request_hyperlink_event(winid_t win) {
+	if (!win) {
+		warning("request_hyperlink_event: invalid ref");
+	} else {
+		win->requestHyperlinkEvent();
+	}
+}
+
+void GlkAPI::glk_cancel_hyperlink_event(winid_t win) {
+	if (win) {
+		win->cancelHyperlinkEvent();
+	} else {
+		warning("cancel_hyperlink_event: invalid ref");
+	}
+}
+
+/*--------------------------------------------------------------------------*/
+
+void GlkAPI::glk_current_time(glktimeval_t *time) {
+	TimeAndDate td;
+	*time = td;
+}
+
+glsi32 GlkAPI::glk_current_simple_time(glui32 factor) {
+	assert(factor);
+	TimeAndDate td;
+
+	return td / factor;
+}
+
+void GlkAPI::glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date) {
+	// TODO: timezones aren't currently supported
+	*date = TimeAndDate(*time);
+}
+
+void GlkAPI::glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date) {
+	*date = TimeAndDate(*time);
+}
+
+void GlkAPI::glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date) {
+	TimeSeconds secs = (int64)time * factor;
+	*date = TimeAndDate(secs);
+}
+
+void GlkAPI::glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date) {
+	TimeSeconds secs = (int64)time * factor;
+	*date = TimeAndDate(secs);
+}
+
+void GlkAPI::glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time) {
+	// WORKAROUND: timezones aren't currently supported
+	*time = TimeAndDate(*date);
+}
+
+void GlkAPI::glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time) {
+	*time = TimeAndDate(*date);
+}
+
+glsi32 GlkAPI::glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor) {
+	// WORKAROUND: timezones aren't currently supported
+	assert(factor);
+	TimeSeconds ts = TimeAndDate(*date);
+	return ts / factor;
+}
+
+glsi32 GlkAPI::glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor) {
+	assert(factor);
+	TimeSeconds ts = TimeAndDate(*date);
+	return ts / factor;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/* XXX non-official Glk functions */
+
+const char *GlkAPI::garglk_fileref_get_name(frefid_t fref) const {
+	return fref->_filename.c_str();
+}
+
+void GlkAPI::garglk_set_program_name(const char *name) {
+	// Program name isn't displayed
+}
+
+void GlkAPI::garglk_set_program_info(const char *info) {
+	// Program info isn't displayed
+}
+
+void GlkAPI::garglk_set_story_name(const char *name) {
+	// Story name isn't displayed
+}
+
+void GlkAPI::garglk_set_story_title(const char *title) {
+	// Story title isn't displayed
+}
+
+void GlkAPI::garglk_set_config(const char *name) {
+	// No implementation
+}
+
+void GlkAPI::garglk_unput_string(const char *str) {
+	_streams->getCurrent()->unputBuffer(str, strlen(str));
+}
+
+void GlkAPI::garglk_unput_string_uni(const glui32 *str) {
+	_streams->getCurrent()->unputBufferUni(str, strlen_uni(str));
+}
+
+void GlkAPI::garglk_set_zcolors(glui32 fg, glui32 bg) {
+	_streams->getCurrent()->setZColors(fg, bg);
+}
+
+void GlkAPI::garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg) {
+	if (str) {
+		str->setZColors(fg, bg);
+	} else {
+		warning("set_style_stream: Invalid ref");
+	}
+}
+
+void GlkAPI::garglk_set_reversevideo(glui32 reverse) {
+	_streams->getCurrent()->setReverseVideo(reverse != 0);
+}
+
+void GlkAPI::garglk_set_reversevideo_stream(strid_t str, glui32 reverse) {
+	if (str) {
+		str->setReverseVideo(reverse != 0);
+	} else {
+		warning("set_reversevideo: Invalid ref");
+	}
+}
+
+} // End of namespace Glk
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
new file mode 100644
index 0000000..f864ac9
--- /dev/null
+++ b/engines/glk/glk_api.h
@@ -0,0 +1,292 @@
+/* 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_GLK_API_H
+#define GLK_GLK_API_H
+
+#include "glk/glk.h"
+#include "glk/glk_types.h"
+#include "glk/blorb.h"
+#include "glk/time.h"
+#include "glk/windows.h"
+
+namespace Glk {
+
+/**
+ * Implements the GLK interface
+ */
+class GlkAPI : public GlkEngine, public Blorb {
+private:
+	bool _gliFirstEvent;
+	unsigned char _charTolowerTable[256];
+	unsigned char _charToupperTable[256];
+public:
+	/**
+	 * Constructor
+	 */
+	GlkAPI(OSystem *syst, const GlkGameDescription *gameDesc);
+
+	void glk_exit(void);
+	void glk_set_interrupt_handler(void(*func)(void));
+	void glk_tick(void);
+
+	glui32 glk_gestalt(glui32 id, glui32 val);
+	glui32 glk_gestalt_ext(glui32 id, glui32 val, glui32 *arr, glui32 arrlen);
+
+	unsigned char glk_char_to_lower(unsigned char ch);
+	unsigned char glk_char_to_upper(unsigned char ch);
+
+	/**
+	 * Get the root window of the window hierarchy
+	 */
+	winid_t glk_window_get_root(void) const;
+
+	/**
+	 * Open a new window
+	 */
+	winid_t glk_window_open(winid_t split, glui32 method, glui32 size,
+	                        glui32 wintype, glui32 rock = 0) const;
+
+	void glk_window_close(winid_t win, stream_result_t *result = nullptr);
+	void glk_window_get_size(winid_t win, glui32 *width, glui32 *height);
+	void glk_window_set_arrangement(winid_t win, glui32 method,
+	                                glui32 size, winid_t keyWin);
+	void glk_window_get_arrangement(winid_t win, glui32 *method,
+	                                glui32 *size, winid_t *keyWin);
+	winid_t glk_window_iterate(winid_t win, glui32 *rock = 0);
+	glui32 glk_window_get_rock(winid_t win);
+	glui32 glk_window_get_type(winid_t win);
+	winid_t glk_window_get_parent(winid_t win);
+	winid_t glk_window_get_sibling(winid_t win);
+	void glk_window_clear(winid_t win);
+	void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos);
+
+	strid_t glk_window_get_stream(winid_t win);
+	void glk_window_set_echo_stream(winid_t win, strid_t str);
+	strid_t glk_window_get_echo_stream(winid_t win);
+	void glk_set_window(winid_t win);
+
+	strid_t glk_stream_open_file(frefid_t fileref, FileMode fmode, glui32 rock = 0);
+	strid_t glk_stream_open_memory(char *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
+	void glk_stream_close(strid_t str, stream_result_t *result = nullptr);
+	strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) const;
+	glui32 glk_stream_get_rock(strid_t str) const;
+	void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekMode);
+	glui32 glk_stream_get_position(strid_t str) const;
+	void glk_stream_set_current(strid_t str);
+	strid_t glk_stream_get_current(void);
+
+	void glk_put_char(unsigned char ch);
+	void glk_put_char_stream(strid_t str, unsigned char ch);
+	void glk_put_string(const char *s);
+	void glk_put_string_stream(strid_t str, const char *s);
+	void glk_put_buffer(char *buf, glui32 len);
+	void glk_put_buffer_stream(strid_t str, const char *buf, glui32 len);
+	void glk_set_style(glui32 styl);
+	void glk_set_style_stream(strid_t str, glui32 styl);
+
+	glsi32 glk_get_char_stream(strid_t str);
+	glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len);
+	glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len);
+
+	void glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint,
+	                       glsi32 val);
+	void glk_stylehint_clear(glui32 wintype, glui32 style, glui32 hint);
+	glui32 glk_style_distinguish(winid_t win, glui32 style1, glui32 style2);
+	bool glk_style_measure(winid_t win, glui32 style, glui32 hint, glui32 *result);
+
+	frefid_t glk_fileref_create_temp(glui32 usage, glui32 rock = 0);
+	frefid_t glk_fileref_create_by_name(glui32 usage, const char *name, glui32 rock = 0);
+	frefid_t glk_fileref_create_by_prompt(glui32 usage, FileMode fmode, glui32 rock = 0);
+	frefid_t glk_fileref_create_from_fileref(glui32 usage, frefid_t fref, glui32 rock = 0);
+	void glk_fileref_destroy(frefid_t fref);
+	frefid_t glk_fileref_iterate(frefid_t fref, glui32 *rockptr);
+	glui32 glk_fileref_get_rock(frefid_t fref);
+	void glk_fileref_delete_file(frefid_t fref);
+	glui32 glk_fileref_does_file_exist(frefid_t fref);
+
+	void glk_select(event_t *event);
+	void glk_select_poll(event_t *event);
+
+	void glk_request_timer_events(glui32 millisecs);
+
+	void glk_request_line_event(winid_t win, char *buf, glui32 maxlen,
+	                            glui32 initlen);
+	void glk_request_char_event(winid_t win);
+	void glk_request_mouse_event(winid_t win);
+
+	void glk_cancel_line_event(winid_t win, event_t *event);
+	void glk_cancel_char_event(winid_t win);
+	void glk_cancel_mouse_event(winid_t win);
+
+#ifdef GLK_MODULE_LINE_ECHO
+	void glk_set_echo_line_event(winid_t win, glui32 val);
+#endif /* GLK_MODULE_LINE_ECHO */
+
+#ifdef GLK_MODULE_LINE_TERMINATORS
+	void glk_set_terminators_line_event(winid_t win, glui32 *keycodes,
+	                                    glui32 count);
+#endif /* GLK_MODULE_LINE_TERMINATORS */
+
+	/** \addtogroup Unicode
+	 *  @{
+	 */
+
+	glui32 glk_buffer_to_lower_case_uni(glui32 *buf, glui32 len,
+	                                    glui32 numchars);
+	glui32 glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len,
+	                                    glui32 numchars);
+	glui32 glk_buffer_to_title_case_uni(glui32 *buf, glui32 len,
+	                                    glui32 numchars, glui32 lowerrest);
+
+	void glk_put_char_uni(glui32 ch);
+	void glk_put_string_uni(glui32 *s);
+	void glk_put_buffer_uni(glui32 *buf, glui32 len);
+	void glk_put_char_stream_uni(strid_t str, glui32 ch);
+	void glk_put_string_stream_uni(strid_t str, const glui32 *s);
+	void glk_put_buffer_stream_uni(strid_t str, const glui32 *buf, glui32 len);
+
+	glsi32 glk_get_char_stream_uni(strid_t str);
+	glui32 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len);
+	glui32 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len);
+
+	strid_t glk_stream_open_file_uni(frefid_t fileref, FileMode fmode, glui32 rock = 0);
+	strid_t glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, FileMode fmode, glui32 rock = 0);
+
+	void glk_request_char_event_uni(winid_t win);
+	void glk_request_line_event_uni(winid_t win, glui32 *buf,
+	                                glui32 maxlen, glui32 initlen);
+
+	/** @}*/
+
+#ifdef GLK_MODULE_UNICODE_NORM
+
+	glui32 glk_buffer_canon_decompose_uni(glui32 *buf, glui32 len,
+	                                      glui32 numchars);
+	glui32 glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len,
+	                                      glui32 numchars);
+
+#endif /* GLK_MODULE_UNICODE_NORM */
+
+#ifdef GLK_MODULE_IMAGE
+
+	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
+	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
+	                             glsi32 val1, glsi32 val2, glui32 width, glui32 height);
+	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
+
+	void glk_window_flow_break(winid_t win);
+
+	void glk_window_erase_rect(winid_t win,
+	                           glsi32 left, glsi32 top, glui32 width, glui32 height);
+	void glk_window_fill_rect(winid_t win, glui32 color,
+	                          glsi32 left, glsi32 top, glui32 width, glui32 height);
+	void glk_window_set_background_color(winid_t win, glui32 color);
+
+#endif /* GLK_MODULE_IMAGE */
+
+#ifdef GLK_MODULE_SOUND
+
+	schanid_t glk_schannel_create(glui32 rock = 0);
+	void glk_schannel_destroy(schanid_t chan);
+	schanid_t glk_schannel_iterate(schanid_t chan, glui32 *rockptr);
+	glui32 glk_schannel_get_rock(schanid_t chan);
+
+	glui32 glk_schannel_play(schanid_t chan, glui32 snd);
+	glui32 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats,
+	                             glui32 notify);
+	void glk_schannel_stop(schanid_t chan);
+	void glk_schannel_set_volume(schanid_t chan, glui32 vol);
+
+	void glk_sound_load_hint(glui32 snd, glui32 flag);
+
+#ifdef GLK_MODULE_SOUND2
+	/* Note that this section is nested inside the #ifdef GLK_MODULE_SOUND.
+	GLK_MODULE_SOUND must be defined if GLK_MODULE_SOUND2 is. */
+
+	schanid_t glk_schannel_create_ext(glui32 rock, glui32 volume);
+	glui32 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
+	                               glui32 *sndarray, glui32 soundcount, glui32 notify);
+	void glk_schannel_pause(schanid_t chan);
+	void glk_schannel_unpause(schanid_t chan);
+	void glk_schannel_set_volume_ext(schanid_t chan, glui32 vol,
+	                                 glui32 duration, glui32 notify);
+
+#endif /* GLK_MODULE_SOUND2 */
+#endif /* GLK_MODULE_SOUND */
+
+#ifdef GLK_MODULE_HYPERLINKS
+
+	void glk_set_hyperlink(glui32 linkval);
+	void glk_set_hyperlink_stream(strid_t str, glui32 linkval);
+	void glk_request_hyperlink_event(winid_t win);
+	void glk_cancel_hyperlink_event(winid_t win);
+
+#endif /* GLK_MODULE_HYPERLINKS */
+
+#ifdef GLK_MODULE_DATETIME
+
+	void glk_current_time(glktimeval_t *time);
+	glsi32 glk_current_simple_time(glui32 factor);
+	void glk_time_to_date_utc(const glktimeval_t *time, glkdate_t *date);
+	void glk_time_to_date_local(const glktimeval_t *time, glkdate_t *date);
+	void glk_simple_time_to_date_utc(glsi32 time, glui32 factor, glkdate_t *date);
+	void glk_simple_time_to_date_local(glsi32 time, glui32 factor, glkdate_t *date);
+	void glk_date_to_time_utc(const glkdate_t *date, glktimeval_t *time);
+	void glk_date_to_time_local(const glkdate_t *date, glktimeval_t *time);
+	glsi32 glk_date_to_simple_time_utc(const glkdate_t *date, glui32 factor);
+	glsi32 glk_date_to_simple_time_local(const glkdate_t *date, glui32 factor);
+
+#endif /* GLK_MODULE_DATETIME */
+
+	/* XXX non-official Glk functions that may or may not exist */
+#define GARGLK 1
+
+	const char *garglk_fileref_get_name(frefid_t fref) const;
+
+	void garglk_set_program_name(const char *name);
+	void garglk_set_program_info(const char *info);
+	void garglk_set_story_name(const char *name);
+	void garglk_set_story_title(const char *title);
+	void garglk_set_config(const char *name);
+
+	/**
+	 * Removes the specified string from the end of the output buffer, if
+	 * indeed it is there.
+	 */
+	void garglk_unput_string(const char *str);
+
+	/**
+	 * Removes the specified string from the end of the output buffer, if
+	 * indeed it is there.
+	 */
+	void garglk_unput_string_uni(const glui32 *str);
+
+	void garglk_set_zcolors(glui32 fg, glui32 bg);
+	void garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg);
+	void garglk_set_reversevideo(glui32 reverse);
+	void garglk_set_reversevideo_stream(strid_t str, glui32 reverse);
+};
+
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index 32b0968..1745178 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -26,7 +26,7 @@
 #include "common/scummsys.h"
 #include "common/stream.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 typedef uint32 glui32;
 typedef int32 glsi32;
@@ -203,6 +203,6 @@ union gidispatch_rock_t {
 	void *ptr;
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index e3fa26b..3fd88ef 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -6,8 +6,8 @@ MODULE_OBJS := \
 	detection.o \
 	events.o \
 	fonts.o \
-	gargoyle.o \
 	glk.o \
+	glk_api.o \
 	picture.o \
 	screen.o \
 	selection.o \
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 444589b..b87b129 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -22,7 +22,7 @@
 
 #include "glk/picture.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 void PicList::increment() {
 	// TODO
@@ -60,4 +60,4 @@ void Picture::drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1) {
 }
 
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/picture.h b/engines/glk/picture.h
index e29d420..3ca615b 100644
--- a/engines/glk/picture.h
+++ b/engines/glk/picture.h
@@ -25,7 +25,7 @@
 
 #include "graphics/surface.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 class PicList {
 public:
@@ -68,6 +68,6 @@ public:
 	void drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1);
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/scott/detection.cpp b/engines/glk/scott/detection.cpp
index 900f650..7afdf93 100644
--- a/engines/glk/scott/detection.cpp
+++ b/engines/glk/scott/detection.cpp
@@ -24,7 +24,7 @@
 #include "common/file.h"
 #include "common/md5.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Scott {
 
 struct ScottGame {
@@ -106,4 +106,4 @@ bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/scott/detection.h b/engines/glk/scott/detection.h
index cd487bd..2c187cd 100644
--- a/engines/glk/scott/detection.h
+++ b/engines/glk/scott/detection.h
@@ -26,7 +26,7 @@
 #include "common/fs.h"
 #include "engines/game.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Scott {
 
 class ScottMetaEngine {
@@ -38,6 +38,6 @@ public:
 };
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index a5e8667..2def1a2 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -23,10 +23,10 @@
 #include "glk/scott/scott.h"
 #include "common/config-manager.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Scott {
 
-Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
+Scott::Scott(OSystem *syst, const GlkGameDescription *gameDesc) : GlkAPI(syst, gameDesc),
 	Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
 	Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
 	split_screen(true), Bottom(0), Top(0), BitFlags(0), _saveSlot(-1) {
@@ -1260,4 +1260,4 @@ void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
 }
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index ec6a860..6899485 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -28,9 +28,9 @@
  */
 
 #include "common/scummsys.h"
-#include "glk/glk.h"
+#include "glk/glk_api.h"
 
-namespace Gargoyle {
+namespace Glk {
 namespace Scott {
 
 #define LIGHT_SOURCE    9   // Always 9 how odd
@@ -109,7 +109,7 @@ struct Tail {
 /**
  * Scott Adams game interpreter
  */
-class Scott : public Glk {
+class Scott : public GlkAPI {
 private:
 	Header GameHeader;
 	Item *Items;
@@ -167,7 +167,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Scott(OSystem *syst, const GargoyleGameDescription *gameDesc);
+	Scott(OSystem *syst, const GlkGameDescription *gameDesc);
 
 	/**
 	 * Execute the game
@@ -186,6 +186,6 @@ public:
 };
 
 } // End of namespace Scott
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/screen.cpp b/engines/glk/screen.cpp
index ff46f1b..1a76ac8 100644
--- a/engines/glk/screen.cpp
+++ b/engines/glk/screen.cpp
@@ -23,7 +23,7 @@
 #include "glk/screen.h"
 #include "glk/conf.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 void Screen::fill(const byte *rgb) {
 	uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
@@ -69,4 +69,4 @@ void Screen::drawCaret(const Point &pos) {
 	}
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/screen.h b/engines/glk/screen.h
index e81cf9f..0020d86 100644
--- a/engines/glk/screen.h
+++ b/engines/glk/screen.h
@@ -26,7 +26,7 @@
 #include "graphics/screen.h"
 #include "glk/fonts.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 enum CaretShape {
 	SMALL_DOT = 0, FAT_DOT = 1, THIN_LINE = 2, FAT_LINE = 3, BLOCK = 4
@@ -60,6 +60,6 @@ public:
 	void drawCaret(const Point &pos);
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/selection.cpp b/engines/glk/selection.cpp
index f1c5bb8..2bc4fb8 100644
--- a/engines/glk/selection.cpp
+++ b/engines/glk/selection.cpp
@@ -22,11 +22,11 @@
 
 #include "glk/selection.h"
 #include "glk/conf.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "glk/windows.h"
 #include "common/system.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 void Clipboard::clipboardStore(const Common::U32String &text) {
 	_text = text;
@@ -318,4 +318,4 @@ bool Selection::getSelection(const Rect &r, int *rx0, int *rx1) const {
 	return (rx0 && rx1);
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/selection.h b/engines/glk/selection.h
index aa44a1d..4497d68 100644
--- a/engines/glk/selection.h
+++ b/engines/glk/selection.h
@@ -29,7 +29,7 @@
 #include "common/rect.h"
 #include "common/ustr.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 enum ClipSource { PRIMARY = 0, CLIPBOARD = 1 };
 
@@ -115,6 +115,6 @@ public:
 	bool getSelection(const Rect &r, int *rx0, int *rx1) const;
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/speech.h b/engines/glk/speech.h
index 5f92555..df4a89e 100644
--- a/engines/glk/speech.h
+++ b/engines/glk/speech.h
@@ -26,7 +26,7 @@
 #include "common/events.h"
 #include "glk/glk_types.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 /**
  * Currently not implemented
@@ -44,6 +44,6 @@ protected:
 	void gli_free_tts(void) {}
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
index d53f5d7..801cd54 100644
--- a/engines/glk/streams.cpp
+++ b/engines/glk/streams.cpp
@@ -23,14 +23,14 @@
 #include "glk/streams.h"
 #include "glk/conf.h"
 #include "glk/events.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "glk/windows.h"
 #include "gui/saveload.h"
 #include "common/file.h"
 #include "common/savefile.h"
 #include "common/translation.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 Stream::Stream(Streams *streams, bool readable, bool writable, uint32 rock, bool unicode) :
 	_streams(streams), _readable(readable), _writable(writable), _readCount(0),
@@ -1589,4 +1589,4 @@ void FileReference::deleteFile() {
 	g_system->getSavefileManager()->removeSavefile(filename);
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/streams.h b/engines/glk/streams.h
index 24ecc68..32bde70 100644
--- a/engines/glk/streams.h
+++ b/engines/glk/streams.h
@@ -29,7 +29,7 @@
 #include "common/str.h"
 #include "glk/glk_types.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 #define SAVEGAME_VERSION 1
 
@@ -634,6 +634,6 @@ public:
 	frefid_t iterate(frefid_t fref, glui32 *rock);
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/time.cpp b/engines/glk/time.cpp
index 9442102..df09716 100644
--- a/engines/glk/time.cpp
+++ b/engines/glk/time.cpp
@@ -23,7 +23,7 @@
 #include "glk/time.h"
 #include "common/system.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 TimeAndDate::TimeAndDate() {
 	::TimeDate t;
@@ -117,4 +117,4 @@ TimeSeconds TimeAndDate::getTime() const {
 	return totalMinutes * 60 + second;
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/time.h b/engines/glk/time.h
index 119fab7..8e68328 100644
--- a/engines/glk/time.h
+++ b/engines/glk/time.h
@@ -25,7 +25,7 @@
 
 #include "glk/glk_types.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 typedef int64 TimeSeconds;
 
@@ -88,6 +88,6 @@ public:
 };
 typedef TimeAndDate glkdate_t;
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/unicode.cpp b/engines/glk/unicode.cpp
index dd88c95..d9f2ee1 100644
--- a/engines/glk/unicode.cpp
+++ b/engines/glk/unicode.cpp
@@ -24,7 +24,7 @@
 #include "glk/unicode_gen.h"
 #include "common/textconsole.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 size_t strlen_uni(const uint32 *s) {
 	size_t len = 0;
@@ -148,4 +148,4 @@ glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCa
 	return outcount;
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/unicode.h b/engines/glk/unicode.h
index 249ab5d..3165d39 100644
--- a/engines/glk/unicode.h
+++ b/engines/glk/unicode.h
@@ -25,7 +25,7 @@
 
 #include "glk/glk_types.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 typedef glui32 gli_case_block_t[2]; /* upper, lower */
 enum BufferChangeCase { CASE_UPPER = 0, CASE_LOWER = 1, CASE_TITLE = 2, CASE_IDENT = 3 };
@@ -46,6 +46,6 @@ size_t strlen_uni(const uint32 *s);
 extern glui32 bufferChangeCase(glui32 *buf, glui32 len,
                                glui32 numchars, BufferChangeCase destcase, BufferChangeCond cond, int changerest);
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/unicode_gen.cpp b/engines/glk/unicode_gen.cpp
index ac8a149..9c67859 100644
--- a/engines/glk/unicode_gen.cpp
+++ b/engines/glk/unicode_gen.cpp
@@ -27,7 +27,7 @@
 
 #include "glk/unicode_gen.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 gli_case_block_t unigen_case_block_0x0[256] = {
 	{ 0x0, 0x0 },
@@ -11823,4 +11823,4 @@ gli_decomp_block_t unigen_decomp_block_0x2fa[256] = {
 		*countptr = 0;  \
 	}
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/unicode_gen.h b/engines/glk/unicode_gen.h
index f601bf6..6822d69 100644
--- a/engines/glk/unicode_gen.h
+++ b/engines/glk/unicode_gen.h
@@ -25,7 +25,7 @@
 
 #include "glk/glk_types.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 #define GET_CASE_BLOCK(ch, blockptr)  \
 	switch ((glui32)(ch) >> 8) {  \
@@ -558,6 +558,6 @@ extern gli_case_special_t unigen_special_0xfb16;
 extern gli_case_special_t unigen_special_0xfb17;
 extern glui32 unigen_special_array[];
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/utils.cpp b/engines/glk/utils.cpp
index 12f9e30..07c0a4d 100644
--- a/engines/glk/utils.cpp
+++ b/engines/glk/utils.cpp
@@ -23,7 +23,7 @@
 #include "glk/utils.h"
 #include "common/textconsole.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 int strToInt(const char *s) {
 	if (!*s)
@@ -41,4 +41,4 @@ int strToInt(const char *s) {
 	return (int)tmp;
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/utils.h b/engines/glk/utils.h
index 361c65f..3655f57 100644
--- a/engines/glk/utils.h
+++ b/engines/glk/utils.h
@@ -26,7 +26,7 @@
 #include "common/rect.h"
 #include "glk/glk_types.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 typedef Common::Point Point;
 
@@ -46,6 +46,6 @@ public:
  */
 int strToInt(const char *s);
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/window_graphics.cpp b/engines/glk/window_graphics.cpp
index 4eea5c0..e80fc8e 100644
--- a/engines/glk/window_graphics.cpp
+++ b/engines/glk/window_graphics.cpp
@@ -22,10 +22,10 @@
 
 #include "glk/window_graphics.h"
 #include "glk/conf.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "glk/screen.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 GraphicsWindow::GraphicsWindow(Windows *windows, uint32 rock) : Window(windows, rock),
 	_w(0), _h(0), _dirty(false), _surface(nullptr) {
@@ -269,4 +269,4 @@ void GraphicsWindow::click(const Point &newPos) {
 	}
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/window_graphics.h b/engines/glk/window_graphics.h
index 1ddc26c..c2f4a55 100644
--- a/engines/glk/window_graphics.h
+++ b/engines/glk/window_graphics.h
@@ -26,7 +26,7 @@
 #include "glk/windows.h"
 #include "glk/picture.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 /**
  * Graphics window
@@ -108,6 +108,6 @@ public:
 	virtual void setBackgroundColor(glui32 color) override;
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/window_pair.cpp b/engines/glk/window_pair.cpp
index 56e5b45..a0fdbb4 100644
--- a/engines/glk/window_pair.cpp
+++ b/engines/glk/window_pair.cpp
@@ -22,10 +22,10 @@
 
 #include "glk/window_pair.h"
 #include "glk/conf.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "glk/screen.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 PairWindow::PairWindow(Windows *windows, glui32 method, Window *key, glui32 size) :
 	Window(windows, 0),
@@ -233,4 +233,4 @@ void PairWindow::click(const Point &newPos) {
 		_child2->click(newPos);
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/window_pair.h b/engines/glk/window_pair.h
index 11b4e49..f77ae92 100644
--- a/engines/glk/window_pair.h
+++ b/engines/glk/window_pair.h
@@ -25,7 +25,7 @@
 
 #include "glk/windows.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 /**
  * Pair window
@@ -68,6 +68,6 @@ public:
 	virtual void click(const Point &newPos) override;
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index 1506f4e..36d8c6e 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -22,12 +22,12 @@
 
 #include "glk/window_text_buffer.h"
 #include "glk/conf.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "glk/screen.h"
 #include "glk/selection.h"
 #include "glk/unicode.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 /**
  *
@@ -1633,4 +1633,4 @@ TextBufferWindow::TextBufferRow::TextBufferRow() : _len(0), _newLine(0), _dirty(
 	Common::fill(&_chars[0], &_chars[TBLINELEN], 0);
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/window_text_buffer.h b/engines/glk/window_text_buffer.h
index a26a95b..4384986 100644
--- a/engines/glk/window_text_buffer.h
+++ b/engines/glk/window_text_buffer.h
@@ -27,7 +27,7 @@
 #include "glk/picture.h"
 #include "glk/speech.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 /**
  * Text Buffer window
@@ -240,6 +240,6 @@ public:
 	}
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/window_text_grid.cpp b/engines/glk/window_text_grid.cpp
index 9630836..e16c9bd 100644
--- a/engines/glk/window_text_grid.cpp
+++ b/engines/glk/window_text_grid.cpp
@@ -22,11 +22,11 @@
 
 #include "glk/window_text_grid.h"
 #include "glk/conf.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "glk/selection.h"
 #include "glk/screen.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 TextGridWindow::TextGridWindow(Windows *windows, uint32 rock) : Window(windows, rock) {
 	_type = wintype_TextGrid;
@@ -651,4 +651,4 @@ void TextGridWindow::TextGridRow::resize(size_t newSize) {
 	Common::fill(&_chars[0], &_chars[0] + newSize, ' ');
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/window_text_grid.h b/engines/glk/window_text_grid.h
index acfd366..30a0f79 100644
--- a/engines/glk/window_text_grid.h
+++ b/engines/glk/window_text_grid.h
@@ -25,7 +25,7 @@
 
 #include "glk/windows.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 /**
  * Text Grid window
@@ -190,6 +190,6 @@ public:
 	}
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif
diff --git a/engines/glk/windows.cpp b/engines/glk/windows.cpp
index d982f6c..02298ad 100644
--- a/engines/glk/windows.cpp
+++ b/engines/glk/windows.cpp
@@ -26,13 +26,13 @@
 #include "glk/window_text_buffer.h"
 #include "glk/window_text_grid.h"
 #include "glk/conf.h"
-#include "glk/gargoyle.h"
+#include "glk/glk.h"
 #include "glk/screen.h"
 #include "glk/streams.h"
 #include "common/algorithm.h"
 #include "common/textconsole.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 bool Windows::_overrideReverse;
 bool Windows::_overrideFgSet;
@@ -770,4 +770,4 @@ byte *Attributes::attrFg(WindowStyle *styles) {
 	}
 }
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
diff --git a/engines/glk/windows.h b/engines/glk/windows.h
index 5bf5f2a..98f0d2d 100644
--- a/engines/glk/windows.h
+++ b/engines/glk/windows.h
@@ -33,7 +33,7 @@
 #include "glk/selection.h"
 #include "glk/streams.h"
 
-namespace Gargoyle {
+namespace Glk {
 
 class Window;
 class PairWindow;
@@ -536,6 +536,6 @@ public:
 	BlankWindow(Windows *windows, uint32 rock);
 };
 
-} // End of namespace Gargoyle
+} // End of namespace Glk
 
 #endif


Commit: 454f92cc74e1357b96ce06e8c77ed6c32cb30ba9
    https://github.com/scummvm/scummvm/commit/454f92cc74e1357b96ce06e8c77ed6c32cb30ba9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Centralize the set of game names

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


diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 893ed87..c3b6865 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -68,7 +68,9 @@ const Common::String &GlkEngine::getGameMD5() const {
 
 } // End of namespace Glk
 
+#include "glk/scott/detection_tables.h"
 #include "glk/frotz/detection_tables.h"
+#define SCOTT(ID, NAME) { ID, Glk::Scott::NAME##_DESC }
 #define ZCODE(ID, NAME) { ID, Glk::Frotz::NAME##_DESC }
 
 static const PlainGameDescriptor glkGames[] = {
@@ -118,23 +120,23 @@ static const PlainGameDescriptor glkGames[] = {
 	ZCODE("ztuu", ZTUU),
 
 	// Scott Adams games
-	{ "adventureland", "Adventureland" },
-	{ "pirateadventure", "Pirate Adventure" },
-	{ "missionimpossible", "Mission Impossible" },
-	{ "voodoocastle", "Voodoo Castle" },
-	{ "thecount", "The Count" },
-	{ "strangeodyssey", "Strange Odyssey" },
-	{ "mysteryfunhouse", "Mystery Fun House" },
-	{ "pyramidofdoom", "Pyramid Of Doom" },
-	{ "ghosttown", "Ghost Town" },
-	{ "savageisland1", "Savage Island, Part 1" },
-	{ "savageisland2", "Savage Island, Part 2" },
-	{ "goldenvoyage", "The Golden Voyage" },
-	{ "adventure13", "Adventure 13" },
-	{ "adventure14", "Adventure 14" },
-	{ "buckaroobonzai", "Buckaroo Banzai" },
-	{ "marveladventure", "Marvel Adventure #1" },
-	{ "scottsampler", "Adventure International's Mini-Adventure Sampler" },
+	SCOTT("adventureland", ADVENTURELAND),
+	SCOTT("pirateadventure", PIRATE_ADVENTURE),
+	SCOTT("missionimpossible", MISSION_IMPOSSIBLE),
+	SCOTT("voodoocastle", VOODOO_CASTLE),
+	SCOTT("thecount", THE_COUNT),
+	SCOTT("strangeodyssey", STRANGE_ODYSSEY),
+	SCOTT("mysteryfunhouse", MYSTERY_FUN_HOUSE),
+	SCOTT("pyramidofdoom", PYRAMID_OF_DOOM),
+	SCOTT("ghosttown", GHOST_TOWN),
+	SCOTT("savageisland1", SAVAGE_ISLAND1),
+	SCOTT("savageisland2", SAVAGE_ISLAND2),
+	SCOTT("goldenvoyage", THE_GOLDEN_VOYAGE),
+	SCOTT("adventure13", ADVENTURE13),
+	SCOTT("adventure14", ADVENTURE14),
+	SCOTT("buckaroobanzai", BUCKAROO_BANZAI),
+	SCOTT("marveladventure", MARVEL_ADVENTURE),
+	SCOTT("scottsampler", MINI_SAMPLER),
 	{0, 0}
 };
 
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 3fd88ef..0f79cd8 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -39,6 +39,7 @@ MODULE_OBJS := \
 	frotz/processor_variables.o \
 	frotz/quetzal.o \
 	scott/detection.o \
+	scott/detection_tables.o \
 	scott/scott.o
 
 # This module can be built as a plugin
diff --git a/engines/glk/scott/detection.cpp b/engines/glk/scott/detection.cpp
index 7afdf93..523b02d 100644
--- a/engines/glk/scott/detection.cpp
+++ b/engines/glk/scott/detection.cpp
@@ -21,57 +21,13 @@
  */
 
 #include "glk/scott/detection.h"
+#include "glk/scott/detection_tables.h"
 #include "common/file.h"
 #include "common/md5.h"
 
 namespace Glk {
 namespace Scott {
 
-struct ScottGame {
-	const char *_md5;
-	const char *_gameId;
-	int32 _filesize;
-	const char *_desc;
-};
-
-const ScottGame SCOTT_GAMES[] = {
-	// PC game versions
-	{ "7c6f495d757a54e73d259efc718d8024", "adventureland",     15896, "Adventureland" },
-	{ "ea535fa7684508410151b4561de1f323", "pirateadventure",   16325, "Pirate Adventure" },
-	{ "379c77a9a483886366b3b5c425e56410", "missionimpossible", 15275, "Mission Impossible" },
-	{ "a530a6857d1092eaa177eee575c94c71", "voodoocastle",      15852, "Voodoo Castle" },
-	{ "5ebb4ade985670bb2eac54f8fa202214", "thecount",          17476, "The Count" },
-	{ "c57bb6df04dc77a2b232bc5bcab6e417", "strangeodyssey",    17489, "Strange Odyssey" },
-	{ "ce2931ac3d5cbc270a5cb7be9e614f6e", "mysteryfunhouse",   17165, "Mystery Fun House" },
-	{ "4e6127fad6b5d75eccd3f3b101f8c9c8", "pyramidofdoom",     17673, "Pyramid Of Doom" },
-	{ "2c08327ab06d5490bd9e367ddaeca627", "ghosttown",         17831, "Ghost Town" },
-	{ "8feb77f11d32e9567ce2fc7d435eaf44", "savageisland1",     19533, "Savage Island, Part 1" },
-	{ "20c40a349f7a214ac515fb1d63c30a87", "savageisland2",     18367, "Savage Island, Part 2" },
-	{ "e2a8f956ab215012d1495550c4c11ee8", "goldenvoyage",      18513, "The Golden Voyage" },
-	{ "f986d7e1ee074f65b6c1d00461c9b3c3", "adventure13",       19232, "Adventure 13" },
-	{ "6d98f422cc986d959a3c74351785aea3", "adventure14",       19013, "Adventure 14" },
-	{ "aadcc04e6b37eb9d30a58b5bc775842e", "marveladventure",   18876, "Marvel Adventure #1" },
-	{ "d569a769f304dc02b3062d97458ddd01", "scottsampler",      13854, "Adventure International's Mini-Adventure Sampler" },
-
-	// PDA game versions
-	{ "ae541fc1085da2f7d561b72ed20a6bc1", "adventureland", 18003, "Adventureland" },
-	{ "cbd47ab4fcfe00231ffd71d52378d410", "pirateadventure", 18482, "Pirate Adventure" },
-	{ "9251ab2c64e63559d8a6e9e6246760a5", "missionimpossible", 17227, "Mission Impossible" },
-	{ "be849c5747c7fc3b201984afb4403b8e", "voodoocastle", 18140, "Voodoo Castle" },
-	{ "85b75b6079b5ee572b5259b29a0e5d21", "thecount", 19999, "The Count" },
-	{ "c423cae841ac1927b5b2e503607b21bc", "strangeodyssey", 20115, "Strange Odyssey" },
-	{ "326b98b991d401605074e64d474ce566", "mysteryfunhouse", 19700, "Mystery Fun House" },
-	{ "8ef9010399f055da9adb15ce7745a11c", "pyramidofdoom", 20320, "Pyramid Of Doom" },
-	{ "fcdcca8b2acf76ba2d0006cefa3630a1", "ghosttown", 20687, "Ghost Town" },
-	{ "c8aaa80f07c40fa8e4b17432644919dc", "savageisland1", 22669, "Savage Island, Part 1" },
-	{ "2add0f28d9b236c866890cdf8d86ee60", "savageisland2", 21169, "Savage Island, Part 2" },
-	{ "675126bd0477e8ed9230ad3db5afc45f", "goldenvoyage", 21401, "The Golden Voyage" },
-	{ "0ef0def798d895ed766041fa99dd28a0", "adventure13", 22346, "Adventure 13" },
-	{ "0bf1bcc649422798332a38c88588fdff", "adventure14", 22087, "Adventure 14" },
-	{ "a0a5423967287dae9cbeb9abe8324479", "buckaroobonzai", 21038, "Buckaroo Banzai" },
-	{ nullptr, nullptr, 0, nullptr }
-};
-
 bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
 	Common::File gameFile;
 	Common::String md5;
diff --git a/engines/glk/scott/detection_tables.cpp b/engines/glk/scott/detection_tables.cpp
new file mode 100644
index 0000000..f9a2a6c
--- /dev/null
+++ b/engines/glk/scott/detection_tables.cpp
@@ -0,0 +1,85 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/scott/detection_tables.h"
+
+namespace Glk {
+namespace Scott {
+	
+const char *const ADVENTURELAND_DESC = "Adventureland";
+const char *const PIRATE_ADVENTURE_DESC = "Pirate Adventure";
+const char *const MISSION_IMPOSSIBLE_DESC = "Mission Impossible";
+const char *const VOODOO_CASTLE_DESC = "Voodoo Castle";
+const char *const THE_COUNT_DESC = "The Count";
+const char *const STRANGE_ODYSSEY_DESC = "Strange Odyssey";
+const char *const MYSTERY_FUN_HOUSE_DESC = "Mystery Fun House";
+const char *const PYRAMID_OF_DOOM_DESC = "Pyramid Of Doom";
+const char *const GHOST_TOWN_DESC = "Ghost Town";
+const char *const SAVAGE_ISLAND1_DESC = "Savage Island, Part 1";
+const char *const SAVAGE_ISLAND2_DESC = "Savage Island, Part 2";
+const char *const THE_GOLDEN_VOYAGE_DESC = "The Golden Voyage";
+const char *const ADVENTURE13_DESC = "Adventure 13";
+const char *const ADVENTURE14_DESC = "Adventure 14";
+const char *const BUCKAROO_BANZAI_DESC = "Buckaroo Banzai";
+const char *const MARVEL_ADVENTURE_DESC = "Marvel Adventure #1";
+const char *const MINI_SAMPLER_DESC = "Adventure International's Mini-Adventure Sampler";
+
+const ScottGame SCOTT_GAMES[] = {
+	// PC game versions
+	{ "7c6f495d757a54e73d259efc718d8024", "adventureland",     15896, ADVENTURELAND_DESC },
+	{ "ea535fa7684508410151b4561de1f323", "pirateadventure",   16325, PIRATE_ADVENTURE_DESC },
+	{ "379c77a9a483886366b3b5c425e56410", "missionimpossible", 15275, MISSION_IMPOSSIBLE_DESC },
+	{ "a530a6857d1092eaa177eee575c94c71", "voodoocastle",      15852, VOODOO_CASTLE_DESC },
+	{ "5ebb4ade985670bb2eac54f8fa202214", "thecount",          17476, THE_COUNT_DESC },
+	{ "c57bb6df04dc77a2b232bc5bcab6e417", "strangeodyssey",    17489, STRANGE_ODYSSEY_DESC },
+	{ "ce2931ac3d5cbc270a5cb7be9e614f6e", "mysteryfunhouse",   17165, MYSTERY_FUN_HOUSE_DESC },
+	{ "4e6127fad6b5d75eccd3f3b101f8c9c8", "pyramidofdoom",     17673, PYRAMID_OF_DOOM_DESC },
+	{ "2c08327ab06d5490bd9e367ddaeca627", "ghosttown",         17831, GHOST_TOWN_DESC },
+	{ "8feb77f11d32e9567ce2fc7d435eaf44", "savageisland1",     19533, SAVAGE_ISLAND1_DESC },
+	{ "20c40a349f7a214ac515fb1d63c30a87", "savageisland2",     18367, SAVAGE_ISLAND2_DESC },
+	{ "e2a8f956ab215012d1495550c4c11ee8", "goldenvoyage",      18513, THE_GOLDEN_VOYAGE_DESC },
+	{ "f986d7e1ee074f65b6c1d00461c9b3c3", "adventure13",       19232, ADVENTURE13_DESC },
+	{ "6d98f422cc986d959a3c74351785aea3", "adventure14",       19013, ADVENTURE14_DESC },
+	{ "aadcc04e6b37eb9d30a58b5bc775842e", "marveladventure",   18876, MARVEL_ADVENTURE_DESC },
+	{ "d569a769f304dc02b3062d97458ddd01", "scottsampler",      13854, MINI_SAMPLER_DESC },
+
+	// PDA game versions
+	{ "ae541fc1085da2f7d561b72ed20a6bc1", "adventureland",     18003, ADVENTURELAND_DESC },
+	{ "cbd47ab4fcfe00231ffd71d52378d410", "pirateadventure",   18482, PIRATE_ADVENTURE_DESC },
+	{ "9251ab2c64e63559d8a6e9e6246760a5", "missionimpossible", 17227, MISSION_IMPOSSIBLE_DESC },
+	{ "be849c5747c7fc3b201984afb4403b8e", "voodoocastle",      18140, VOODOO_CASTLE_DESC },
+	{ "85b75b6079b5ee572b5259b29a0e5d21", "thecount",          19999, THE_COUNT_DESC },
+	{ "c423cae841ac1927b5b2e503607b21bc", "strangeodyssey",    20115, STRANGE_ODYSSEY_DESC },
+	{ "326b98b991d401605074e64d474ce566", "mysteryfunhouse",   19700, MYSTERY_FUN_HOUSE_DESC },
+	{ "8ef9010399f055da9adb15ce7745a11c", "pyramidofdoom",     20320, PYRAMID_OF_DOOM_DESC },
+	{ "fcdcca8b2acf76ba2d0006cefa3630a1", "ghosttown",         20687, GHOST_TOWN_DESC },
+	{ "c8aaa80f07c40fa8e4b17432644919dc", "savageisland1",     22669, SAVAGE_ISLAND1_DESC },
+	{ "2add0f28d9b236c866890cdf8d86ee60", "savageisland2",     21169, SAVAGE_ISLAND2_DESC },
+	{ "675126bd0477e8ed9230ad3db5afc45f", "goldenvoyage",      21401, THE_GOLDEN_VOYAGE_DESC },
+	{ "0ef0def798d895ed766041fa99dd28a0", "adventure13",       22346, ADVENTURE13_DESC },
+	{ "0bf1bcc649422798332a38c88588fdff", "adventure14",       22087, ADVENTURE14_DESC },
+	{ "a0a5423967287dae9cbeb9abe8324479", "buckaroobanzai",    21038, BUCKAROO_BANZAI_DESC },
+	{ nullptr, nullptr, 0, nullptr }
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
diff --git a/engines/glk/scott/detection_tables.h b/engines/glk/scott/detection_tables.h
new file mode 100644
index 0000000..6365bde
--- /dev/null
+++ b/engines/glk/scott/detection_tables.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/advancedDetector.h"
+#include "common/language.h"
+
+namespace Glk {
+namespace Scott {
+
+/**
+ * Game descriptor for Scott Adams games
+ */
+struct ScottGame {
+	const char *_md5;
+	const char *_gameId;
+	int32 _filesize;
+	const char *_desc;
+};
+
+extern const ScottGame SCOTT_GAMES[];
+extern const char *const ADVENTURELAND_DESC;
+extern const char *const PIRATE_ADVENTURE_DESC;
+extern const char *const MISSION_IMPOSSIBLE_DESC;
+extern const char *const VOODOO_CASTLE_DESC;
+extern const char *const THE_COUNT_DESC;
+extern const char *const STRANGE_ODYSSEY_DESC;
+extern const char *const MYSTERY_FUN_HOUSE_DESC;
+extern const char *const PYRAMID_OF_DOOM_DESC;
+extern const char *const GHOST_TOWN_DESC;
+extern const char *const SAVAGE_ISLAND1_DESC;
+extern const char *const SAVAGE_ISLAND2_DESC;
+extern const char *const THE_GOLDEN_VOYAGE_DESC;
+extern const char *const ADVENTURE13_DESC;
+extern const char *const ADVENTURE14_DESC;
+extern const char *const BUCKAROO_BANZAI_DESC;
+extern const char *const MARVEL_ADVENTURE_DESC;
+extern const char *const MINI_SAMPLER_DESC;
+
+} // End of namespace Frotz
+} // End of namespace Glk


Commit: 67275924b5255d7503fe8bd15c853cfec197c569
    https://github.com/scummvm/scummvm/commit/67275924b5255d7503fe8bd15c853cfec197c569
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Derive detection from MetaEngine rather than AdvancedMetaEngine

Changed paths:
  R engines/glk/detection_tables.h
  R engines/glk/frotz/detection_tables.cpp
  R engines/glk/scott/detection_tables.cpp
    engines/glk/detection.cpp
    engines/glk/frotz/detection.cpp
    engines/glk/frotz/detection.h
    engines/glk/frotz/detection_tables.h
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/frotz.h
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/glk_interface.h
    engines/glk/frotz/processor.cpp
    engines/glk/frotz/processor.h
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/glk_api.cpp
    engines/glk/glk_api.h
    engines/glk/module.mk
    engines/glk/scott/detection.cpp
    engines/glk/scott/detection.h
    engines/glk/scott/detection_tables.h
    engines/glk/scott/scott.cpp
    engines/glk/scott/scott.h


diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index c3b6865..b3cbccc 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -21,6 +21,10 @@
  */
 
 #include "glk/glk.h"
+#include "glk/frotz/detection.h"
+#include "glk/frotz/frotz.h"
+#include "glk/scott/detection.h"
+#include "glk/scott/scott.h"
 
 #include "base/plugins.h"
 #include "common/md5.h"
@@ -31,128 +35,14 @@
 #include "engines/advancedDetector.h"
 #include "graphics/colormasks.h"
 #include "graphics/surface.h"
-
-#define MAX_SAVES 99
-
-namespace Glk {
-
-struct GlkGameDescription {
-	ADGameDescription _desc;
-	Common::String _filename;
-	InterpreterType _interpType;
-	Common::String _md5;
-};
-
-const Common::String &GlkEngine::getFilename() const {
-	return _gameDescription->_filename;
-}
-uint32 GlkEngine::getFeatures() const {
-	return _gameDescription->_desc.flags;
-}
-
-bool GlkEngine::isDemo() const {
-	return (bool)(_gameDescription->_desc.flags & ADGF_DEMO);
-}
-
-Common::Language GlkEngine::getLanguage() const {
-	return _gameDescription->_desc.language;
-}
-
-InterpreterType GlkEngine::getInterpreterType() const {
-	return _gameDescription->_interpType;
-}
-
-const Common::String &GlkEngine::getGameMD5() const {
-	return _gameDescription->_md5;
-}
-
-} // End of namespace Glk
-
-#include "glk/scott/detection_tables.h"
-#include "glk/frotz/detection_tables.h"
-#define SCOTT(ID, NAME) { ID, Glk::Scott::NAME##_DESC }
-#define ZCODE(ID, NAME) { ID, Glk::Frotz::NAME##_DESC }
-
-static const PlainGameDescriptor glkGames[] = {
-	{"zcode", "Zcode Games" },
-	{"scottadams", "Scott Adams Games"},
-
-	// Infocom/Z-code games
-	ZCODE("amfv", AMFV),
-	ZCODE("arthur", ARTHUR),
-	ZCODE("ballyhoo", BALLYHOO),
-	ZCODE("beyondzork", BEYONDZORK),
-	ZCODE("borderzone", BORDERZONE),
-	ZCODE("bureaucracy", BUREAUCRACY),
-	ZCODE("cutthroats", CUTTHROATS),
-	ZCODE("deadline", DEADLINE),
-	ZCODE("enchanter", ENCHANTER),
-	ZCODE("hhgttg", HHGTTG),
-	ZCODE("hijinx", HIJINX),
-	ZCODE("infidel", INFIDEL),
-	ZCODE("journey", JOURNEY),
-	ZCODE("lgop", LGOP),
-	ZCODE("lgop2", LGOP2),
-	ZCODE("lurking", LURKING),
-	ZCODE("minizork1", MINIZORK1),
-	ZCODE("moonmist", MOONMIST),
-	ZCODE("nordbert", NORDBERT),
-	ZCODE("planetfall", PLANETFALL),
-	ZCODE("plundered", PLUNDERED),
-	ZCODE("sampler1", SAMPLER1),
-	ZCODE("sampler2", SAMPLER2),
-	ZCODE("seastalker", SEASTALKER),
-	ZCODE("sherlockriddle", SHERLOCKRIDDLE),
-	ZCODE("shogun", SHOGUN),
-	ZCODE("sorcerer", SORCERER),
-	ZCODE("spellbreaker", SPELLBREAKER),
-	ZCODE("starcross", STARCROSS),
-	ZCODE("stationfall", STATIONFALL),
-	ZCODE("suspect", SUSPECT),
-	ZCODE("suspended", SUSPENDED),
-	ZCODE("trinity", TRINITY),
-	ZCODE("wishbringer", WISHBRINGER),
-	ZCODE("witness", WITNESS),
-	ZCODE("zork0", ZORK0),
-	ZCODE("zork1", ZORK1),
-	ZCODE("zork2", ZORK2),
-	ZCODE("zork3", ZORK3),
-	ZCODE("ztuu", ZTUU),
-
-	// Scott Adams games
-	SCOTT("adventureland", ADVENTURELAND),
-	SCOTT("pirateadventure", PIRATE_ADVENTURE),
-	SCOTT("missionimpossible", MISSION_IMPOSSIBLE),
-	SCOTT("voodoocastle", VOODOO_CASTLE),
-	SCOTT("thecount", THE_COUNT),
-	SCOTT("strangeodyssey", STRANGE_ODYSSEY),
-	SCOTT("mysteryfunhouse", MYSTERY_FUN_HOUSE),
-	SCOTT("pyramidofdoom", PYRAMID_OF_DOOM),
-	SCOTT("ghosttown", GHOST_TOWN),
-	SCOTT("savageisland1", SAVAGE_ISLAND1),
-	SCOTT("savageisland2", SAVAGE_ISLAND2),
-	SCOTT("goldenvoyage", THE_GOLDEN_VOYAGE),
-	SCOTT("adventure13", ADVENTURE13),
-	SCOTT("adventure14", ADVENTURE14),
-	SCOTT("buckaroobanzai", BUCKAROO_BANZAI),
-	SCOTT("marveladventure", MARVEL_ADVENTURE),
-	SCOTT("scottsampler", MINI_SAMPLER),
-	{0, 0}
-};
-
 #include "common/config-manager.h"
 #include "common/file.h"
-#include "glk/detection_tables.h"
-#include "glk/frotz/detection.h"
-#include "glk/frotz/frotz.h"
-#include "glk/scott/detection.h"
-#include "glk/scott/scott.h"
 
-class GlkMetaEngine : public AdvancedMetaEngine {
+#define MAX_SAVES 99
+
+class GlkMetaEngine : public MetaEngine {
 public:
-	GlkMetaEngine() : AdvancedMetaEngine(Glk::gameDescriptions, sizeof(Glk::GlkGameDescription), glkGames) {
-		_maxScanDepth = 3;
-	}
+	GlkMetaEngine() : MetaEngine() {}
 
 	virtual const char *getName() const {
 		return "ScummGlk Engine";
@@ -163,15 +53,28 @@ public:
 	}
 
 	virtual bool hasFeature(MetaEngineFeature f) const override;
-	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
+	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const override;
 	virtual SaveStateList listSaves(const char *target) const;
 	virtual int getMaximumSaveSlot() const;
 	virtual void removeSaveState(const char *target, int slot) const;
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
 
+	/**
+	 * Returns a list of games supported by this engine.
+	 */
+	virtual PlainGameList getSupportedGames() const override;
+
+	/**
+	 * Runs the engine's game detector on the given list of files, and returns a
+	 * (possibly empty) list of games supported by the engine which it was able
+	 * to detect amongst the given files.
+	 */
 	virtual DetectedGames detectGames(const Common::FSList &fslist) const override;
 
-	virtual ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const override;
+	/**
+	 * Query the engine for a PlainGameDescriptor for the specified gameid, if any.
+	 */
+	virtual PlainGameDescriptor findGame(const char *gameId) const override;
 };
 
 bool GlkMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -192,21 +95,49 @@ bool Glk::GlkEngine::hasFeature(EngineFeature f) const {
 	    (f == kSupportsSavingDuringRuntime);
 }
 
-bool GlkMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
-	const Glk::GlkGameDescription *gd = (const Glk::GlkGameDescription *)desc;
-
-	switch (gd->_interpType) {
-	case Glk::INTERPRETER_FROTZ:
-		*engine = new Glk::Frotz::Frotz(syst, gd);
-		break;
-	case Glk::INTERPRETER_SCOTT:
-		*engine = new Glk::Scott::Scott(syst, gd);
-		break;
-	default:
-		error("Unknown interpreter");
-	}
+Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
+	assert(engine);
 
-	return gd != 0;
+	Glk::GlkGameDescription gameDesc;
+	gameDesc._gameId = ConfMan.get("gameid");
+	gameDesc._filename = ConfMan.get("filename");
+
+	if (Glk::Frotz::FrotzMetaEngine::findGame(gameDesc._gameId.c_str()).description)
+		*engine = new Glk::Frotz::Frotz(syst, gameDesc);
+	else if (Glk::Scott::ScottMetaEngine::findGame(gameDesc._gameId.c_str()).description)
+		*engine = new Glk::Scott::Scott(syst, gameDesc);
+	else
+		return Common::kNoGameDataFoundError;
+
+	return Common::kNoError;
+}
+
+PlainGameList GlkMetaEngine::getSupportedGames() const {
+	PlainGameList list;
+	Glk::Frotz::FrotzMetaEngine::getSupportedGames(list);
+	Glk::Scott::ScottMetaEngine::getSupportedGames(list);
+
+	return list;
+}
+
+PlainGameDescriptor GlkMetaEngine::findGame(const char *gameId) const {
+	PlainGameDescriptor gd;
+
+	gd = Glk::Frotz::FrotzMetaEngine::findGame(gameId);
+	if (gd.description) return gd;
+
+	gd = Glk::Scott::ScottMetaEngine::findGame(gameId);
+	if (gd.description) return gd;
+
+	return PlainGameDescriptor();
+}
+
+DetectedGames GlkMetaEngine::detectGames(const Common::FSList &fslist) const {
+	DetectedGames detectedGames;
+	Glk::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
+	Glk::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
+
+	return detectedGames;
 }
 
 SaveStateList GlkMetaEngine::listSaves(const char *target) const {
@@ -270,54 +201,6 @@ SaveStateDescriptor GlkMetaEngine::querySaveMetaInfos(const char *target, int sl
 	return SaveStateDescriptor();
 }
 
-DetectedGames GlkMetaEngine::detectGames(const Common::FSList &fslist) const {
-	DetectedGames detectedGames;
-	Glk::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
-	Glk::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
-
-	return detectedGames;
-}
-
-static Glk::GlkGameDescription gameDescription;
-
-ADDetectedGames GlkMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
-	static char gameId[100];
-	strcpy(gameId, ConfMan.get("gameid").c_str());
-	Common::String filename = ConfMan.get("filename");
-
-	Common::FSList fslist;
-	DetectedGames detectedGames;
-	fslist.push_back(parent.getChild(filename));
-	ADDetectedGames results;
-	Common::File f;
-
-	// Check each sub-engine for any detected games
-	if (Glk::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames))
-		gameDescription._interpType = Glk::INTERPRETER_FROTZ;
-	else if (Glk::Scott::ScottMetaEngine::detectGames(fslist, detectedGames))
-		gameDescription._interpType = Glk::INTERPRETER_SCOTT;
-	else
-		// No match found, so return no results
-		return results;
-
-	// Set up the game description and return it
-	if (f.open(parent.getChild(filename))) {
-		DetectedGame gd = detectedGames.front();
-
-		gameDescription._desc.gameId = gameId;
-		gameDescription._desc.language = gd.language;
-		gameDescription._desc.platform = gd.platform;
-		gameDescription._desc.guiOptions = GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES);
-		gameDescription._filename = filename;
-		gameDescription._md5 = Common::computeStreamMD5AsString(f, 5000);
-
-		ADDetectedGame dg((ADGameDescription *)&gameDescription);
-		results.push_back(dg);
-	}
-
-	return results;
-}
-
 #if PLUGIN_ENABLED_DYNAMIC(GLK)
 REGISTER_PLUGIN_DYNAMIC(GLK, PLUGIN_TYPE_ENGINE, GlkMetaEngine);
 #else
diff --git a/engines/glk/detection_tables.h b/engines/glk/detection_tables.h
deleted file mode 100644
index 47a761e..0000000
--- a/engines/glk/detection_tables.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-namespace Glk {
-
-static const GlkGameDescription gameDescriptions[] = {
-	{ AD_TABLE_END_MARKER, "", (InterpreterType)0, "" }
-};
-
-} // End of namespace Glk
diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index 36303d4..b77344d 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -21,14 +21,27 @@
  */
 
 #include "glk/frotz/detection.h"
+#include "glk/frotz/detection_tables.h"
 #include "common/file.h"
 #include "common/md5.h"
 
-#include "glk/frotz/detection_tables.h"
-
 namespace Glk {
 namespace Frotz {
 
+void FrotzMetaEngine::getSupportedGames(PlainGameList &games) {
+	for (const PlainGameDescriptor *pd = FROTZ_GAME_LIST; pd->gameId; ++pd)
+		games.push_back(*pd);
+}
+
+PlainGameDescriptor FrotzMetaEngine::findGame(const char *gameId) {
+	for (const PlainGameDescriptor *pd = FROTZ_GAME_LIST; pd->gameId; ++pd) {
+		if (!strcmp(gameId, pd->gameId))
+			return *pd;
+	}
+
+	return PlainGameDescriptor();;
+}
+
 bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
 	const char *const EXTENSIONS[9] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb" };
 
@@ -66,7 +79,8 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 			warning("Uknown zcode game %s - %s %d", filename.c_str(), md5.c_str(), filesize);
 			gd = DetectedGame("zcode", "Unrecognised zcode game", Common::UNK_LANG, Common::kPlatformUnknown);
 		} else {
-			gd = DetectedGame(p->_gameId, p->_description, p->_language, Common::kPlatformUnknown, p->_extra);
+			PlainGameDescriptor gameDesc = findGame(p->_gameId);
+			gd = DetectedGame(p->_gameId, gameDesc.description, p->_language, Common::kPlatformUnknown, p->_extra);
 		}
 
 		gd.addExtraEntry("filename", filename);
diff --git a/engines/glk/frotz/detection.h b/engines/glk/frotz/detection.h
index 9cd9373..6c70f98 100644
--- a/engines/glk/frotz/detection.h
+++ b/engines/glk/frotz/detection.h
@@ -32,6 +32,17 @@ namespace Frotz {
 class FrotzMetaEngine {
 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 PlainGameDescriptor findGame(const char *gameId);
+
+	/**
 	 * Detect supported games
 	 */
 	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
diff --git a/engines/glk/frotz/detection_tables.cpp b/engines/glk/frotz/detection_tables.cpp
deleted file mode 100644
index f52b099..0000000
--- a/engines/glk/frotz/detection_tables.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "glk/frotz/detection_tables.h"
-
-namespace Glk {
-namespace Frotz {
-
-const char *const AMFV_DESC = "A Mind Forever Voyaging";
-const char *const ARTHUR_DESC = "Arthur: The Quest for Excalibur";
-const char *const BALLYHOO_DESC = "Ballyhoo";
-const char *const BEYONDZORK_DESC = "Beyond Zork";
-const char *const BORDERZONE_DESC = "Border Zone";
-const char *const BUREAUCRACY_DESC = "Bureaucracy";
-const char *const CUTTHROATS_DESC = "Cutthroats";
-const char *const DEADLINE_DESC = "Deadline";
-const char *const ENCHANTER_DESC = "Enchanter";
-const char *const HHGTTG_DESC = "The Hitchhiker's Guide to the Galaxy";
-const char *const HIJINX_DESC = "Hollywood Hijinx";
-const char *const INFIDEL_DESC = "Infidel";
-const char *const JOURNEY_DESC = "Journey";
-const char *const LGOP_DESC = "Leather Goddesses of Phobos";
-const char *const LGOP2_DESC = "Leather Goddesses of Phobos 2";
-const char *const LURKING_DESC = "The Lurking Horror";
-const char *const MINIZORK1_DESC = "Mini Zork I: The Great Underground Empire";
-const char *const MOONMIST_DESC = "Moonmist";
-const char *const NORDBERT_DESC = "Nord and Bert Couldn't Make Head or Tail of It";
-const char *const PLANETFALL_DESC = "Planetfall";
-const char *const PLUNDERED_DESC = "Plundered Hearts";
-const char *const SAMPLER1_DESC = "Infocom Sampler 1";
-const char *const SAMPLER2_DESC = "Infocom Sampler 2";
-const char *const SEASTALKER_DESC = "Seastalker";
-const char *const SHERLOCKRIDDLE_DESC = "Sherlock: The Riddle of the Crown Jewels";
-const char *const SHOGUN_DESC = "James Clavell's Shogun";
-const char *const SORCERER_DESC = "Sorcerer";
-const char *const SPELLBREAKER_DESC = "Spellbreaker";
-const char *const STARCROSS_DESC = "Starcross";
-const char *const STATIONFALL_DESC = "Stationfall";
-const char *const SUSPECT_DESC = "Suspect";
-const char *const SUSPENDED_DESC = "Suspended";
-const char *const TRINITY_DESC = "Trinity";
-const char *const WISHBRINGER_DESC = "Wishbringer";
-const char *const WITNESS_DESC = "The Witness";
-const char *const ZORK0_DESC = "Zork Zero: The Revenge of Megaboz";
-const char *const ZORK1_DESC = "Zork I: The Great Underground Empire";
-const char *const ZORK2_DESC = "Zork II: The Wizard of Frobozz";
-const char *const ZORK3_DESC = "Zork III: The Dungeon Master";
-const char *const ZTUU_DESC = "Zork: The Undiscovered Underground";
-
-#define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
-#define ENTRY0(ID, DESCRIPTION, VERSION, MD5, FILESIZE) { ID, DESCRIPTION##_DESC, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
-#define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
-
-const FrotzGameDescription FROTZ_GAMES[] = {
-	ENTRY0("hhgttg", HHGTTG, "v31 Solid Gold", "379022bcd4ec74b90274c6100c33f579", 158412),
-	ENTRY0("hhgttg", HHGTTG, "v47", "fdda8f4239819402c62db866bb61a648", 112622),
-	ENTRY0("hhgttg", HHGTTG, "v56", "a214fcb42bc9f554d07d983a12f6a062", 113444),
-	ENTRY0("hhgttg", HHGTTG, "v58", "e867d49ad1fb9406ff4e0678a4ee2ac9", 113332),
-	ENTRY0("hhgttg", HHGTTG, "v59", "34f6abc1f2a42be127ef434fc475f0ee", 113334),
-
-	FROTZ_TABLE_END_MARKER
-};
-
-
-} // End of namespace Frotz
-} // End of namespace Glk
diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
index 51994ed..5eb310d 100644
--- a/engines/glk/frotz/detection_tables.h
+++ b/engines/glk/frotz/detection_tables.h
@@ -20,7 +20,8 @@
  *
  */
 
-#include "engines/advancedDetector.h"
+#include "engines/game.h"
+#include "common/gui_options.h"
 #include "common/language.h"
 
 namespace Glk {
@@ -31,7 +32,6 @@ namespace Frotz {
  */
 struct FrotzGameDescription {
 	const char *const _gameId;
-	const char *const _description;
 	const char *const _extra;
 	const char *const _md5;
 	size_t _filesize;
@@ -39,47 +39,63 @@ struct FrotzGameDescription {
 	const char *const _guiOptions;
 };
 
-extern const FrotzGameDescription FROTZ_GAMES[];
-extern const char *const AMFV_DESC;
-extern const char *const ARTHUR_DESC;
-extern const char *const BALLYHOO_DESC;
-extern const char *const BEYONDZORK_DESC;
-extern const char *const BORDERZONE_DESC;
-extern const char *const BUREAUCRACY_DESC;
-extern const char *const CUTTHROATS_DESC;
-extern const char *const DEADLINE_DESC;
-extern const char *const ENCHANTER_DESC;
-extern const char *const HHGTTG_DESC;
-extern const char *const HIJINX_DESC;
-extern const char *const INFIDEL_DESC;
-extern const char *const JOURNEY_DESC;
-extern const char *const LGOP_DESC;
-extern const char *const LGOP2_DESC;
-extern const char *const LURKING_DESC;
-extern const char *const MINIZORK1_DESC;
-extern const char *const MOONMIST_DESC;
-extern const char *const NORDBERT_DESC;
-extern const char *const PLANETFALL_DESC;
-extern const char *const PLUNDERED_DESC;
-extern const char *const SAMPLER1_DESC;
-extern const char *const SAMPLER2_DESC;
-extern const char *const SEASTALKER_DESC;
-extern const char *const SHERLOCKRIDDLE_DESC;
-extern const char *const SHOGUN_DESC;
-extern const char *const SORCERER_DESC;
-extern const char *const SPELLBREAKER_DESC;
-extern const char *const STARCROSS_DESC;
-extern const char *const STATIONFALL_DESC;
-extern const char *const SUSPECT_DESC;
-extern const char *const SUSPENDED_DESC;
-extern const char *const TRINITY_DESC;
-extern const char *const WISHBRINGER_DESC;
-extern const char *const WITNESS_DESC;
-extern const char *const ZORK0_DESC;
-extern const char *const ZORK1_DESC;
-extern const char *const ZORK2_DESC;
-extern const char *const ZORK3_DESC;
-extern const char *const ZTUU_DESC;
+const PlainGameDescriptor FROTZ_GAME_LIST[] = {
+	{ "amfv", "A Mind Forever Voyaging" },
+	{ "arthur", "Arthur: The Quest for Excalibur" },
+	{ "ballyhoo", "Ballyhoo" },
+	{ "beyondzork", "Beyond Zork" },
+	{ "borderzone", "Border Zone" },
+	{ "bureaucracy", "Bureaucracy" },
+	{ "cutthroats", "Cutthroats" },
+	{ "deadline", "Deadline" },
+	{ "enchanter", "Enchanter" },
+	{ "hhgttg", "The Hitchhiker's Guide to the Galaxy" },
+	{ "hollywoodhijinx", "Hollywood Hijinx" },
+	{ "infidel", "Infidel" },
+	{ "journey", "Journey" },
+	{ "lgop", "Leather Goddesses of Phobos" },
+	{ "lgop2", "Leather Goddesses of Phobos 2" },
+	{ "lurkinghorror", "The Lurking Horror" },
+	{ "minizork", "Mini Zork I: The Great Underground Empire" },
+	{ "moonmist", "Moonmist" },
+	{ "nordbert", "Nord and Bert Couldn't Make Head or Tail of It" },
+	{ "planetfall", "Planetfall" },
+	{ "plunderedhearts", "Plundered Hearts" },
+	{ "infocomsampler1", "Infocom Sampler 1" },
+	{ "infocomsampler2", "Infocom Sampler 2" },
+	{ "seastalker", "Seastalker" },
+	{ "sherlockriddle", "Sherlock: The Riddle of the Crown Jewels" },
+	{ "shogun", "James Clavell's Shogun" },
+	{ "sorcerer", "Sorcerer" },
+	{ "spellbreaker", "Spellbreaker" },
+	{ "starcross", "Starcross" },
+	{ "stationfall", "Stationfall" },
+	{ "suspect", "Suspect" },
+	{ "suspended", "Suspended" },
+	{ "trinity", "Trinity" },
+	{ "wishbringer", "Wishbringer" },
+	{ "thewitness", "The Witness" },
+	{ "zork0", "Zork Zero: The Revenge of Megaboz" },
+	{ "zork1", "Zork I: The Great Underground Empire" },
+	{ "zork2", "Zork II: The Wizard of Frobozz" },
+	{ "zork3", "Zork III: The Dungeon Master" },
+	{ "ztuu", "Zork: The Undiscovered Underground" },
+	{ nullptr, nullptr }
+};
+
+#define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
+#define ENTRY0(ID, VERSION, MD5, FILESIZE) { ID, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
+#define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
+
+const FrotzGameDescription FROTZ_GAMES[] = {
+	ENTRY0("hhgttg", "v31 Solid Gold", "379022bcd4ec74b90274c6100c33f579", 158412),
+	ENTRY0("hhgttg", "v47", "fdda8f4239819402c62db866bb61a648", 112622),
+	ENTRY0("hhgttg", "v56", "a214fcb42bc9f554d07d983a12f6a062", 113444),
+	ENTRY0("hhgttg", "v58", "e867d49ad1fb9406ff4e0678a4ee2ac9", 113332),
+	ENTRY0("hhgttg", "v59", "34f6abc1f2a42be127ef434fc475f0ee", 113334),
+
+	FROTZ_TABLE_END_MARKER
+};
 
 } // End of namespace Frotz
 } // End of namespace Glk
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index f30acd0..a6371b6 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -29,7 +29,7 @@ namespace Frotz {
 
 Frotz *g_vm;
 
-Frotz::Frotz(OSystem *syst, const GlkGameDescription *gameDesc) :
+Frotz::Frotz(OSystem *syst, const GlkGameDescription &gameDesc) :
 		Processor(syst, gameDesc) {
 	g_vm = this;
 }
diff --git a/engines/glk/frotz/frotz.h b/engines/glk/frotz/frotz.h
index cecd886..e45abc0 100644
--- a/engines/glk/frotz/frotz.h
+++ b/engines/glk/frotz/frotz.h
@@ -36,7 +36,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Frotz(OSystem *syst, const GlkGameDescription *gameDesc);
+	Frotz(OSystem *syst, const GlkGameDescription &gameDesc);
 
 	/**
 	 * Destructor
diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index da27382..b9652bc 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -25,7 +25,7 @@
 namespace Glk {
 namespace Frotz {
 
-GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription *gameDesc) :
+GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc) :
 		GlkAPI(syst, gameDesc),
 		oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
 		curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index d78d49f..8bbdb73 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -181,7 +181,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	GlkInterface(OSystem *syst, const GlkGameDescription *gameDesc);
+	GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc);
 
 	/**
 	 * Initialization
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
index a7cbad0..442b007 100644
--- a/engines/glk/frotz/processor.cpp
+++ b/engines/glk/frotz/processor.cpp
@@ -131,7 +131,7 @@ Opcode Processor::ext_opcodes[64] = {
 	&Processor::z_buffer_screen,	// spec 1.1
 };
 
-Processor::Processor(OSystem *syst, const GlkGameDescription *gameDesc) : 
+Processor::Processor(OSystem *syst, const GlkGameDescription &gameDesc) : 
 		GlkInterface(syst, gameDesc),
 		_finished(0), _sp(nullptr), _fp(nullptr), _frameCount(0),
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index 4bd9d2c..ddb264c 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -1522,7 +1522,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Processor(OSystem *syst, const GlkGameDescription *gameDesc);
+	Processor(OSystem *syst, const GlkGameDescription &gameDesc);
 
 	/**
 	 * Initialization
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index a35f242..987fbc8 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -41,7 +41,7 @@ namespace Glk {
 
 GlkEngine *g_vm;
 
-GlkEngine::GlkEngine(OSystem *syst, const GlkGameDescription *gameDesc) :
+GlkEngine::GlkEngine(OSystem *syst, const GlkGameDescription &gameDesc) :
 	_gameDescription(gameDesc), Engine(syst), _random("Glk"), _clipboard(nullptr),
 	_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr),
 	_selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 92ea2ab..b48d4e6 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -70,7 +70,13 @@ enum GlkDebugChannels {
 
 #define GLK_SAVEGAME_VERSION 1
 
-struct GlkGameDescription;
+struct GlkGameDescription {
+	Common::String _gameId;
+	Common::Language _language;
+	Common::String _filename;
+	InterpreterType _interpType;
+	Common::String _md5;
+};
 
 /**
  * Base class for the different interpreters
@@ -87,7 +93,7 @@ private:
 	 */
 	void initGraphicsMode();
 protected:
-	const GlkGameDescription *_gameDescription;
+	const GlkGameDescription _gameDescription;
 	Common::RandomSource _random;
 	int _loadSaveSlot;
 
@@ -119,7 +125,7 @@ public:
 	void (*gli_unregister_arr)(void *array, glui32 len, const char *typecode, gidispatch_rock_t objrock);
 
 public:
-	GlkEngine(OSystem *syst, const GlkGameDescription *gameDesc);
+	GlkEngine(OSystem *syst, const GlkGameDescription &gameDesc);
 	virtual ~GlkEngine();
 
 	/**
@@ -149,22 +155,22 @@ public:
 	/**
 	 * Returns the language
 	 */
-	Common::Language getLanguage() const;
+	Common::Language getLanguage() const { return _gameDescription._language; };
 
 	/**
 	 * Returns the running interpreter type
 	 */
-	InterpreterType getInterpreterType() const;
+	InterpreterType getInterpreterType() const { return _gameDescription._interpType; }
 
 	/**
 	 * Returns the game's md5
 	 */
-	const Common::String &getGameMD5() const;
+	const Common::String &getGameMD5() const { return _gameDescription._md5; }
 
 	/**
 	 * Returns the primary filename for the game
 	 */
-	const Common::String &getFilename() const;
+	const Common::String &getFilename() const { return _gameDescription._filename; }
 
 	/**
 	 * Return the game engine's target name
diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index c219cd1..6094015 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -34,7 +34,7 @@
 
 namespace Glk {
 
-GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription *gameDesc) :
+GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc) :
 	GlkEngine(syst, gameDesc), _gliFirstEvent(false) {
 	// Set uppercase/lowercase tables
 	int ix, res;
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
index f864ac9..8a37df1 100644
--- a/engines/glk/glk_api.h
+++ b/engines/glk/glk_api.h
@@ -43,7 +43,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	GlkAPI(OSystem *syst, const GlkGameDescription *gameDesc);
+	GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc);
 
 	void glk_exit(void);
 	void glk_set_interrupt_handler(void(*func)(void));
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 0f79cd8..b523034 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -22,7 +22,6 @@ MODULE_OBJS := \
 	window_text_buffer.o \
 	window_text_grid.o \
 	frotz/detection.o \
-	frotz/detection_tables.o \
 	frotz/frotz.o \
 	frotz/glk_interface.o \
 	frotz/mem.o \
@@ -39,7 +38,6 @@ MODULE_OBJS := \
 	frotz/processor_variables.o \
 	frotz/quetzal.o \
 	scott/detection.o \
-	scott/detection_tables.o \
 	scott/scott.o
 
 # This module can be built as a plugin
diff --git a/engines/glk/scott/detection.cpp b/engines/glk/scott/detection.cpp
index 523b02d..a02e3c5 100644
--- a/engines/glk/scott/detection.cpp
+++ b/engines/glk/scott/detection.cpp
@@ -24,10 +24,25 @@
 #include "glk/scott/detection_tables.h"
 #include "common/file.h"
 #include "common/md5.h"
+#include "engines/game.h"
 
 namespace Glk {
 namespace Scott {
 
+void ScottMetaEngine::getSupportedGames(PlainGameList &games) {
+	for (const PlainGameDescriptor *pd = SCOTT_GAME_LIST; pd->gameId; ++pd)
+		games.push_back(*pd);
+}
+
+PlainGameDescriptor ScottMetaEngine::findGame(const char *gameId) {
+	for (const PlainGameDescriptor *pd = SCOTT_GAME_LIST; pd->gameId; ++pd) {
+		if (!strcmp(gameId, pd->gameId))
+			return *pd;
+	}
+
+	return PlainGameDescriptor();;
+}
+
 bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
 	Common::File gameFile;
 	Common::String md5;
@@ -48,7 +63,8 @@ bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 
 			if (p->_filesize) {
 				// Found a match
-				DetectedGame gd(p->_gameId, p->_desc, Common::EN_ANY, Common::kPlatformUnknown);
+				PlainGameDescriptor gameDesc = findGame(p->_gameId);
+				DetectedGame gd(p->_gameId, gameDesc.description, Common::EN_ANY, Common::kPlatformUnknown);
 				gd.addExtraEntry("filename", file->getName());
 
 				gameList.push_back(gd);
diff --git a/engines/glk/scott/detection.h b/engines/glk/scott/detection.h
index 2c187cd..384197c 100644
--- a/engines/glk/scott/detection.h
+++ b/engines/glk/scott/detection.h
@@ -32,6 +32,16 @@ namespace Scott {
 class ScottMetaEngine {
 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 PlainGameDescriptor findGame(const char *gameId);
+
+	/**
 	 * Detect supported games
 	 */
 	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
diff --git a/engines/glk/scott/detection_tables.cpp b/engines/glk/scott/detection_tables.cpp
deleted file mode 100644
index f9a2a6c..0000000
--- a/engines/glk/scott/detection_tables.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "glk/scott/detection_tables.h"
-
-namespace Glk {
-namespace Scott {
-	
-const char *const ADVENTURELAND_DESC = "Adventureland";
-const char *const PIRATE_ADVENTURE_DESC = "Pirate Adventure";
-const char *const MISSION_IMPOSSIBLE_DESC = "Mission Impossible";
-const char *const VOODOO_CASTLE_DESC = "Voodoo Castle";
-const char *const THE_COUNT_DESC = "The Count";
-const char *const STRANGE_ODYSSEY_DESC = "Strange Odyssey";
-const char *const MYSTERY_FUN_HOUSE_DESC = "Mystery Fun House";
-const char *const PYRAMID_OF_DOOM_DESC = "Pyramid Of Doom";
-const char *const GHOST_TOWN_DESC = "Ghost Town";
-const char *const SAVAGE_ISLAND1_DESC = "Savage Island, Part 1";
-const char *const SAVAGE_ISLAND2_DESC = "Savage Island, Part 2";
-const char *const THE_GOLDEN_VOYAGE_DESC = "The Golden Voyage";
-const char *const ADVENTURE13_DESC = "Adventure 13";
-const char *const ADVENTURE14_DESC = "Adventure 14";
-const char *const BUCKAROO_BANZAI_DESC = "Buckaroo Banzai";
-const char *const MARVEL_ADVENTURE_DESC = "Marvel Adventure #1";
-const char *const MINI_SAMPLER_DESC = "Adventure International's Mini-Adventure Sampler";
-
-const ScottGame SCOTT_GAMES[] = {
-	// PC game versions
-	{ "7c6f495d757a54e73d259efc718d8024", "adventureland",     15896, ADVENTURELAND_DESC },
-	{ "ea535fa7684508410151b4561de1f323", "pirateadventure",   16325, PIRATE_ADVENTURE_DESC },
-	{ "379c77a9a483886366b3b5c425e56410", "missionimpossible", 15275, MISSION_IMPOSSIBLE_DESC },
-	{ "a530a6857d1092eaa177eee575c94c71", "voodoocastle",      15852, VOODOO_CASTLE_DESC },
-	{ "5ebb4ade985670bb2eac54f8fa202214", "thecount",          17476, THE_COUNT_DESC },
-	{ "c57bb6df04dc77a2b232bc5bcab6e417", "strangeodyssey",    17489, STRANGE_ODYSSEY_DESC },
-	{ "ce2931ac3d5cbc270a5cb7be9e614f6e", "mysteryfunhouse",   17165, MYSTERY_FUN_HOUSE_DESC },
-	{ "4e6127fad6b5d75eccd3f3b101f8c9c8", "pyramidofdoom",     17673, PYRAMID_OF_DOOM_DESC },
-	{ "2c08327ab06d5490bd9e367ddaeca627", "ghosttown",         17831, GHOST_TOWN_DESC },
-	{ "8feb77f11d32e9567ce2fc7d435eaf44", "savageisland1",     19533, SAVAGE_ISLAND1_DESC },
-	{ "20c40a349f7a214ac515fb1d63c30a87", "savageisland2",     18367, SAVAGE_ISLAND2_DESC },
-	{ "e2a8f956ab215012d1495550c4c11ee8", "goldenvoyage",      18513, THE_GOLDEN_VOYAGE_DESC },
-	{ "f986d7e1ee074f65b6c1d00461c9b3c3", "adventure13",       19232, ADVENTURE13_DESC },
-	{ "6d98f422cc986d959a3c74351785aea3", "adventure14",       19013, ADVENTURE14_DESC },
-	{ "aadcc04e6b37eb9d30a58b5bc775842e", "marveladventure",   18876, MARVEL_ADVENTURE_DESC },
-	{ "d569a769f304dc02b3062d97458ddd01", "scottsampler",      13854, MINI_SAMPLER_DESC },
-
-	// PDA game versions
-	{ "ae541fc1085da2f7d561b72ed20a6bc1", "adventureland",     18003, ADVENTURELAND_DESC },
-	{ "cbd47ab4fcfe00231ffd71d52378d410", "pirateadventure",   18482, PIRATE_ADVENTURE_DESC },
-	{ "9251ab2c64e63559d8a6e9e6246760a5", "missionimpossible", 17227, MISSION_IMPOSSIBLE_DESC },
-	{ "be849c5747c7fc3b201984afb4403b8e", "voodoocastle",      18140, VOODOO_CASTLE_DESC },
-	{ "85b75b6079b5ee572b5259b29a0e5d21", "thecount",          19999, THE_COUNT_DESC },
-	{ "c423cae841ac1927b5b2e503607b21bc", "strangeodyssey",    20115, STRANGE_ODYSSEY_DESC },
-	{ "326b98b991d401605074e64d474ce566", "mysteryfunhouse",   19700, MYSTERY_FUN_HOUSE_DESC },
-	{ "8ef9010399f055da9adb15ce7745a11c", "pyramidofdoom",     20320, PYRAMID_OF_DOOM_DESC },
-	{ "fcdcca8b2acf76ba2d0006cefa3630a1", "ghosttown",         20687, GHOST_TOWN_DESC },
-	{ "c8aaa80f07c40fa8e4b17432644919dc", "savageisland1",     22669, SAVAGE_ISLAND1_DESC },
-	{ "2add0f28d9b236c866890cdf8d86ee60", "savageisland2",     21169, SAVAGE_ISLAND2_DESC },
-	{ "675126bd0477e8ed9230ad3db5afc45f", "goldenvoyage",      21401, THE_GOLDEN_VOYAGE_DESC },
-	{ "0ef0def798d895ed766041fa99dd28a0", "adventure13",       22346, ADVENTURE13_DESC },
-	{ "0bf1bcc649422798332a38c88588fdff", "adventure14",       22087, ADVENTURE14_DESC },
-	{ "a0a5423967287dae9cbeb9abe8324479", "buckaroobanzai",    21038, BUCKAROO_BANZAI_DESC },
-	{ nullptr, nullptr, 0, nullptr }
-};
-
-} // End of namespace Frotz
-} // End of namespace Glk
diff --git a/engines/glk/scott/detection_tables.h b/engines/glk/scott/detection_tables.h
index 6365bde..37a0163 100644
--- a/engines/glk/scott/detection_tables.h
+++ b/engines/glk/scott/detection_tables.h
@@ -20,7 +20,8 @@
  *
  */
 
-#include "engines/advancedDetector.h"
+#include "engines/game.h"
+#include "common/gui_options.h"
 #include "common/language.h"
 
 namespace Glk {
@@ -33,27 +34,66 @@ struct ScottGame {
 	const char *_md5;
 	const char *_gameId;
 	int32 _filesize;
-	const char *_desc;
 };
 
-extern const ScottGame SCOTT_GAMES[];
-extern const char *const ADVENTURELAND_DESC;
-extern const char *const PIRATE_ADVENTURE_DESC;
-extern const char *const MISSION_IMPOSSIBLE_DESC;
-extern const char *const VOODOO_CASTLE_DESC;
-extern const char *const THE_COUNT_DESC;
-extern const char *const STRANGE_ODYSSEY_DESC;
-extern const char *const MYSTERY_FUN_HOUSE_DESC;
-extern const char *const PYRAMID_OF_DOOM_DESC;
-extern const char *const GHOST_TOWN_DESC;
-extern const char *const SAVAGE_ISLAND1_DESC;
-extern const char *const SAVAGE_ISLAND2_DESC;
-extern const char *const THE_GOLDEN_VOYAGE_DESC;
-extern const char *const ADVENTURE13_DESC;
-extern const char *const ADVENTURE14_DESC;
-extern const char *const BUCKAROO_BANZAI_DESC;
-extern const char *const MARVEL_ADVENTURE_DESC;
-extern const char *const MINI_SAMPLER_DESC;
+const PlainGameDescriptor SCOTT_GAME_LIST[] = {
+	{ "adventureland",     "Adventureland" },
+	{ "pirateadventure",   "Pirate Adventure" },
+	{ "missionimpossible", "Mission Impossible" },
+	{ "voodoocastle",      "Voodoo Castle" },
+	{ "thecount",          "The Count" },
+	{ "strangeodyssey",    "Strange Odyssey" },
+	{ "mysteryfunhouse",   "Mystery Fun House" },
+	{ "pyramidofdoom",     "Pyramid Of Doom" },
+	{ "ghosttown",         "Ghost Town" },
+	{ "savageisland1",     "Savage Island, Part 1" },
+	{ "savageisland2",     "Savage Island, Part 2" },
+	{ "goldenvoyage",      "The Golden Voyage" },
+	{ "adventure13",       "Adventure 13" },
+	{ "adventure14",       "Adventure 14" },
+	{ "buckaroobanzai",    "Buckaroo Banzai" },
+	{ "marveladventure",   "Marvel Adventure #1" },
+	{ "scottsampler",      "Adventure International's Mini-Adventure Sampler" },
+	{ nullptr, nullptr }
+};
+
+const ScottGame SCOTT_GAMES[] = {
+	// PC game versions
+	{ "7c6f495d757a54e73d259efc718d8024", "adventureland",     15896 },
+	{ "ea535fa7684508410151b4561de1f323", "pirateadventure",   16325 },
+	{ "379c77a9a483886366b3b5c425e56410", "missionimpossible", 15275 },
+	{ "a530a6857d1092eaa177eee575c94c71", "voodoocastle",      15852 },
+	{ "5ebb4ade985670bb2eac54f8fa202214", "thecount",          17476 },
+	{ "c57bb6df04dc77a2b232bc5bcab6e417", "strangeodyssey",    17489 },
+	{ "ce2931ac3d5cbc270a5cb7be9e614f6e", "mysteryfunhouse",   17165 },
+	{ "4e6127fad6b5d75eccd3f3b101f8c9c8", "pyramidofdoom",     17673 },
+	{ "2c08327ab06d5490bd9e367ddaeca627", "ghosttown",         17831 },
+	{ "8feb77f11d32e9567ce2fc7d435eaf44", "savageisland1",     19533 },
+	{ "20c40a349f7a214ac515fb1d63c30a87", "savageisland2",     18367 },
+	{ "e2a8f956ab215012d1495550c4c11ee8", "goldenvoyage",      18513 },
+	{ "f986d7e1ee074f65b6c1d00461c9b3c3", "adventure13",       19232 },
+	{ "6d98f422cc986d959a3c74351785aea3", "adventure14",       19013 },
+	{ "aadcc04e6b37eb9d30a58b5bc775842e", "marveladventure",   18876 },
+	{ "d569a769f304dc02b3062d97458ddd01", "scottsampler",      13854 },
+
+	// PDA game versions
+	{ "ae541fc1085da2f7d561b72ed20a6bc1", "adventureland",     18003 },
+	{ "cbd47ab4fcfe00231ffd71d52378d410", "pirateadventure",   18482 },
+	{ "9251ab2c64e63559d8a6e9e6246760a5", "missionimpossible", 17227 },
+	{ "be849c5747c7fc3b201984afb4403b8e", "voodoocastle",      18140 },
+	{ "85b75b6079b5ee572b5259b29a0e5d21", "thecount",          19999 },
+	{ "c423cae841ac1927b5b2e503607b21bc", "strangeodyssey",    20115 },
+	{ "326b98b991d401605074e64d474ce566", "mysteryfunhouse",   19700 },
+	{ "8ef9010399f055da9adb15ce7745a11c", "pyramidofdoom",     20320 },
+	{ "fcdcca8b2acf76ba2d0006cefa3630a1", "ghosttown",         20687 },
+	{ "c8aaa80f07c40fa8e4b17432644919dc", "savageisland1",     22669 },
+	{ "2add0f28d9b236c866890cdf8d86ee60", "savageisland2",     21169 },
+	{ "675126bd0477e8ed9230ad3db5afc45f", "goldenvoyage",      21401 },
+	{ "0ef0def798d895ed766041fa99dd28a0", "adventure13",       22346 },
+	{ "0bf1bcc649422798332a38c88588fdff", "adventure14",       22087 },
+	{ "a0a5423967287dae9cbeb9abe8324479", "buckaroobanzai",    21038 },
+	{ nullptr, nullptr, 0 }
+};
 
 } // End of namespace Frotz
 } // End of namespace Glk
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index 2def1a2..6fedab1 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -26,7 +26,7 @@
 namespace Glk {
 namespace Scott {
 
-Scott::Scott(OSystem *syst, const GlkGameDescription *gameDesc) : GlkAPI(syst, gameDesc),
+Scott::Scott(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
 	Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
 	Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
 	split_screen(true), Bottom(0), Top(0), BitFlags(0), _saveSlot(-1) {
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 6899485..cf5f7bd 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -167,7 +167,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Scott(OSystem *syst, const GlkGameDescription *gameDesc);
+	Scott(OSystem *syst, const GlkGameDescription &gameDesc);
 
 	/**
 	 * Execute the game


Commit: eff0ac80ff312d4e20fdcafa8d15971ba2183ce2
    https://github.com/scummvm/scummvm/commit/eff0ac80ff312d4e20fdcafa8d15971ba2183ce2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Handle starting the engine directly from the command line

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


diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index b3cbccc..e6a129f 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -41,6 +41,8 @@
 #define MAX_SAVES 99
 
 class GlkMetaEngine : public MetaEngine {
+private:
+	Common::String findFileByGameId(const Common::String &gameId) const;
 public:
 	GlkMetaEngine() : MetaEngine() {}
 
@@ -98,10 +100,27 @@ bool Glk::GlkEngine::hasFeature(EngineFeature f) const {
 Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
 	assert(engine);
 
+	// Populate the game description
 	Glk::GlkGameDescription gameDesc;
 	gameDesc._gameId = ConfMan.get("gameid");
 	gameDesc._filename = ConfMan.get("filename");
 
+	gameDesc._language = Common::UNK_LANG;
+	gameDesc._platform = Common::kPlatformUnknown;
+	if (ConfMan.hasKey("language"))
+		gameDesc._language = Common::parseLanguage(ConfMan.get("language"));
+	if (ConfMan.hasKey("platform"))
+		gameDesc._platform = Common::parsePlatform(ConfMan.get("platform"));
+
+	// If the game description has no filename, the engine has been launched directly from
+	// the command line. Do a scan for supported games for that Id in the game folder
+	if (gameDesc._filename.empty()) {
+		gameDesc._filename = findFileByGameId(gameDesc._gameId);
+		if (gameDesc._filename.empty())
+			return Common::kNoGameDataFoundError;
+	}
+
+	// Correct the correct engine
 	if (Glk::Frotz::FrotzMetaEngine::findGame(gameDesc._gameId.c_str()).description)
 		*engine = new Glk::Frotz::Frotz(syst, gameDesc);
 	else if (Glk::Scott::ScottMetaEngine::findGame(gameDesc._gameId.c_str()).description)
@@ -112,6 +131,28 @@ Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
 	return Common::kNoError;
 }
 
+Common::String GlkMetaEngine::findFileByGameId(const Common::String &gameId) const {
+	// Get the list of files in the folder and return detection against them
+	Common::FSNode folder = Common::FSNode(ConfMan.get("path"));
+	Common::FSList fslist;
+	folder.getChildren(fslist, Common::FSNode::kListFilesOnly);
+	
+	// Iterate over the files
+	for (Common::FSList::iterator i = fslist.begin(); i != fslist.end(); ++i) {
+		// Run a detection on each file in the folder individually
+		Common::FSList singleList;
+		singleList.push_back(*i);
+		DetectedGames games = detectGames(singleList);
+
+		// If a detection was found with the correct game Id, we have a winner
+		if (!games.empty() && games.front().gameId == gameId)
+			return (*i).getName();
+	}
+
+	// No match found
+	return Common::String();
+}
+
 PlainGameList GlkMetaEngine::getSupportedGames() const {
 	PlainGameList list;
 	Glk::Frotz::FrotzMetaEngine::getSupportedGames(list);
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index b48d4e6..8dce78f 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -73,6 +73,7 @@ enum GlkDebugChannels {
 struct GlkGameDescription {
 	Common::String _gameId;
 	Common::Language _language;
+	Common::Platform _platform;
 	Common::String _filename;
 	InterpreterType _interpType;
 	Common::String _md5;


Commit: 7b1b3935b89e119d7a268521adbc39afc605e403
    https://github.com/scummvm/scummvm/commit/7b1b3935b89e119d7a268521adbc39afc605e403
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix remaining Gargoyle references identified by digitall

Changed paths:
    devtools/credits.pl
    dists/scummvm.rc


diff --git a/devtools/credits.pl b/devtools/credits.pl
index 3cfa7b7..25ef73c 100755
--- a/devtools/credits.pl
+++ b/devtools/credits.pl
@@ -624,9 +624,9 @@ begin_credits("Credits");
 				add_person("Eugene Sandulenko", "sev", "");
 			end_section();
 
-			begin_section("Gargoyle");
+			begin_section("ScummGlk");
 				add_person("Paul Gilbert", "dreammaster", "");
-				add_person("Tor Andersson", "", "GLK library");
+				add_person("Tor Andersson", "", "GarGlk library");
 				add_person("Stefan Jokisch", "", "Frotz interpreter");
 				add_person("Alan Cox", "", "ScottFree interpreter");
 			end_section();
diff --git a/dists/scummvm.rc b/dists/scummvm.rc
index 6d7782a..fb45f42 100644
--- a/dists/scummvm.rc
+++ b/dists/scummvm.rc
@@ -34,7 +34,7 @@ cryo.dat               FILE    "dists/engine-data/cryo.dat"
 #if ENABLE_DRASCULA   == STATIC_PLUGIN
 drascula.dat           FILE    "dists/engine-data/drascula.dat"
 #endif
-#if ENABLE_GARGOYLE   == STATIC_PLUGIN
+#if ENABLE_GLK        == STATIC_PLUGIN
 fonts.dat              FILE   "dists/engine-data/fonts.dat"
 #endif
 #if ENABLE_HUGO       == STATIC_PLUGIN


Commit: a1d355d0be8dfe0e3c1227a8f17ada609041ece4
    https://github.com/scummvm/scummvm/commit/a1d355d0be8dfe0e3c1227a8f17ada609041ece4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Move ostream_* flags from GlkInterface to Processor

Changed paths:
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/glk_interface.h
    engines/glk/frotz/processor.cpp
    engines/glk/frotz/processor.h


diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index b9652bc..6a0d510 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -32,10 +32,8 @@ GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc) :
 		curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
 		gos_lower(nullptr), gos_curwin(nullptr), gos_linepending(0), gos_linebuf(nullptr),
 		gos_linewin(nullptr), gos_channel(nullptr), cwin(0), mwin(0), mouse_x(0), mouse_y(0),
-		menu_selected(0), ostream_screen(false), ostream_script(false), ostream_memory(false),
-		ostream_record(false), istream_replay(false), message(false),
-		enable_wrapping(false), enable_scripting(false), enable_scrolling(false),
-		enable_buffering(false), next_sample(0), next_volume(0),
+		menu_selected(0), enable_wrapping(false), enable_scripting(false),
+		enable_scrolling(false), enable_buffering(false), next_sample(0), next_volume(0),
 		_soundLocked(false), _soundPlaying(false) {
 	Common::fill(&statusline[0], &statusline[256], '\0');
 }
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index 8bbdb73..c18ebb8 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -81,14 +81,6 @@ public:
 	int mouse_x;
 	int menu_selected;
 
-	// IO streams
-	bool ostream_screen;
-	bool ostream_script;
-	bool ostream_memory;
-	bool ostream_record;
-	bool istream_replay;
-	bool message;
-
 	// Window attributes
 	bool enable_wrapping;
 	bool enable_scripting;
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
index 442b007..eaa7310 100644
--- a/engines/glk/frotz/processor.cpp
+++ b/engines/glk/frotz/processor.cpp
@@ -137,7 +137,8 @@ Processor::Processor(OSystem *syst, const GlkGameDescription &gameDesc) :
 		zargc(0), _decoded(nullptr), _encoded(nullptr), _resolution(0),
 		_randomInterval(0), _randomCtr(0), first_restart(true), script_valid(false),
 		_bufPos(0), _locked(false), _prevC('\0'), script_width(0),
-		sfp(nullptr), rfp(nullptr), pfp(nullptr) {
+		sfp(nullptr), rfp(nullptr), pfp(nullptr), ostream_screen(true), ostream_script(false),
+		ostream_memory(false), ostream_record(false), istream_replay(false), message(false) {
 	static const Opcode OP0_OPCODES[16] = {
 		&Processor::z_rtrue,
 		&Processor::z_rfalse,
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index ddb264c..1a1dfc5 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -88,6 +88,12 @@ private:
 	// Stream related fields
 	int script_width;
 	strid_t sfp, rfp, pfp;
+	bool ostream_screen;
+	bool ostream_script;
+	bool ostream_memory;
+	bool ostream_record;
+	bool istream_replay;
+	bool message;
 	Common::FixedStack<Redirect, MAX_NESTING> _redirect;
 private:
 	/**


Commit: f29147ba22b994a8985a6831290aa899a262af0c
    https://github.com/scummvm/scummvm/commit/f29147ba22b994a8985a6831290aa899a262af0c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix showing unicode input lines

Changed paths:
    engines/glk/window_text_buffer.cpp
    engines/glk/window_text_grid.cpp


diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index 36d8c6e..21de0d9 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -663,6 +663,7 @@ void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 in
 
 	int pw;
 
+	_lineRequestUni = true;
 	gli_tts_flush();
 
 	// because '>' prompt is ugly without extra space
@@ -693,9 +694,6 @@ void TextBufferWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 in
 		putTextUni(buf, initlen, _inCurs, 0);
 	}
 
-	// WORKAROUND: Mark bottom line as dirty so caret will be drawn
-	_lines[0]._dirty = true;
-
 	_echoLineInput = _echoLineInputBase;
 
 	if (_lineTerminatorsBase && _termCt) {
diff --git a/engines/glk/window_text_grid.cpp b/engines/glk/window_text_grid.cpp
index e16c9bd..3ee8198 100644
--- a/engines/glk/window_text_grid.cpp
+++ b/engines/glk/window_text_grid.cpp
@@ -272,6 +272,8 @@ void TextGridWindow::requestLineEventUni(glui32 *buf, glui32 maxlen, glui32 init
 		return;
 	}
 
+	_lineRequestUni = true;
+
 	if ((int)maxlen > (_width - _curX))
 		maxlen = (_width - _curX);
 


Commit: beddf3853e631c4b3bc5393ee9072c489d1679ea
    https://github.com/scummvm/scummvm/commit/beddf3853e631c4b3bc5393ee9072c489d1679ea
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fill out HHGTTG version serial numbers

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


diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
index 5eb310d..be32aba 100644
--- a/engines/glk/frotz/detection_tables.h
+++ b/engines/glk/frotz/detection_tables.h
@@ -88,11 +88,11 @@ const PlainGameDescriptor FROTZ_GAME_LIST[] = {
 #define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
 
 const FrotzGameDescription FROTZ_GAMES[] = {
-	ENTRY0("hhgttg", "v31 Solid Gold", "379022bcd4ec74b90274c6100c33f579", 158412),
-	ENTRY0("hhgttg", "v47", "fdda8f4239819402c62db866bb61a648", 112622),
-	ENTRY0("hhgttg", "v56", "a214fcb42bc9f554d07d983a12f6a062", 113444),
-	ENTRY0("hhgttg", "v58", "e867d49ad1fb9406ff4e0678a4ee2ac9", 113332),
-	ENTRY0("hhgttg", "v59", "34f6abc1f2a42be127ef434fc475f0ee", 113334),
+	ENTRY0("hhgttg", "R47-840914", "fdda8f4239819402c62db866bb61a648", 112622),
+	ENTRY0("hhgttg", "R56-841221", "a214fcb42bc9f554d07d983a12f6a062", 113444),
+	ENTRY0("hhgttg", "R58-851002", "e867d49ad1fb9406ff4e0678a4ee2ac9", 113332),
+	ENTRY0("hhgttg", "R59-851108", "34f6abc1f2a42be127ef434fc475f0ee", 113334),
+	ENTRY0("hhgttg", "R31-871119", "379022bcd4ec74b90274c6100c33f579", 158412),
 
 	FROTZ_TABLE_END_MARKER
 };


Commit: af081ad322649918c205e03e4dd29991547981e0
    https://github.com/scummvm/scummvm/commit/af081ad322649918c205e03e4dd29991547981e0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Improve fallback detection of Z-code games

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


diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index b77344d..9ac033b 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -77,7 +77,8 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 				continue;
 
 			warning("Uknown zcode game %s - %s %d", filename.c_str(), md5.c_str(), filesize);
-			gd = DetectedGame("zcode", "Unrecognised zcode game", Common::UNK_LANG, Common::kPlatformUnknown);
+			const PlainGameDescriptor &desc = FROTZ_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);
diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
index be32aba..d4d6f7e 100644
--- a/engines/glk/frotz/detection_tables.h
+++ b/engines/glk/frotz/detection_tables.h
@@ -40,6 +40,9 @@ struct FrotzGameDescription {
 };
 
 const PlainGameDescriptor FROTZ_GAME_LIST[] = {
+	{ "zcode", "Unknown Z-code game" },
+
+	// Infocom games
 	{ "amfv", "A Mind Forever Voyaging" },
 	{ "arthur", "Arthur: The Quest for Excalibur" },
 	{ "ballyhoo", "Ballyhoo" },


Commit: d9e7fca83c7025d2a91ac5341fb9082ea2b8a0fb
    https://github.com/scummvm/scummvm/commit/d9e7fca83c7025d2a91ac5341fb9082ea2b8a0fb
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix exiting when application is forcefully closed

Changed paths:
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/processor.cpp
    engines/glk/frotz/processor_streams.cpp


diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index 6a0d510..292409f 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -155,7 +155,7 @@ void GlkInterface::initialize() {
 
 	// Use the ms-dos interpreter number for v6, because that's the
 	// kind of graphics files we understand.  Otherwise, use DEC.
-	h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
+	h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_AMIGA;
 	h_interpreter_version = 'F';
 
 	{
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
index eaa7310..343299e 100644
--- a/engines/glk/frotz/processor.cpp
+++ b/engines/glk/frotz/processor.cpp
@@ -285,7 +285,7 @@ void Processor::interpret() {
 		if (end_of_sound_flag)
 			end_of_sound();
 #endif
-	} while (!_finished);
+	} while (!shouldQuit() && !_finished);
 
 	_finished--;
 }
diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
index 8413f4b..581eb1e 100644
--- a/engines/glk/frotz/processor_streams.cpp
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -182,6 +182,8 @@ continue_input:
 			key = replay_read_input(buf);
 		else
 			key = console_read_input(max, buf, timeout, key != ZC_BAD);
+		if (shouldQuit())
+			return ZC_BAD;
     } while (key == ZC_BAD);
 
     // Copy input line to the command file


Commit: 04f42acca06c705eeb49da50c58ff15f7611a7c1
    https://github.com/scummvm/scummvm/commit/04f42acca06c705eeb49da50c58ff15f7611a7c1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Allow exiting ScumMVM when waiting for a keypress

Changed paths:
    engines/glk/frotz/processor_streams.cpp


diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
index 581eb1e..93b9c0b 100644
--- a/engines/glk/frotz/processor_streams.cpp
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -145,6 +145,8 @@ continue_input:
 			key = replay_read_key();
 		else
 			key = console_read_key(timeout);
+		if (shouldQuit())
+			return ZC_BAD;
     } while (key == ZC_BAD);
 
     // Copy key to the command file


Commit: 625df27cfcbdffcec708692a6f182891ad6b9363
    https://github.com/scummvm/scummvm/commit/625df27cfcbdffcec708692a6f182891ad6b9363
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Adding detection entries for Infocom games

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


diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
index d4d6f7e..a1fdbdf 100644
--- a/engines/glk/frotz/detection_tables.h
+++ b/engines/glk/frotz/detection_tables.h
@@ -57,9 +57,8 @@ const PlainGameDescriptor FROTZ_GAME_LIST[] = {
 	{ "infidel", "Infidel" },
 	{ "journey", "Journey" },
 	{ "lgop", "Leather Goddesses of Phobos" },
-	{ "lgop2", "Leather Goddesses of Phobos 2" },
 	{ "lurkinghorror", "The Lurking Horror" },
-	{ "minizork", "Mini Zork I: The Great Underground Empire" },
+	{ "minizork1", "Mini Zork I: The Great Underground Empire" },
 	{ "moonmist", "Moonmist" },
 	{ "nordbert", "Nord and Bert Couldn't Make Head or Tail of It" },
 	{ "planetfall", "Planetfall" },
@@ -91,11 +90,45 @@ const PlainGameDescriptor FROTZ_GAME_LIST[] = {
 #define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
 
 const FrotzGameDescription FROTZ_GAMES[] = {
+	ENTRY0("amfv", "R77-850814", "b7ffaed0ca4a90450f92b34066133377", 262016),
+	ENTRY0("amfv", "R79-851122", "1e37dbcf7ccc9244dbfc3229796362f4", 262544),
+	ENTRY0("arthur", "R54-890606", "ced2c66d03a49de0e8190b468332f081", 271360),
+	ENTRY0("arthur", "R74-890714", "13d13f375f85a874c82a8ac7ad69dc41", 269200),
+	ENTRY0("ballyhoo", "R97-851218", "7944e832a7d7b34037c7b6791de43dbd", 128556),
+	ENTRY0("beyondzork", "R49-870917", "a5547795def620d0a75a064f9a37ab2d", 261900),
+	ENTRY0("beyondzork", "R51-870923", "73948f415596fa4d9afe442b2c19e61f", 261548),
+	ENTRY0("beyondzork", "R57-871221", "c56cac07a500e5864a994b19286bc07c", 261388),
+	ENTRY0("borderzone", "R9-871008", "189231ed0675f6be3be86856f49211af", 178372),
+	ENTRY0("bureaucracy", "R86-870212", "2bb00311d4c201082cfcd278ae5db921", 243144),
+	ENTRY0("bureaucracy", "R116-870602", "a8ae194257a989ed3d82648a507466f2", 243340),
+	ENTRY0("cutthroats", "R23-840809", "059801d9f90fffeb3645816c37c7eda2", 112558),
+	ENTRY0("deadline", "R22-820809", "1610e84ca2505885566e648c1c525976", 111782),
+	ENTRY0("deadline", "R26-821108", "e1ae6af1098067b86076c34865ae713c", 108372),
+	ENTRY0("deadline", "R27-831006", "166ffb7cabc6b85f210655f371c89c46", 108454),
+	ENTRY0("enchanter", "R10-830810", "7b41d915b4c2e31423d99925e9438aa4", 109126),
+	ENTRY0("enchanter", "R15-831107", "e70f21aad650dd196fa3601cab5e0fc5", 109230),
+	ENTRY0("enchanter", "R16-831118", "46187e0691f6f5ecdd5a336885db6aad", 109234),
+	ENTRY0("enchanter", "R29-860820", "f87cdafad3682ead25cfc473656ff713", 111126),
 	ENTRY0("hhgttg", "R47-840914", "fdda8f4239819402c62db866bb61a648", 112622),
 	ENTRY0("hhgttg", "R56-841221", "a214fcb42bc9f554d07d983a12f6a062", 113444),
 	ENTRY0("hhgttg", "R58-851002", "e867d49ad1fb9406ff4e0678a4ee2ac9", 113332),
 	ENTRY0("hhgttg", "R59-851108", "34f6abc1f2a42be127ef434fc475f0ee", 113334),
 	ENTRY0("hhgttg", "R31-871119", "379022bcd4ec74b90274c6100c33f579", 158412),
+	ENTRY0("hollywoodhijinx", "R37-861215", "7b52824057ae24e098c228c41460ef75", 109650),
+	ENTRY0("infidel", "R22-830916", "38f713e53af720624434529ea780040c", 93556),
+	ENTRY0("journey", "R30-890322", "c9893bc0399080bd3850d4db2120d110", 280472),
+	ENTRY0("journey", "R77-890616", "8a4ab56f62e1b7c918b837794182dbcd", 282176),
+	ENTRY0("journey", "R83-890706", "c33ea33ab8aec6c617734dcfe1211067", 282312),
+	ENTRY0("lgop", "R0", "69b3534570851b90d7f53ebe9d224a6a", 128998),
+	ENTRY0("lgop", "R4-880405", "6bdae7434df7c03f3589ece0bed3317d", 159928),
+	ENTRY0("lgop", "R59-860730", "e81237e220a612c5a93fbcc1fdf85a0a", 129022),
+	ENTRY0("lurkinghorror", "R203", "e2d2505510479fec0405727e3d0abc10", 128986),
+	ENTRY0("lurkinghorror", "R219", "83936d75c2cfd71fb64bf63c4696b9ac", 129704),
+	ENTRY0("lurkinghorror", "R221", "c60cd0bf3c6eda867241378c7cb5464a", 129944),
+	ENTRY0("minizork1", "R34-871124", "0d7700679e5e63dec97f712698610a46", 52216),
+
+
+
 
 	FROTZ_TABLE_END_MARKER
 };


Commit: dee20ace65cb16a30c80c0790462d569f5da627d
    https://github.com/scummvm/scummvm/commit/dee20ace65cb16a30c80c0790462d569f5da627d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added detection entries for more Infocom games

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


diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
index a1fdbdf..8e6da37 100644
--- a/engines/glk/frotz/detection_tables.h
+++ b/engines/glk/frotz/detection_tables.h
@@ -126,7 +126,37 @@ const FrotzGameDescription FROTZ_GAMES[] = {
 	ENTRY0("lurkinghorror", "R219", "83936d75c2cfd71fb64bf63c4696b9ac", 129704),
 	ENTRY0("lurkinghorror", "R221", "c60cd0bf3c6eda867241378c7cb5464a", 129944),
 	ENTRY0("minizork1", "R34-871124", "0d7700679e5e63dec97f712698610a46", 52216),
-
+	ENTRY0("moonmist", "R4-860918", "284797c3025ffaf76aecfa5c2bbffa86", 129002),
+	ENTRY0("moonmist", "R9-861022", "698475de2769c66bc5a1eca600c71561", 128866),
+	ENTRY0("nordbert", "R19-870722", "da1e189e19e3b24b2e35bd41fc32d261", 170284),
+	ENTRY0("planetfall", "R20-830708", "15815c461a8548b7630d2aee46d07cc7", 107958),
+	ENTRY0("planetfall", "R26-831014", "cf6ce61eb2eff9d4f18d7bcba7c12cfb", 108674),
+	ENTRY0("planetfall", "R29-840118", "9facd8b974e658520fb762af4c4789dc", 109052),
+	ENTRY0("planetfall", "R37-851003", "01844816673414c97d21dc003304989b",109398),
+	ENTRY0("planetfall", "R10-880531", "34c69f1d24418fd4d2de195a1d7546c4", 136560),
+	ENTRY0("plunderedhearts", "R26-870730", "fe5b9eb91949d41838166364f1753b10", 128962),
+	ENTRY0("infocomsampler1", "R26-840731", "5483febc51abd55fb5e04c4c97a0b260", 112610),
+	ENTRY0("infocomsampler1", "R53-850407", "47b8b8394e25faec870a798145529688", 126708),
+	ENTRY0("infocomsampler1", "R55-850823", "05d9d1a1c3c73fce9e24ab695ece16c8", 126902),
+	ENTRY0("infocomsampler2", "R97-870601", "201fa230a942df5aa75bb5b5f609e8ce", 125314),
+	ENTRY0("seastalker", "R15-840501", "2f0220b0390deda695e01832a92b5493", 117738),
+	ENTRY0("seastalker", "R15-840522", "050961fa7788c309bbf40accbff2ffdf", 117728),
+	ENTRY0("seastalker", "R16-850515", "eb39dff7beb3589c8581dd2e3569eb78", 117752),
+	ENTRY0("seastalker", "R16-850603", "bccf194b1e823e37db2431b586662773", 117762),
+	ENTRY0("seastalker", "R86-840320", "64fb27e7b9fd682ff4f0d0ec6616a468", 116456),
+	ENTRY0("sherlockriddle", "R21-871214", "69862f7f07a4e977159ea4da7f2f2ba6", 188444),
+	ENTRY0("sherlockriddle", "R26-880127", "2cb2bda2e34eb7f9494cb585720e74cd", 190180),
+	ENTRY0("shogun", "R322-890706", "62cca41feb94082442026f44f3e48e19", 344816),
+	ENTRY0("sorcerer", "R4-840131", "d4a914fdfe90f5cd055a03b9aa24addd", 109734),
+	ENTRY0("sorcerer", "R6-840508", "7ee357c10a9e049fe7c641a4817ee575", 109482),
+	ENTRY0("sorcerer", "R13-851021", "7a076459806eaee72015b2b2882a89dc", 108692),
+	ENTRY0("sorcerer", "R15-851108", "cde88a011d2ba183ff69b47b0d8881c6", 108682),
+	ENTRY0("spellbreaker", "R63-850916", "b7b9eef231dee03fb40a9d98416fa0d5", 128480),
+	ENTRY0("spellbreaker", "R87-860904", "852286847f4cdd790075fa824260ff4e", 128916),
+	ENTRY0("starcross", "R15-820901", "fb2e6d9a0ad5822f3a8d4aec949e4e3c", 84984),
+	ENTRY0("starcross", "R17-821021", "ed1e62e1f0eb9d819be45c076c5729f7", 83792),
+	ENTRY0("stationfall", "R107-870430", "cfadfb66afabaa2971ec9b4ae65266ca", 128934),
+	ENTRY0("suspect", "R14-841005", "3d759ccb19233f51968fa79d7374b393", 118692),
 
 
 


Commit: 238022890edd256182d0bebd894af39a7f6cd79b
    https://github.com/scummvm/scummvm/commit/238022890edd256182d0bebd894af39a7f6cd79b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Debug code to generate detection entries for unknown games

Used it to flesh out the list of Infocom game detection entries

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


diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index 9ac033b..61d9a14 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -22,6 +22,7 @@
 
 #include "glk/frotz/detection.h"
 #include "glk/frotz/detection_tables.h"
+#include "common/debug.h"
 #include "common/file.h"
 #include "common/md5.h"
 
@@ -63,6 +64,11 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 			continue;
 		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
 		size_t filesize = gameFile.size();
+		char serial[7] = "unkown";
+		if (!filename.hasSuffixIgnoreCase(".zblorb")) {
+			gameFile.seek(18);
+			gameFile.read(&serial[0], 6);
+		}
 		gameFile.close();
 
 		// Check for known game
@@ -76,7 +82,20 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 			if (filename.hasSuffixIgnoreCase(".dat"))
 				continue;
 
-			warning("Uknown zcode game %s - %s %d", filename.c_str(), md5.c_str(), filesize);
+			if (gDebugLevel > 0) {
+				// Print an entry suitable for putting into the detection_tables.h, using the
+				// name of the parent folder the game is in as the presumed game Id
+				Common::String folderName = file->getParent().getName();
+				if (folderName.hasSuffix("\\"))
+					folderName.deleteLastChar();
+				Common::String fname = filename;
+				const char *dot = strchr(fname.c_str(), '.');
+				if (dot)
+					fname = Common::String(fname.c_str(), dot);
+
+				debug("ENTRY0(\"%s\", \"%s-%s\", \"%s\", %u),",
+					folderName.c_str(), fname.c_str(), serial, md5.c_str(), filesize);
+			}
 			const PlainGameDescriptor &desc = FROTZ_GAME_LIST[0];
 			gd = DetectedGame(desc.gameId, desc.description, Common::UNK_LANG, Common::kPlatformUnknown);
 		} else {
diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
index 8e6da37..179067c 100644
--- a/engines/glk/frotz/detection_tables.h
+++ b/engines/glk/frotz/detection_tables.h
@@ -87,9 +87,11 @@ const PlainGameDescriptor FROTZ_GAME_LIST[] = {
 
 #define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
 #define ENTRY0(ID, VERSION, MD5, FILESIZE) { ID, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
+#define ENTRY1(ID, VERSION, MD5, FILESIZE, LANG) { ID, VERSION, MD5, FILESIZE, LANG, NONE }
 #define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
 
 const FrotzGameDescription FROTZ_GAMES[] = {
+	// Infocom Games - English
 	ENTRY0("amfv", "R77-850814", "b7ffaed0ca4a90450f92b34066133377", 262016),
 	ENTRY0("amfv", "R79-851122", "1e37dbcf7ccc9244dbfc3229796362f4", 262544),
 	ENTRY0("arthur", "R54-890606", "ced2c66d03a49de0e8190b468332f081", 271360),
@@ -157,8 +159,53 @@ const FrotzGameDescription FROTZ_GAMES[] = {
 	ENTRY0("starcross", "R17-821021", "ed1e62e1f0eb9d819be45c076c5729f7", 83792),
 	ENTRY0("stationfall", "R107-870430", "cfadfb66afabaa2971ec9b4ae65266ca", 128934),
 	ENTRY0("suspect", "R14-841005", "3d759ccb19233f51968fa79d7374b393", 118692),
+	ENTRY0("suspended", "v5-830222", "d898430e3cccdee9f9acfffcc9ef709c", 105418),
+	ENTRY0("suspended", "R7-830419", "65f0cc760a2500d110242fbf942f1028", 105500),
+	ENTRY0("suspended", "R8-830521", "b749d42462dfec21831b69635cd9c5e8", 105492),
+	ENTRY0("suspended", "R8_2-840521", "6088ad7cb553626b52875a9b8e801312", 105584),
+	ENTRY0("trinity", "R11-860509", "994ea591f8d401e11661c912b92ee05e", 262016),
+	ENTRY0("trinity", "R12-860926", "5377dc1ee39f1c8ed572944f89946eb2", 262064),
+	ENTRY0("wishbringer", "R23-880706", "bec823084c5622e88eca5a886278d2a5", 164712),
+	ENTRY0("wishbringer", "R68-850501", "898b9b157ce8e54a0953366d6317fbd5", 128952),
+	ENTRY0("wishbringer", "R69-850920", "e7c0412c4b3bda39de438a02cbae3816", 128904),
+	ENTRY0("thewitness", "R13-830524", "d2297ddfe2c1b976c1b0c381ab01e2b3", 102608),
+	ENTRY0("thewitness", "R18-830910", "a6e441b0b92a72537c830fed201267af", 103728),
+	ENTRY0("thewitness", "R22-840924", "1019b9b1e1aa2c6eda945d7d92c2073a", 104664),
+	ENTRY0("zork0", "R296-881019", "fca554c21542729c9d154c319af6307e", 295536),
+	ENTRY0("zork0", "R366-demo-890323", "b58c35dc2ba36d48fade99564922c7c3", 296376),
+	ENTRY0("zork0", "R366-890323", "e787b2cad2d6f29fd812e737f75646e8", 296376),
+	ENTRY0("zork0", "R383-890602", "32e3e7ec438dabe77df2351af6ece324", 299392),
+	ENTRY0("zork0", "R393-890714", "29fb0e090bbff7bc8e9661e55da69ae7", 299968),
+	ENTRY0("zork1", "R15-UG3AU5", "fa2d22304700898cb8de921f46ca4bc9", 78566),
+	ENTRY0("zork1", "R20", "b222bed4a0ab2650135cba7f4b1b1c67", 75734),
+	ENTRY0("zork1", "R23-820428", "6ad3d9ab2874beefcbc53630e9261118", 75780),
+	ENTRY0("zork1", "R25-820515", "287a1ce17f458fb2e776499a13796719", 75808),
+	ENTRY0("zork1", "R26-820803", "285f1d7c5deb1a2f23825f63823d0777", 75964),
+	ENTRY0("zork1", "R28-821013", "83bb70d73f3b4b5c4a32d8588b2d0707", 76018),
+	ENTRY0("zork1", "R30-830330", "d6d8b3ae49a683a6fce2383a8bab36a5", 76324),
+	ENTRY0("zork1", "R5", "dd5ba502b30189d03abfcfb9817dffe0", 82836),
+	ENTRY0("zork1", "R52-871125", "e56267fd041c71fc229f7deb6b6537c2", 105264),
+	ENTRY0("zork1", "R75-830929", "b35bca8dd18f6312c7e54dcd7958d7e5", 84868),
+	ENTRY0("zork1", "R76-840509", "50ebf3c0c959ac2571c23cb7f7907c70", 84874),
+	ENTRY0("zork1", "R88-840726", "d708b6751126f3b2b7612c760f080d41", 84876),
+	ENTRY0("zork2", "R15-820308", "4b6ecc8e40243ddbd4cc19ef82304c3b", 82110),
+	ENTRY0("zork2", "R17-820427", "386f2cd937e0ca316695d6ddca521c78", 82368),
+	ENTRY0("zork2", "R18-820512", "a019dd721134b57f5926ee2adf634b55", 82422),
+	ENTRY0("zork2", "R18_2-820517", "6cafa0e5239a74aa120bb8e2c33441be", 82422),
+	ENTRY0("zork2", "R19-820721", "a5236020509af26b47c737e51ce884aa", 82586),
+	ENTRY0("zork2", "R22-830331", "600264d62720731283454592261ec3fe", 82920),
+	ENTRY0("zork2", "R23-830411", "6c2e766b553c127bb07f7a0f8fe03ae2", 81876),
+	ENTRY0("zork2", "R48-840904", "a5064c9c3ce0bc02f16e01d745f39b67", 89912),
+	ENTRY0("zork2", "R7-UG3AU5", "8243ce12e7b3ce24b150f34cc2cb472c", 85260),
+	ENTRY0("zork3", "R10-820818", "ba4897f4d82ba08906295dd3aebf501a", 82334),
+	ENTRY0("zork3", "R15-830331", "2fb29e6f5eebb643f42714ca9086e145", 82558),
+	ENTRY0("zork3", "R15_2-840518", "672b54d8f457bd3be32e41fc9e069d71", 82642),
+	ENTRY0("zork3", "R16-830410", "4717f8ec2f08da7d438c05f1351d28bd", 81626),
+	ENTRY0("zork3", "R17-840727", "c5dc520f469771c59d193558d405341d", 82714),
+	ENTRY0("ztuu", "ztuu-970828", "3a55991be19943a13da58a91cd3615eb", 102524),
 
-
+	// Infocom Games - Foreign
+	ENTRY1("zork1", "R3-880113", "9f336c92c1fd392fc7e81288e5c9b6ab", 116216, Common::DE_DEU),
 
 	FROTZ_TABLE_END_MARKER
 };


Commit: bd4bc0a43f5f90c6c4cead91df9f5296f3b82899
    https://github.com/scummvm/scummvm/commit/bd4bc0a43f5f90c6c4cead91df9f5296f3b82899
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix crash clicking screen with mouse without dragging

Changed paths:
    engines/glk/selection.cpp


diff --git a/engines/glk/selection.cpp b/engines/glk/selection.cpp
index 2bc4fb8..566ae65 100644
--- a/engines/glk/selection.cpp
+++ b/engines/glk/selection.cpp
@@ -152,10 +152,8 @@ void Selection::startSelection(const Point &pos) {
 	tx = MIN(pos.x, (int16)_hor);
 	ty = MIN(pos.y, (int16)_ver);
 
-	_select.left = _last.x = tx;
-	_select.top = _last.y = ty;
-	_select.right = 0;
-	_select.bottom = 0;
+	_select.left = _select.right = _last.x = tx;
+	_select.top = _select.bottom = _last.y = ty;
 
 	g_vm->_windows->selectionChanged();
 }
@@ -189,8 +187,8 @@ void Selection::clearSelection() {
 }
 
 bool Selection::checkSelection(const Rect &r) const {
-	Rect select(MIN(_select.left, _select.right), MAX(_select.left, _select.right),
-	            MIN(_select.top, _select.bottom), MAX(_select.top, _select.bottom));
+	Rect select(MIN(_select.left, _select.right), MIN(_select.top, _select.bottom),		
+		MAX(_select.left, _select.right), MAX(_select.top, _select.bottom));
 	if (select.isEmpty())
 		return false;
 


Commit: 027240678e54f410fcce21b1a9f86d52fdcc07af
    https://github.com/scummvm/scummvm/commit/027240678e54f410fcce21b1a9f86d52fdcc07af
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Change engine to use ScummVM String and Arrays

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


diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index 6fedab1..c895d2b 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -27,9 +27,8 @@ namespace Glk {
 namespace Scott {
 
 Scott::Scott(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
-	Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
-	Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
-	split_screen(true), Bottom(0), Top(0), BitFlags(0), _saveSlot(-1) {
+		CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0), split_screen(true),
+		Bottom(0), Top(0), BitFlags(0), _saveSlot(-1) {
 	Common::fill(&NounText[0], &NounText[16], '\0');
 	Common::fill(&Counters[0], &Counters[16], 0);
 	Common::fill(&RoomSaved[0], &RoomSaved[16], 0);
@@ -182,13 +181,6 @@ void Scott::clearScreen(void) {
 	glk_window_clear(Bottom);
 }
 
-void *Scott::memAlloc(int size) {
-	void *t = (void *)malloc(size);
-	if (t == nullptr)
-		fatal("Out of memory");
-	return t;
-}
-
 bool Scott::randomPercent(uint n) {
 	return _random.getRandomNumber(99) < n;
 }
@@ -209,7 +201,7 @@ const char *Scott::mapSynonym(const char *word) {
 	const char *tp;
 	static char lastword[16];   // Last non synonym
 	while (n <= GameHeader.NumWords) {
-		tp = Nouns[n];
+		tp = Nouns[n].c_str();
 		if (*tp == '*')
 			tp++;
 		else
@@ -229,8 +221,8 @@ int Scott::matchUpItem(const char *text, int loc) {
 		word = text;
 
 	while (ct <= GameHeader.NumItems) {
-		if (Items[ct].AutoGet && Items[ct].Location == loc &&
-		        xstrncasecmp(Items[ct].AutoGet, word, GameHeader.WordLength) == 0)
+		if (!Items[ct].AutoGet.empty() && Items[ct].Location == loc &&
+		        xstrncasecmp(Items[ct].AutoGet.c_str(), word, GameHeader.WordLength) == 0)
 			return ct;
 		ct++;
 	}
@@ -238,9 +230,8 @@ int Scott::matchUpItem(const char *text, int loc) {
 	return -1;
 }
 
-char *Scott::readString(Common::SeekableReadStream *f) {
+Common::String Scott::readString(Common::SeekableReadStream *f) {
 	char tmp[1024];
-	char *t;
 	int c, nc;
 	int ct = 0;
 	do {
@@ -280,9 +271,7 @@ char *Scott::readString(Common::SeekableReadStream *f) {
 	}
 
 	tmp[ct] = 0;
-	t = (char *)memAlloc(ct + 1);
-	memcpy(t, tmp, ct + 1);
-	return t;
+	return Common::String(tmp);
 }
 
 void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
@@ -297,27 +286,27 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
 
 	GameHeader.NumItems = ni;
-	Items = (Item *)memAlloc(sizeof(Item) * (ni + 1));
+	Items.resize(ni + 1);
 	GameHeader.NumActions = na;
-	Actions = (Action *)memAlloc(sizeof(Action) * (na + 1));
+	Actions.resize(na + 1);
 	GameHeader.NumWords = nw;
 	GameHeader.WordLength = wl;
-	Verbs = (const char **)memAlloc(sizeof(char *) * (nw + 1));
-	Nouns = (const char **)memAlloc(sizeof(char *) * (nw + 1));
+	Verbs.resize(nw + 1);
+	Nouns.resize(nw + 1);
 	GameHeader.NumRooms = nr;
-	Rooms = (Room *)memAlloc(sizeof(Room) * (nr + 1));
+	Rooms.resize(nr + 1);
 	GameHeader.MaxCarry = mc;
 	GameHeader.PlayerRoom = pr;
 	GameHeader.Treasures = tr;
 	GameHeader.LightTime = lt;
 	LightRefill = lt;
 	GameHeader.NumMessages = mn;
-	Messages = (const char **)memAlloc(sizeof(char *) * (mn + 1));
+	Messages.resize(mn + 1);
 	GameHeader.TreasureRoom = trm;
 
 	// Load the actions
 	ct = 0;
-	ap = Actions;
+	ap = &Actions[0];
 	if (loud)
 		debug("Reading %d actions.", na);
 	while (ct < na + 1) {
@@ -343,7 +332,7 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 		ct++;
 	}
 	ct = 0;
-	rp = Rooms;
+	rp = &Rooms[0];
 	if (loud)
 		debug("Reading %d rooms.", nr);
 	while (ct < nr + 1) {
@@ -367,17 +356,23 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	ct = 0;
 	if (loud)
 		debug("Reading %d items.", ni);
-	ip = Items;
+	ip = &Items[0];
 	while (ct < ni + 1) {
 		ip->Text = readString(f);
-		ip->AutoGet = strchr(ip->Text, '/');
-		// Some games use // to mean no auto get/drop word!
-		if (ip->AutoGet && strcmp(ip->AutoGet, "//") && strcmp(ip->AutoGet, "/*")) {
-			char *t;
-			*ip->AutoGet++ = 0;
-			t = strchr(ip->AutoGet, '/');
-			if (t != nullptr)
-				*t = 0;
+
+		const char *p = strchr(ip->Text.c_str(), '/');
+		if (p) {
+			ip->AutoGet = Common::String(p);
+
+			// Some games use // to mean no auto get/drop word!
+			if (!ip->AutoGet.hasPrefix("//") && !ip->AutoGet.hasPrefix("/*")) {
+				ip->Text = Common::String(ip->Text.c_str(), p);
+				ip->AutoGet.deleteChar(0);
+
+				const char *t = strchr(ip->AutoGet.c_str(), '/');
+				if (t)
+					ip->AutoGet = Common::String(ip->AutoGet.c_str(), t);
+			}
 		}
 
 		readInts(f, 1, &lo);
@@ -387,9 +382,10 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 		ct++;
 	}
 	ct = 0;
-	// Discard Comment Strings
+
+	// Skip Comment Strings
 	while (ct < na + 1) {
-		free(readString(f));
+		readString(f);
 		ct++;
 	}
 
@@ -402,9 +398,9 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 		debug("%d.\nLoad Complete.\n", ct);
 }
 
-void Scott::output(const char *a) {
+void Scott::output(const Common::String &a) {
 	if (_saveSlot == -1)
-		display(Bottom, "%s", a);
+		display(Bottom, "%s", a.c_str());
 }
 
 void Scott::outputNumber(int a) {
@@ -431,13 +427,13 @@ void Scott::look(void) {
 		return;
 	}
 	r = &Rooms[MyLoc];
-	if (*r->Text == '*')
-		display(Top, "%s\n", r->Text + 1);
+	if (r->Text.hasPrefix("*"))
+		display(Top, "%s\n", r->Text.c_str() + 1);
 	else {
 		if (Options & YOUARE)
-			display(Top, "You are in a %s\n", r->Text);
+			display(Top, "You are in a %s\n", r->Text.c_str());
 		else
-			display(Top, "I'm in a %s\n", r->Text);
+			display(Top, "I'm in a %s\n", r->Text.c_str());
 	}
 
 	ct = 0;
@@ -475,12 +471,12 @@ void Scott::look(void) {
 				display(Top, " - ");
 				pos += 3;
 			}
-			if (pos + (int)strlen(Items[ct].Text) > (Width - 10)) {
+			if (pos + (int)Items[ct].Text.size() > (Width - 10)) {
 				pos = 0;
 				display(Top, "\n");
 			}
-			display(Top, "%s", Items[ct].Text);
-			pos += strlen(Items[ct].Text);
+			display(Top, "%s", Items[ct].Text.c_str());
+			pos += Items[ct].Text.size();
 			if (Options & TRS80_STYLE) {
 				display(Top, ". ");
 				pos += 2;
@@ -494,12 +490,12 @@ void Scott::look(void) {
 		display(Top, TRS80_LINE);
 }
 
-int Scott::whichWord(const char *word, const char **list) {
+int Scott::whichWord(const char *word, const Common::StringArray &list) {
 	int n = 1;
 	int ne = 1;
 	const char *tp;
 	while (ne <= GameHeader.NumWords) {
-		tp = list[ne];
+		tp = list[ne].c_str();
 		if (*tp == '*')
 			tp++;
 		else
@@ -862,7 +858,7 @@ doneit:
 				int n = 0;
 				while (i <= GameHeader.NumItems) {
 					if (Items[i].Location == GameHeader.TreasureRoom &&
-					        *Items[i].Text == '*')
+					        Items[i].Text.hasPrefix("*"))
 						n++;
 					i++;
 				}
@@ -1115,7 +1111,7 @@ int Scott::performActions(int vb, int no) {
 					}
 					while (i <= GameHeader.NumItems) {
 						if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
-							no = whichWord(Items[i].AutoGet, Nouns);
+							no = whichWord(Items[i].AutoGet.c_str(), Nouns);
 							disable_sysfunc = 1;    // Don't recurse into auto get !
 							performActions(vb, no); // Recursively check each items table code
 							disable_sysfunc = 0;
@@ -1165,8 +1161,9 @@ int Scott::performActions(int vb, int no) {
 					int i = 0;
 					int f = 0;
 					while (i <= GameHeader.NumItems) {
-						if (Items[i].Location == CARRIED && Items[i].AutoGet && Items[i].AutoGet[0] != '*') {
-							no = whichWord(Items[i].AutoGet, Nouns);
+						if (Items[i].Location == CARRIED && !Items[i].AutoGet.empty()
+								&& !Items[i].AutoGet.hasPrefix("*")) {
+							no = whichWord(Items[i].AutoGet.c_str(), Nouns);
 							disable_sysfunc = 1;
 							performActions(vb, no);
 							disable_sysfunc = 0;
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index cf5f7bd..188ddd7 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -81,21 +81,21 @@ struct Action {
 };
 
 struct Room {
-	char *Text;
+	Common::String Text;
 	short Exits[6];
 
-	Room() : Text(0) {
+	Room() {
 		Common::fill(&Exits[0], &Exits[6], 0);
 	}
 };
 
 struct Item {
-	char *Text;     // PORTABILITY WARNING: THESE TWO MUST BE 8 BIT VALUES.
+	Common::String Text;
 	byte Location;
 	byte InitialLoc;
-	char *AutoGet;
+	Common::String AutoGet;
 
-	Item() : Text(nullptr), Location(0), InitialLoc(0), AutoGet(nullptr) {}
+	Item() : Location(0), InitialLoc(0) {}
 };
 
 struct Tail {
@@ -112,12 +112,12 @@ struct Tail {
 class Scott : public GlkAPI {
 private:
 	Header GameHeader;
-	Item *Items;
-	Room *Rooms;
-	const char **Verbs;
-	const char **Nouns;
-	const char **Messages;
-	Action *Actions;
+	Common::Array<Item> Items;
+	Common::Array<Room> Rooms;
+	Common::StringArray Verbs;
+	Common::StringArray Nouns;
+	Common::StringArray Messages;
+	Common::Array<Action> Actions;
 	int LightRefill;
 	char NounText[16];
 	int Counters[16];   ///< Range unknown
@@ -142,17 +142,16 @@ private:
 	void delay(int seconds);
 	void fatal(const char *x);
 	void clearScreen(void);
-	void *memAlloc(int size);
 	bool randomPercent(uint n);
 	int countCarried(void);
 	const char *mapSynonym(const char *word);
 	int matchUpItem(const char *text, int loc);
-	char *readString(Common::SeekableReadStream *f);
+	Common::String readString(Common::SeekableReadStream *f);
 	void loadDatabase(Common::SeekableReadStream *f, bool loud);
-	void output(const char *a);
+	void output(const Common::String &a);
 	void outputNumber(int a);
 	void look(void);
-	int whichWord(const char *word, const char **list);
+	int whichWord(const char *word, const Common::StringArray &list);
 	void lineInput(char *buf, size_t n);
 	void saveGame(void);
 	void loadGame(void);


Commit: 83646598c9afacb522cabe6bd7e3778a82d3459c
    https://github.com/scummvm/scummvm/commit/83646598c9afacb522cabe6bd7e3778a82d3459c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Renaming of all fields to ScummVM convention

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


diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index c895d2b..ff014e7 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -27,38 +27,38 @@ namespace Glk {
 namespace Scott {
 
 Scott::Scott(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
-		CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0), split_screen(true),
-		Bottom(0), Top(0), BitFlags(0), _saveSlot(-1) {
-	Common::fill(&NounText[0], &NounText[16], '\0');
-	Common::fill(&Counters[0], &Counters[16], 0);
-	Common::fill(&RoomSaved[0], &RoomSaved[16], 0);
+		_currentCounter(0), _savedRoom(0), _options(0), _width(0), _topHeight(0), _splitScreen(true),
+		_bottomWindow(0), _topWindow(0), _bitFlags(0), _saveSlot(-1) {
+	Common::fill(&_nounText[0], &_nounText[16], '\0');
+	Common::fill(&_counters[0], &_counters[16], 0);
+	Common::fill(&_roomSaved[0], &_roomSaved[16], 0);
 }
 
 void Scott::runGame(Common::SeekableReadStream *gameFile) {
 	int vb, no;
 	initialize();
 
-	Bottom = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
-	if (Bottom == nullptr)
+	_bottomWindow = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
+	if (_bottomWindow == nullptr)
 		glk_exit();
-	glk_set_window(Bottom);
+	glk_set_window(_bottomWindow);
 
-	if (Options & TRS80_STYLE) {
-		Width = 64;
-		TopHeight = 11;
+	if (_options & TRS80_STYLE) {
+		_width = 64;
+		_topHeight = 11;
 	} else {
-		Width = 80;
-		TopHeight = 10;
+		_width = 80;
+		_topHeight = 10;
 	}
 
-	if (split_screen) {
-		Top = glk_window_open(Bottom, winmethod_Above | winmethod_Fixed, TopHeight, wintype_TextGrid, 0);
-		if (Top == nullptr) {
-			split_screen = 0;
-			Top = Bottom;
+	if (_splitScreen) {
+		_topWindow = glk_window_open(_bottomWindow, winmethod_Above | winmethod_Fixed, _topHeight, wintype_TextGrid, 0);
+		if (_topWindow == nullptr) {
+			_splitScreen = 0;
+			_topWindow = _bottomWindow;
 		}
 	} else {
-		Top = Bottom;
+		_topWindow = _bottomWindow;
 	}
 
 	output("ScummVM support adapted from Scott Free, A Scott Adams game driver in C.\n\n");
@@ -67,7 +67,7 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 	_saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
 
 	// Load the game
-	loadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
+	loadDatabase(gameFile, (_options & DEBUGGING) ? 1 : 0);
 
 	// Main game loop
 	while (!shouldQuit()) {
@@ -100,29 +100,29 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 		}
 
 		// Brian Howarth games seem to use -1 for forever
-		if (Items[LIGHT_SOURCE].Location/*==-1*/ != DESTROYED && GameHeader.LightTime != -1) {
-			GameHeader.LightTime--;
-			if (GameHeader.LightTime < 1) {
-				BitFlags |= (1 << LIGHTOUTBIT);
-				if (Items[LIGHT_SOURCE].Location == CARRIED ||
-				        Items[LIGHT_SOURCE].Location == MyLoc) {
-					if (Options & SCOTTLIGHT)
+		if (_items[LIGHT_SOURCE]._location/*==-1*/ != DESTROYED && _gameHeader._lightTime != -1) {
+			_gameHeader._lightTime--;
+			if (_gameHeader._lightTime < 1) {
+				_bitFlags |= (1 << LIGHTOUTBIT);
+				if (_items[LIGHT_SOURCE]._location == CARRIED ||
+				        _items[LIGHT_SOURCE]._location == MY_LOC) {
+					if (_options & SCOTTLIGHT)
 						output("Light has run out! ");
 					else
 						output("Your light has run out. ");
 				}
-				if (Options & PREHISTORIC_LAMP)
-					Items[LIGHT_SOURCE].Location = DESTROYED;
-			} else if (GameHeader.LightTime < 25) {
-				if (Items[LIGHT_SOURCE].Location == CARRIED ||
-				        Items[LIGHT_SOURCE].Location == MyLoc) {
+				if (_options & PREHISTORIC_LAMP)
+					_items[LIGHT_SOURCE]._location = DESTROYED;
+			} else if (_gameHeader._lightTime < 25) {
+				if (_items[LIGHT_SOURCE]._location == CARRIED ||
+				        _items[LIGHT_SOURCE]._location == MY_LOC) {
 
-					if (Options & SCOTTLIGHT) {
+					if (_options & SCOTTLIGHT) {
 						output("Light runs out in ");
-						outputNumber(GameHeader.LightTime);
+						outputNumber(_gameHeader._lightTime);
 						output(" turns. ");
 					} else {
-						if (GameHeader.LightTime % 5 == 0)
+						if (_gameHeader._lightTime % 5 == 0)
 							output("Your light is growing dim. ");
 					}
 				}
@@ -134,18 +134,18 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 void Scott::initialize() {
 	if (ConfMan.hasKey("YOUARE")) {
 		if (ConfMan.getBool("YOUARE"))
-			Options |= YOUARE;
+			_options |= YOUARE;
 		else
-			Options &= ~YOUARE;
+			_options &= ~YOUARE;
 	}
 	if (gDebugLevel > 0)
-		Options |= DEBUGGING;
+		_options |= DEBUGGING;
 	if (ConfMan.hasKey("SCOTTLIGHT") && ConfMan.getBool("SCOTTLIGHT"))
-		Options |= SCOTTLIGHT;
+		_options |= SCOTTLIGHT;
 	if (ConfMan.hasKey("TRS80_STYLE") && ConfMan.getBool("TRS80_STYLE"))
-		Options |= TRS80_STYLE;
+		_options |= TRS80_STYLE;
 	if (ConfMan.hasKey("PREHISTORIC_LAMP") && ConfMan.getBool("PREHISTORIC_LAMP"))
-		Options |= PREHISTORIC_LAMP;
+		_options |= PREHISTORIC_LAMP;
 }
 
 void Scott::display(winid_t w, const char *fmt, ...) {
@@ -178,7 +178,7 @@ void Scott::fatal(const char *x) {
 }
 
 void Scott::clearScreen(void) {
-	glk_window_clear(Bottom);
+	glk_window_clear(_bottomWindow);
 }
 
 bool Scott::randomPercent(uint n) {
@@ -188,8 +188,8 @@ bool Scott::randomPercent(uint n) {
 int Scott::countCarried(void) {
 	int ct = 0;
 	int n = 0;
-	while (ct <= GameHeader.NumItems) {
-		if (Items[ct].Location == CARRIED)
+	while (ct <= _gameHeader._numItems) {
+		if (_items[ct]._location == CARRIED)
 			n++;
 		ct++;
 	}
@@ -200,13 +200,13 @@ const char *Scott::mapSynonym(const char *word) {
 	int n = 1;
 	const char *tp;
 	static char lastword[16];   // Last non synonym
-	while (n <= GameHeader.NumWords) {
-		tp = Nouns[n].c_str();
+	while (n <= _gameHeader._numWords) {
+		tp = _nouns[n].c_str();
 		if (*tp == '*')
 			tp++;
 		else
 			strcpy(lastword, tp);
-		if (xstrncasecmp(word, tp, GameHeader.WordLength) == 0)
+		if (xstrncasecmp(word, tp, _gameHeader._wordLength) == 0)
 			return lastword;
 		n++;
 	}
@@ -220,9 +220,9 @@ int Scott::matchUpItem(const char *text, int loc) {
 	if (word == nullptr)
 		word = text;
 
-	while (ct <= GameHeader.NumItems) {
-		if (!Items[ct].AutoGet.empty() && Items[ct].Location == loc &&
-		        xstrncasecmp(Items[ct].AutoGet.c_str(), word, GameHeader.WordLength) == 0)
+	while (ct <= _gameHeader._numItems) {
+		if (!_items[ct]._autoGet.empty() && _items[ct]._location == loc &&
+		        xstrncasecmp(_items[ct]._autoGet.c_str(), word, _gameHeader._wordLength) == 0)
 			return ct;
 		ct++;
 	}
@@ -285,40 +285,40 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	// Load the header
 	readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
 
-	GameHeader.NumItems = ni;
-	Items.resize(ni + 1);
-	GameHeader.NumActions = na;
-	Actions.resize(na + 1);
-	GameHeader.NumWords = nw;
-	GameHeader.WordLength = wl;
-	Verbs.resize(nw + 1);
-	Nouns.resize(nw + 1);
-	GameHeader.NumRooms = nr;
-	Rooms.resize(nr + 1);
-	GameHeader.MaxCarry = mc;
-	GameHeader.PlayerRoom = pr;
-	GameHeader.Treasures = tr;
-	GameHeader.LightTime = lt;
-	LightRefill = lt;
-	GameHeader.NumMessages = mn;
-	Messages.resize(mn + 1);
-	GameHeader.TreasureRoom = trm;
+	_gameHeader._numItems = ni;
+	_items.resize(ni + 1);
+	_gameHeader._numActions = na;
+	_actions.resize(na + 1);
+	_gameHeader._numWords = nw;
+	_gameHeader._wordLength = wl;
+	_verbs.resize(nw + 1);
+	_nouns.resize(nw + 1);
+	_gameHeader._numRooms = nr;
+	_rooms.resize(nr + 1);
+	_gameHeader._maxCarry = mc;
+	_gameHeader._playerRoom = pr;
+	_gameHeader._treasures = tr;
+	_gameHeader._lightTime = lt;
+	_lightRefill = lt;
+	_gameHeader._numMessages = mn;
+	_messages.resize(mn + 1);
+	_gameHeader._treasureRoom = trm;
 
 	// Load the actions
 	ct = 0;
-	ap = &Actions[0];
+	ap = &_actions[0];
 	if (loud)
 		debug("Reading %d actions.", na);
 	while (ct < na + 1) {
 		readInts(f, 8,
-		         &ap->Vocab,
-		         &ap->Condition[0],
-		         &ap->Condition[1],
-		         &ap->Condition[2],
-		         &ap->Condition[3],
-		         &ap->Condition[4],
-		         &ap->action[0],
-		         &ap->action[1]);
+		         &ap->_vocab,
+		         &ap->_condition[0],
+		         &ap->_condition[1],
+		         &ap->_condition[2],
+		         &ap->_condition[3],
+		         &ap->_condition[4],
+		         &ap->_action[0],
+		         &ap->_action[1]);
 		ap++;
 		ct++;
 	}
@@ -327,20 +327,20 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	if (loud)
 		debug("Reading %d word pairs.", nw);
 	while (ct < nw + 1) {
-		Verbs[ct] = readString(f);
-		Nouns[ct] = readString(f);
+		_verbs[ct] = readString(f);
+		_nouns[ct] = readString(f);
 		ct++;
 	}
 	ct = 0;
-	rp = &Rooms[0];
+	rp = &_rooms[0];
 	if (loud)
 		debug("Reading %d rooms.", nr);
 	while (ct < nr + 1) {
 		readInts(f, 6,
-		         &rp->Exits[0], &rp->Exits[1], &rp->Exits[2],
-		         &rp->Exits[3], &rp->Exits[4], &rp->Exits[5]);
+		         &rp->_exits[0], &rp->_exits[1], &rp->_exits[2],
+		         &rp->_exits[3], &rp->_exits[4], &rp->_exits[5]);
 
-		rp->Text = readString(f);
+		rp->_text = readString(f);
 		ct++;
 		rp++;
 	}
@@ -349,35 +349,35 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	if (loud)
 		debug("Reading %d messages.", mn);
 	while (ct < mn + 1) {
-		Messages[ct] = readString(f);
+		_messages[ct] = readString(f);
 		ct++;
 	}
 
 	ct = 0;
 	if (loud)
 		debug("Reading %d items.", ni);
-	ip = &Items[0];
+	ip = &_items[0];
 	while (ct < ni + 1) {
-		ip->Text = readString(f);
+		ip->_text = readString(f);
 
-		const char *p = strchr(ip->Text.c_str(), '/');
+		const char *p = strchr(ip->_text.c_str(), '/');
 		if (p) {
-			ip->AutoGet = Common::String(p);
+			ip->_autoGet = Common::String(p);
 
 			// Some games use // to mean no auto get/drop word!
-			if (!ip->AutoGet.hasPrefix("//") && !ip->AutoGet.hasPrefix("/*")) {
-				ip->Text = Common::String(ip->Text.c_str(), p);
-				ip->AutoGet.deleteChar(0);
+			if (!ip->_autoGet.hasPrefix("//") && !ip->_autoGet.hasPrefix("/*")) {
+				ip->_text = Common::String(ip->_text.c_str(), p);
+				ip->_autoGet.deleteChar(0);
 
-				const char *t = strchr(ip->AutoGet.c_str(), '/');
+				const char *t = strchr(ip->_autoGet.c_str(), '/');
 				if (t)
-					ip->AutoGet = Common::String(ip->AutoGet.c_str(), t);
+					ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
 			}
 		}
 
 		readInts(f, 1, &lo);
-		ip->Location = (unsigned char)lo;
-		ip->InitialLoc = ip->Location;
+		ip->_location = (unsigned char)lo;
+		ip->_initialLoc = ip->_location;
 		ip++;
 		ct++;
 	}
@@ -400,11 +400,11 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 
 void Scott::output(const Common::String &a) {
 	if (_saveSlot == -1)
-		display(Bottom, "%s", a.c_str());
+		display(_bottomWindow, "%s", a.c_str());
 }
 
 void Scott::outputNumber(int a) {
-	display(Bottom, "%d", a);
+	display(_bottomWindow, "%d", a);
 }
 
 void Scott::look(void) {
@@ -413,94 +413,94 @@ void Scott::look(void) {
 	int ct, f;
 	int pos;
 
-	if (split_screen)
-		glk_window_clear(Top);
+	if (_splitScreen)
+		glk_window_clear(_topWindow);
 
-	if ((BitFlags & (1 << DARKBIT)) && Items[LIGHT_SOURCE].Location != CARRIED
-	        && Items[LIGHT_SOURCE].Location != MyLoc) {
-		if (Options & YOUARE)
-			display(Top, "You can't see. It is too dark!\n");
+	if ((_bitFlags & (1 << DARKBIT)) && _items[LIGHT_SOURCE]._location != CARRIED
+	        && _items[LIGHT_SOURCE]._location != MY_LOC) {
+		if (_options & YOUARE)
+			display(_topWindow, "You can't see. It is too dark!\n");
 		else
-			display(Top, "I can't see. It is too dark!\n");
-		if (Options & TRS80_STYLE)
-			display(Top, TRS80_LINE);
+			display(_topWindow, "I can't see. It is too dark!\n");
+		if (_options & TRS80_STYLE)
+			display(_topWindow, TRS80_LINE);
 		return;
 	}
-	r = &Rooms[MyLoc];
-	if (r->Text.hasPrefix("*"))
-		display(Top, "%s\n", r->Text.c_str() + 1);
+	r = &_rooms[MY_LOC];
+	if (r->_text.hasPrefix("*"))
+		display(_topWindow, "%s\n", r->_text.c_str() + 1);
 	else {
-		if (Options & YOUARE)
-			display(Top, "You are in a %s\n", r->Text.c_str());
+		if (_options & YOUARE)
+			display(_topWindow, "You are in a %s\n", r->_text.c_str());
 		else
-			display(Top, "I'm in a %s\n", r->Text.c_str());
+			display(_topWindow, "I'm in a %s\n", r->_text.c_str());
 	}
 
 	ct = 0;
 	f = 0;
-	display(Top, "\nObvious exits: ");
+	display(_topWindow, "\nObvious exits: ");
 	while (ct < 6) {
-		if (r->Exits[ct] != 0) {
+		if (r->_exits[ct] != 0) {
 			if (f == 0)
 				f = 1;
 			else
-				display(Top, ", ");
-			display(Top, "%s", ExitNames[ct]);
+				display(_topWindow, ", ");
+			display(_topWindow, "%s", ExitNames[ct]);
 		}
 		ct++;
 	}
 
 	if (f == 0)
-		display(Top, "none");
-	display(Top, ".\n");
+		display(_topWindow, "none");
+	display(_topWindow, ".\n");
 	ct = 0;
 	f = 0;
 	pos = 0;
-	while (ct <= GameHeader.NumItems) {
-		if (Items[ct].Location == MyLoc) {
+	while (ct <= _gameHeader._numItems) {
+		if (_items[ct]._location == MY_LOC) {
 			if (f == 0) {
-				if (Options & YOUARE) {
-					display(Top, "\nYou can also see: ");
+				if (_options & YOUARE) {
+					display(_topWindow, "\nYou can also see: ");
 					pos = 18;
 				} else {
-					display(Top, "\nI can also see: ");
+					display(_topWindow, "\nI can also see: ");
 					pos = 16;
 				}
 				f++;
-			} else if (!(Options & TRS80_STYLE)) {
-				display(Top, " - ");
+			} else if (!(_options & TRS80_STYLE)) {
+				display(_topWindow, " - ");
 				pos += 3;
 			}
-			if (pos + (int)Items[ct].Text.size() > (Width - 10)) {
+			if (pos + (int)_items[ct]._text.size() > (_width - 10)) {
 				pos = 0;
-				display(Top, "\n");
+				display(_topWindow, "\n");
 			}
-			display(Top, "%s", Items[ct].Text.c_str());
-			pos += Items[ct].Text.size();
-			if (Options & TRS80_STYLE) {
-				display(Top, ". ");
+			display(_topWindow, "%s", _items[ct]._text.c_str());
+			pos += _items[ct]._text.size();
+			if (_options & TRS80_STYLE) {
+				display(_topWindow, ". ");
 				pos += 2;
 			}
 		}
 		ct++;
 	}
 
-	display(Top, "\n");
-	if (Options & TRS80_STYLE)
-		display(Top, TRS80_LINE);
+	display(_topWindow, "\n");
+	if (_options & TRS80_STYLE)
+		display(_topWindow, TRS80_LINE);
 }
 
 int Scott::whichWord(const char *word, const Common::StringArray &list) {
 	int n = 1;
 	int ne = 1;
 	const char *tp;
-	while (ne <= GameHeader.NumWords) {
+	while (ne <= _gameHeader._numWords) {
 		tp = list[ne].c_str();
 		if (*tp == '*')
 			tp++;
 		else
 			n = ne;
-		if (xstrncasecmp(word, tp, GameHeader.WordLength) == 0)
+		if (xstrncasecmp(word, tp, _gameHeader._wordLength) == 0)
 			return n;
 		ne++;
 	}
@@ -510,7 +510,7 @@ int Scott::whichWord(const char *word, const Common::StringArray &list) {
 void Scott::lineInput(char *buf, size_t n) {
 	event_t ev;
 
-	glk_request_line_event(Bottom, buf, n - 1, 0);
+	glk_request_line_event(_bottomWindow, buf, n - 1, 0);
 
 	do {
 		glk_select(&ev);
@@ -518,7 +518,7 @@ void Scott::lineInput(char *buf, size_t n) {
 			return;
 		else if (ev.type == evtype_LineInput)
 			break;
-		else if (ev.type == evtype_Arrange && split_screen)
+		else if (ev.type == evtype_Arrange && _splitScreen)
 			look();
 	} while (ev.type != evtype_Quit);
 
@@ -547,17 +547,17 @@ Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
 		return Common::kWritingFailed;
 
 	for (int ct = 0; ct < 16; ct++) {
-		msg = Common::String::format("%d %d\n", Counters[ct], RoomSaved[ct]);
+		msg = Common::String::format("%d %d\n", _counters[ct], _roomSaved[ct]);
 		glk_put_string_stream(file, msg.c_str());
 	}
 
 	msg = Common::String::format("%u %d %hd %d %d %hd\n",
-	                             BitFlags, (BitFlags & (1 << DARKBIT)) ? 1 : 0,
-	                             MyLoc, CurrentCounter, SavedRoom, GameHeader.LightTime);
+	                             _bitFlags, (_bitFlags & (1 << DARKBIT)) ? 1 : 0,
+	                             MY_LOC, _currentCounter, _savedRoom, _gameHeader._lightTime);
 	glk_put_string_stream(file, msg.c_str());
 
-	for (int ct = 0; ct <= GameHeader.NumItems; ct++) {
-		msg = Common::String::format("%hd\n", (short)Items[ct].Location);
+	for (int ct = 0; ct <= _gameHeader._numItems; ct++) {
+		msg = Common::String::format("%hd\n", (short)_items[ct]._location);
 		glk_put_string_stream(file, msg.c_str());
 	}
 
@@ -594,21 +594,21 @@ Common::Error Scott::loadGameState(int slot) {
 
 	for (ct = 0; ct < 16; ct++) {
 		glk_get_line_stream(file, buf, sizeof buf);
-		sscanf(buf, "%d %d", &Counters[ct], &RoomSaved[ct]);
+		sscanf(buf, "%d %d", &_counters[ct], &_roomSaved[ct]);
 	}
 
 	glk_get_line_stream(file, buf, sizeof buf);
 	sscanf(buf, "%u %hd %d %d %d %d\n",
-	       &BitFlags, &darkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
-	       &GameHeader.LightTime);
+	       &_bitFlags, &darkFlag, &MY_LOC, &_currentCounter, &_savedRoom,
+	       &_gameHeader._lightTime);
 
 	// Backward compatibility
 	if (darkFlag)
-		BitFlags |= (1 << 15);
-	for (ct = 0; ct <= GameHeader.NumItems; ct++) {
+		_bitFlags |= (1 << 15);
+	for (ct = 0; ct <= _gameHeader._numItems; ct++) {
 		glk_get_line_stream(file, buf, sizeof buf);
 		sscanf(buf, "%hd\n", &lo);
-		Items[ct].Location = (unsigned char)lo;
+		_items[ct]._location = (unsigned char)lo;
 	}
 
 	return Common::kNoError;
@@ -662,13 +662,13 @@ int Scott::getInput(int *vb, int *no) {
 				break;
 			}
 		}
-		nc = whichWord(verb, Nouns);
+		nc = whichWord(verb, _nouns);
 		// The Scott Adams system has a hack to avoid typing 'go'
 		if (nc >= 1 && nc <= 6) {
 			vc = 1;
 		} else {
-			vc = whichWord(verb, Verbs);
-			nc = whichWord(noun, Nouns);
+			vc = whichWord(verb, _verbs);
+			nc = whichWord(noun, _nouns);
 		}
 		*vb = vc;
 		*no = nc;
@@ -677,7 +677,7 @@ int Scott::getInput(int *vb, int *no) {
 		}
 	} while (vc == -1);
 
-	strcpy(NounText, noun); // Needed by GET/DROP hack
+	strcpy(_nounText, noun); // Needed by GET/DROP hack
 	return 0;
 }
 
@@ -689,7 +689,7 @@ int Scott::performLine(int ct) {
 
 	while (cc < 5) {
 		int cv, dv;
-		cv = Actions[ct].Condition[cc];
+		cv = _actions[ct]._condition[cc];
 		dv = cv / 20;
 		cv %= 20;
 		switch (cv) {
@@ -697,40 +697,40 @@ int Scott::performLine(int ct) {
 			param[pptr++] = dv;
 			break;
 		case 1:
-			if (Items[dv].Location != CARRIED)
+			if (_items[dv]._location != CARRIED)
 				return 0;
 			break;
 		case 2:
-			if (Items[dv].Location != MyLoc)
+			if (_items[dv]._location != MY_LOC)
 				return 0;
 			break;
 		case 3:
-			if (Items[dv].Location != CARRIED &&
-			        Items[dv].Location != MyLoc)
+			if (_items[dv]._location != CARRIED &&
+			        _items[dv]._location != MY_LOC)
 				return 0;
 			break;
 		case 4:
-			if (MyLoc != dv)
+			if (MY_LOC != dv)
 				return 0;
 			break;
 		case 5:
-			if (Items[dv].Location == MyLoc)
+			if (_items[dv]._location == MY_LOC)
 				return 0;
 			break;
 		case 6:
-			if (Items[dv].Location == CARRIED)
+			if (_items[dv]._location == CARRIED)
 				return 0;
 			break;
 		case 7:
-			if (MyLoc == dv)
+			if (MY_LOC == dv)
 				return 0;
 			break;
 		case 8:
-			if ((BitFlags & (1 << dv)) == 0)
+			if ((_bitFlags & (1 << dv)) == 0)
 				return 0;
 			break;
 		case 9:
-			if (BitFlags & (1 << dv))
+			if (_bitFlags & (1 << dv))
 				return 0;
 			break;
 		case 10:
@@ -742,45 +742,45 @@ int Scott::performLine(int ct) {
 				return 0;
 			break;
 		case 12:
-			if (Items[dv].Location == CARRIED || Items[dv].Location == MyLoc)
+			if (_items[dv]._location == CARRIED || _items[dv]._location == MY_LOC)
 				return 0;
 			break;
 		case 13:
-			if (Items[dv].Location == 0)
+			if (_items[dv]._location == 0)
 				return 0;
 			break;
 		case 14:
-			if (Items[dv].Location)
+			if (_items[dv]._location)
 				return 0;
 			break;
 		case 15:
-			if (CurrentCounter > dv)
+			if (_currentCounter > dv)
 				return 0;
 			break;
 		case 16:
-			if (CurrentCounter <= dv)
+			if (_currentCounter <= dv)
 				return 0;
 			break;
 		case 17:
-			if (Items[dv].Location != Items[dv].InitialLoc)
+			if (_items[dv]._location != _items[dv]._initialLoc)
 				return 0;
 			break;
 		case 18:
-			if (Items[dv].Location == Items[dv].InitialLoc)
+			if (_items[dv]._location == _items[dv]._initialLoc)
 				return 0;
 			break;
 		case 19:
 			// Only seen in Brian Howarth games so far
-			if (CurrentCounter != dv)
+			if (_currentCounter != dv)
 				return 0;
 			break;
 		}
 		cc++;
 	}
 
-	// Actions
-	act[0] = Actions[ct].action[0];
-	act[2] = Actions[ct].action[1];
+	// _actions
+	act[0] = _actions[ct]._action[0];
+	act[2] = _actions[ct]._action[1];
 	act[1] = act[0] % 150;
 	act[3] = act[2] % 150;
 	act[0] /= 150;
@@ -789,61 +789,61 @@ int Scott::performLine(int ct) {
 	pptr = 0;
 	while (cc < 4) {
 		if (act[cc] >= 1 && act[cc] < 52) {
-			output(Messages[act[cc]]);
+			output(_messages[act[cc]]);
 			output("\n");
 		} else if (act[cc] > 101) {
-			output(Messages[act[cc] - 50]);
+			output(_messages[act[cc] - 50]);
 			output("\n");
 		} else {
 			switch (act[cc]) {
 			case 0:// NOP
 				break;
 			case 52:
-				if (countCarried() == GameHeader.MaxCarry) {
-					if (Options & YOUARE)
+				if (countCarried() == _gameHeader._maxCarry) {
+					if (_options & YOUARE)
 						output("You are carrying too much. ");
 					else
 						output("I've too much to carry! ");
 					break;
 				}
-				Items[param[pptr++]].Location = CARRIED;
+				_items[param[pptr++]]._location = CARRIED;
 				break;
 			case 53:
-				Items[param[pptr++]].Location = MyLoc;
+				_items[param[pptr++]]._location = MY_LOC;
 				break;
 			case 54:
-				MyLoc = param[pptr++];
+				MY_LOC = param[pptr++];
 				break;
 			case 55:
-				Items[param[pptr++]].Location = 0;
+				_items[param[pptr++]]._location = 0;
 				break;
 			case 56:
-				BitFlags |= 1 << DARKBIT;
+				_bitFlags |= 1 << DARKBIT;
 				break;
 			case 57:
-				BitFlags &= ~(1 << DARKBIT);
+				_bitFlags &= ~(1 << DARKBIT);
 				break;
 			case 58:
-				BitFlags |= (1 << param[pptr++]);
+				_bitFlags |= (1 << param[pptr++]);
 				break;
 			case 59:
-				Items[param[pptr++]].Location = 0;
+				_items[param[pptr++]]._location = 0;
 				break;
 			case 60:
-				BitFlags &= ~(1 << param[pptr++]);
+				_bitFlags &= ~(1 << param[pptr++]);
 				break;
 			case 61:
-				if (Options & YOUARE)
+				if (_options & YOUARE)
 					output("You are dead.\n");
 				else
 					output("I am dead.\n");
-				BitFlags &= ~(1 << DARKBIT);
-				MyLoc = GameHeader.NumRooms;// It seems to be what the code says!
+				_bitFlags &= ~(1 << DARKBIT);
+				MY_LOC = _gameHeader._numRooms;// It seems to be what the code says!
 				break;
 			case 62: {
 				// Bug fix for some systems - before it could get parameters wrong */
 				int i = param[pptr++];
-				Items[i].Location = param[pptr++];
+				_items[i]._location = param[pptr++];
 				break;
 			}
 			case 63:
@@ -856,21 +856,21 @@ doneit:
 			case 65: {
 				int i = 0;
 				int n = 0;
-				while (i <= GameHeader.NumItems) {
-					if (Items[i].Location == GameHeader.TreasureRoom &&
-					        Items[i].Text.hasPrefix("*"))
+				while (i <= _gameHeader._numItems) {
+					if (_items[i]._location == _gameHeader._treasureRoom &&
+					        _items[i]._text.hasPrefix("*"))
 						n++;
 					i++;
 				}
-				if (Options & YOUARE)
+				if (_options & YOUARE)
 					output("You have stored ");
 				else
 					output("I've stored ");
 				outputNumber(n);
 				output(" treasures.  On a scale of 0 to 100, that rates ");
-				outputNumber((n * 100) / GameHeader.Treasures);
+				outputNumber((n * 100) / _gameHeader._treasures);
 				output(".\n");
-				if (n == GameHeader.Treasures) {
+				if (n == _gameHeader._treasures) {
 					output("Well done.\n");
 					goto doneit;
 				}
@@ -879,20 +879,20 @@ doneit:
 			case 66: {
 				int i = 0;
 				int f = 0;
-				if (Options & YOUARE)
+				if (_options & YOUARE)
 					output("You are carrying:\n");
 				else
 					output("I'm carrying:\n");
-				while (i <= GameHeader.NumItems) {
-					if (Items[i].Location == CARRIED) {
+				while (i <= _gameHeader._numItems) {
+					if (_items[i]._location == CARRIED) {
 						if (f == 1) {
-							if (Options & TRS80_STYLE)
+							if (_options & TRS80_STYLE)
 								output(". ");
 							else
 								output(" - ");
 						}
 						f = 1;
-						output(Items[i].Text);
+						output(_items[i]._text);
 					}
 					i++;
 				}
@@ -902,15 +902,15 @@ doneit:
 				break;
 			}
 			case 67:
-				BitFlags |= (1 << 0);
+				_bitFlags |= (1 << 0);
 				break;
 			case 68:
-				BitFlags &= ~(1 << 0);
+				_bitFlags &= ~(1 << 0);
 				break;
 			case 69:
-				GameHeader.LightTime = LightRefill;
-				Items[LIGHT_SOURCE].Location = CARRIED;
-				BitFlags &= ~(1 << LIGHTOUTBIT);
+				_gameHeader._lightTime = _lightRefill;
+				_items[LIGHT_SOURCE]._location = CARRIED;
+				_bitFlags &= ~(1 << LIGHTOUTBIT);
 				break;
 			case 70:
 				clearScreen(); // pdd.
@@ -921,41 +921,41 @@ doneit:
 			case 72: {
 				int i1 = param[pptr++];
 				int i2 = param[pptr++];
-				int t = Items[i1].Location;
-				Items[i1].Location = Items[i2].Location;
-				Items[i2].Location = t;
+				int t = _items[i1]._location;
+				_items[i1]._location = _items[i2]._location;
+				_items[i2]._location = t;
 				break;
 			}
 			case 73:
 				continuation = 1;
 				break;
 			case 74:
-				Items[param[pptr++]].Location = CARRIED;
+				_items[param[pptr++]]._location = CARRIED;
 				break;
 			case 75: {
 				int i1, i2;
 				i1 = param[pptr++];
 				i2 = param[pptr++];
-				Items[i1].Location = Items[i2].Location;
+				_items[i1]._location = _items[i2]._location;
 				break;
 			}
 			case 76:
 				// Looking at adventure ..
 				break;
 			case 77:
-				if (CurrentCounter >= 0)
-					CurrentCounter--;
+				if (_currentCounter >= 0)
+					_currentCounter--;
 				break;
 			case 78:
-				outputNumber(CurrentCounter);
+				outputNumber(_currentCounter);
 				break;
 			case 79:
-				CurrentCounter = param[pptr++];
+				_currentCounter = param[pptr++];
 				break;
 			case 80: {
-				int t = MyLoc;
-				MyLoc = SavedRoom;
-				SavedRoom = t;
+				int t = MY_LOC;
+				MY_LOC = _savedRoom;
+				_savedRoom = t;
 				break;
 			}
 			case 81: {
@@ -963,26 +963,26 @@ doneit:
 				// select counter n, thing, select counter n, but uses one value that always
 				// seems to exist. Trying a few options I found this gave sane results on ageing
 				int t = param[pptr++];
-				int c1 = CurrentCounter;
-				CurrentCounter = Counters[t];
-				Counters[t] = c1;
+				int c1 = _currentCounter;
+				_currentCounter = _counters[t];
+				_counters[t] = c1;
 				break;
 			}
 			case 82:
-				CurrentCounter += param[pptr++];
+				_currentCounter += param[pptr++];
 				break;
 			case 83:
-				CurrentCounter -= param[pptr++];
-				if (CurrentCounter < -1)
-					CurrentCounter = -1;
+				_currentCounter -= param[pptr++];
+				if (_currentCounter < -1)
+					_currentCounter = -1;
 				// Note: This seems to be needed. I don't yet know if there
 				// is a maximum value to limit too
 				break;
 			case 84:
-				output(NounText);
+				output(_nounText);
 				break;
 			case 85:
-				output(NounText);
+				output(_nounText);
 				output("\n");
 				break;
 			case 86:
@@ -991,9 +991,9 @@ doneit:
 			case 87: {
 				// Changed this to swap location<->roomflag[x] not roomflag 0 and x
 				int p = param[pptr++];
-				int sr = MyLoc;
-				MyLoc = RoomSaved[p];
-				RoomSaved[p] = sr;
+				int sr = MY_LOC;
+				MY_LOC = _roomSaved[p];
+				_roomSaved[p] = sr;
 				break;
 			}
 			case 88:
@@ -1020,7 +1020,7 @@ doneit:
 
 int Scott::performActions(int vb, int no) {
 	static int disable_sysfunc = 0; // Recursion lock
-	int d = BitFlags & (1 << DARKBIT);
+	int d = _bitFlags & (1 << DARKBIT);
 
 	int ct = 0;
 	int fl;
@@ -1031,24 +1031,24 @@ int Scott::performActions(int vb, int no) {
 	}
 	if (vb == 1 && no >= 1 && no <= 6) {
 		int nl;
-		if (Items[LIGHT_SOURCE].Location == MyLoc ||
-		        Items[LIGHT_SOURCE].Location == CARRIED)
+		if (_items[LIGHT_SOURCE]._location == MY_LOC ||
+		        _items[LIGHT_SOURCE]._location == CARRIED)
 			d = 0;
 		if (d)
 			output("Dangerous to move in the dark! ");
-		nl = Rooms[MyLoc].Exits[no - 1];
+		nl = _rooms[MY_LOC]._exits[no - 1];
 		if (nl != 0) {
-			MyLoc = nl;
+			MY_LOC = nl;
 			return 0;
 		}
 		if (d) {
-			if (Options & YOUARE)
+			if (_options & YOUARE)
 				output("You fell down and broke your neck. ");
 			else
 				output("I fell down and broke my neck. ");
 			glk_exit();
 		}
-		if (Options & YOUARE)
+		if (_options & YOUARE)
 			output("You can't go in that direction. ");
 		else
 			output("I can't go in that direction. ");
@@ -1056,9 +1056,9 @@ int Scott::performActions(int vb, int no) {
 	}
 
 	fl = -1;
-	while (ct <= GameHeader.NumActions) {
+	while (ct <= _gameHeader._numActions) {
 		int vv, nv;
-		vv = Actions[ct].Vocab;
+		vv = _actions[ct]._vocab;
 		// Think this is now right. If a line we run has an action73
 		// run all following lines with vocab of 0,0
 		if (vb != 0 && (doagain && vv != 0))
@@ -1068,7 +1068,7 @@ int Scott::performActions(int vb, int no) {
 			break;
 		nv = vv % 150;
 		vv /= 150;
-		if ((vv == vb) || (doagain && Actions[ct].Vocab == 0)) {
+		if ((vv == vb) || (doagain && _actions[ct]._vocab == 0)) {
 			if ((vv == 0 && randomPercent(nv)) || doagain ||
 			        (vv != 0 && (nv == no || nv == 0))) {
 				int f2;
@@ -1086,22 +1086,22 @@ int Scott::performActions(int vb, int no) {
 		}
 		ct++;
 
-		// Previously this did not check ct against GameHeader.NumActions and would read
-		// past the end of Actions.  I don't know what should happen on the last action,
+		// Previously this did not check ct against _gameHeader._numActions and would read
+		// past the end of _actions.  I don't know what should happen on the last action,
 		// but doing nothing is better than reading one past the end.
 		// --Chris
-		if (ct <= GameHeader.NumActions && Actions[ct].Vocab != 0)
+		if (ct <= _gameHeader._numActions && _actions[ct]._vocab != 0)
 			doagain = 0;
 	}
 	if (fl != 0 && disable_sysfunc == 0) {
 		int item;
-		if (Items[LIGHT_SOURCE].Location == MyLoc ||
-		        Items[LIGHT_SOURCE].Location == CARRIED)
+		if (_items[LIGHT_SOURCE]._location == MY_LOC ||
+		        _items[LIGHT_SOURCE]._location == CARRIED)
 			d = 0;
 		if (vb == 10 || vb == 18) {
 			// Yes they really _are_ hardcoded values
 			if (vb == 10) {
-				if (xstrcasecmp(NounText, "ALL") == 0) {
+				if (xstrcasecmp(_nounText, "ALL") == 0) {
 					int i = 0;
 					int f = 0;
 
@@ -1109,21 +1109,21 @@ int Scott::performActions(int vb, int no) {
 						output("It is dark.\n");
 						return 0;
 					}
-					while (i <= GameHeader.NumItems) {
-						if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
-							no = whichWord(Items[i].AutoGet.c_str(), Nouns);
+					while (i <= _gameHeader._numItems) {
+						if (_items[i]._location == MY_LOC && _items[i]._autoGet != nullptr && _items[i]._autoGet[0] != '*') {
+							no = whichWord(_items[i]._autoGet.c_str(), _nouns);
 							disable_sysfunc = 1;    // Don't recurse into auto get !
 							performActions(vb, no); // Recursively check each items table code
 							disable_sysfunc = 0;
-							if (countCarried() == GameHeader.MaxCarry) {
-								if (Options & YOUARE)
+							if (countCarried() == _gameHeader._maxCarry) {
+								if (_options & YOUARE)
 									output("You are carrying too much. ");
 								else
 									output("I've too much to carry. ");
 								return 0;
 							}
-							Items[i].Location = CARRIED;
-							output(Items[i].Text);
+							_items[i]._location = CARRIED;
+							output(_items[i]._text);
 							output(": O.K.\n");
 							f = 1;
 						}
@@ -1137,38 +1137,38 @@ int Scott::performActions(int vb, int no) {
 					output("What ? ");
 					return 0;
 				}
-				if (countCarried() == GameHeader.MaxCarry) {
-					if (Options & YOUARE)
+				if (countCarried() == _gameHeader._maxCarry) {
+					if (_options & YOUARE)
 						output("You are carrying too much. ");
 					else
 						output("I've too much to carry. ");
 					return 0;
 				}
-				item = matchUpItem(NounText, MyLoc);
+				item = matchUpItem(_nounText, MY_LOC);
 				if (item == -1) {
-					if (Options & YOUARE)
+					if (_options & YOUARE)
 						output("It is beyond your power to do that. ");
 					else
 						output("It's beyond my power to do that. ");
 					return 0;
 				}
-				Items[item].Location = CARRIED;
+				_items[item]._location = CARRIED;
 				output("O.K. ");
 				return 0;
 			}
 			if (vb == 18) {
-				if (xstrcasecmp(NounText, "ALL") == 0) {
+				if (xstrcasecmp(_nounText, "ALL") == 0) {
 					int i = 0;
 					int f = 0;
-					while (i <= GameHeader.NumItems) {
-						if (Items[i].Location == CARRIED && !Items[i].AutoGet.empty()
-								&& !Items[i].AutoGet.hasPrefix("*")) {
-							no = whichWord(Items[i].AutoGet.c_str(), Nouns);
+					while (i <= _gameHeader._numItems) {
+						if (_items[i]._location == CARRIED && !_items[i]._autoGet.empty()
+								&& !_items[i]._autoGet.hasPrefix("*")) {
+							no = whichWord(_items[i]._autoGet.c_str(), _nouns);
 							disable_sysfunc = 1;
 							performActions(vb, no);
 							disable_sysfunc = 0;
-							Items[i].Location = MyLoc;
-							output(Items[i].Text);
+							_items[i]._location = MY_LOC;
+							output(_items[i]._text);
 							output(": O.K.\n");
 							f = 1;
 						}
@@ -1182,15 +1182,15 @@ int Scott::performActions(int vb, int no) {
 					output("What ? ");
 					return 0;
 				}
-				item = matchUpItem(NounText, CARRIED);
+				item = matchUpItem(_nounText, CARRIED);
 				if (item == -1) {
-					if (Options & YOUARE)
+					if (_options & YOUARE)
 						output("It's beyond your power to do that.\n");
 					else
 						output("It's beyond my power to do that.\n");
 					return 0;
 				}
-				Items[item].Location = MyLoc;
+				_items[item]._location = MY_LOC;
 				output("O.K. ");
 				return 0;
 			}
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 188ddd7..955ebb9 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -48,62 +48,62 @@ enum GameOption {
 };
 
 #define TRS80_LINE  "\n<------------------------------------------------------------>\n"
-#define MyLoc   (GameHeader.PlayerRoom)
+#define MY_LOC   (_gameHeader._playerRoom)
 
 struct Header {
-	int Unknown;
-	int NumItems;
-	int NumActions;
-	int NumWords;           ///< Smaller of verb/noun is padded to same size
-	int NumRooms;
-	int MaxCarry;
-	int PlayerRoom;
-	int Treasures;
-	int WordLength;
-	int LightTime;
-	int NumMessages;
-	int TreasureRoom;
-
-	Header() : Unknown(0), NumItems(0), NumActions(0), NumWords(0), NumRooms(0),
-		MaxCarry(0), PlayerRoom(0), Treasures(0), WordLength(0), LightTime(0),
-		NumMessages(0), TreasureRoom(0) {}
+	int _unknown;
+	int _numItems;
+	int _numActions;
+	int _numWords;           ///< Smaller of verb/noun is padded to same size
+	int _numRooms;
+	int _maxCarry;
+	int _playerRoom;
+	int _treasures;
+	int _wordLength;
+	int _lightTime;
+	int _numMessages;
+	int _treasureRoom;
+
+	Header() : _unknown(0), _numItems(0), _numActions(0), _numWords(0), _numRooms(0),
+		_maxCarry(0), _playerRoom(0), _treasures(0), _wordLength(0), _lightTime(0),
+		_numMessages(0), _treasureRoom(0) {}
 };
 
 struct Action {
-	uint Vocab;
-	uint Condition[5];
-	uint action[2];
+	uint _vocab;
+	uint _condition[5];
+	uint _action[2];
 
-	Action() : Vocab(0) {
-		Common::fill(&Condition[0], &Condition[5], 0);
-		Common::fill(&action[0], &action[2], 0);
+	Action() : _vocab(0) {
+		Common::fill(&_condition[0], &_condition[5], 0);
+		Common::fill(&_action[0], &_action[2], 0);
 	}
 };
 
 struct Room {
-	Common::String Text;
-	short Exits[6];
+	Common::String _text;
+	short _exits[6];
 
 	Room() {
-		Common::fill(&Exits[0], &Exits[6], 0);
+		Common::fill(&_exits[0], &_exits[6], 0);
 	}
 };
 
 struct Item {
-	Common::String Text;
-	byte Location;
-	byte InitialLoc;
-	Common::String AutoGet;
+	Common::String _text;
+	byte _location;
+	byte _initialLoc;
+	Common::String _autoGet;
 
-	Item() : Location(0), InitialLoc(0) {}
+	Item() : _location(0), _initialLoc(0) {}
 };
 
 struct Tail {
-	int Version;
-	int AdventureNumber;
-	int Unknown;
+	int _version;
+	int _adventureNumber;
+	int _unknown;
 
-	Tail() : Version(0), AdventureNumber(0), Unknown(0) {}
+	Tail() : _version(0), _adventureNumber(0), _unknown(0) {}
 };
 
 /**
@@ -111,27 +111,27 @@ struct Tail {
  */
 class Scott : public GlkAPI {
 private:
-	Header GameHeader;
-	Common::Array<Item> Items;
-	Common::Array<Room> Rooms;
-	Common::StringArray Verbs;
-	Common::StringArray Nouns;
-	Common::StringArray Messages;
-	Common::Array<Action> Actions;
-	int LightRefill;
-	char NounText[16];
-	int Counters[16];   ///< Range unknown
-	int CurrentCounter;
-	int SavedRoom;
-	int RoomSaved[16];  ///< Range unknown
-	int Options;        ///< Option flags set
-	int Width;          ///< Terminal width
-	int TopHeight;      ///< Height of top window
-
-	bool split_screen;
-	winid_t Bottom, Top;
-	uint32 BitFlags;    ///< Might be >32 flags - I haven't seen >32 yet
-	int _saveSlot;		///< Save slot when loading savegame from launcher
+	Header _gameHeader;
+	Common::Array<Item> _items;
+	Common::Array<Room> _rooms;
+	Common::StringArray _verbs;
+	Common::StringArray _nouns;
+	Common::StringArray _messages;
+	Common::Array<Action> _actions;
+	int _lightRefill;
+	char _nounText[16];
+	int _counters[16];   ///< Range unknown
+	int _currentCounter;
+	int _savedRoom;
+	int _roomSaved[16];  ///< Range unknown
+	int _options;        ///< Option flags set
+	int _width;          ///< Terminal width
+	int _topHeight;      ///< Height of top window
+
+	bool _splitScreen;
+	winid_t _bottomWindow, _topWindow;
+	uint32 _bitFlags;    ///< Might be >32 flags - I haven't seen >32 yet
+	int _saveSlot;		 ///< Save slot when loading savegame from launcher
 private:
 	/**
 	 * Initialization code


Commit: fc2b1edd1fd6227e9495ada72840e9434f9095fa
    https://github.com/scummvm/scummvm/commit/fc2b1edd1fd6227e9495ada72840e9434f9095fa
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Extra returns/exits after glk_exit is called

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


diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index ff014e7..77fd9cc 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -39,8 +39,10 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 	initialize();
 
 	_bottomWindow = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
-	if (_bottomWindow == nullptr)
+	if (_bottomWindow == nullptr) {
 		glk_exit();
+		return;
+	}
 	glk_set_window(_bottomWindow);
 
 	if (_options & TRS80_STYLE) {
@@ -74,6 +76,8 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 		glk_tick();
 
 		performActions(0, 0);
+		if (shouldQuit())
+			break;
 
 		if (_saveSlot >= 0) {
 			// Load any savegame during startup
@@ -1019,7 +1023,7 @@ doneit:
 }
 
 int Scott::performActions(int vb, int no) {
-	static int disable_sysfunc = 0; // Recursion lock
+	static bool disableSysFunc = false; // Recursion lock
 	int d = _bitFlags & (1 << DARKBIT);
 
 	int ct = 0;
@@ -1047,6 +1051,7 @@ int Scott::performActions(int vb, int no) {
 			else
 				output("I fell down and broke my neck. ");
 			glk_exit();
+			return 0;
 		}
 		if (_options & YOUARE)
 			output("You can't go in that direction. ");
@@ -1093,7 +1098,7 @@ int Scott::performActions(int vb, int no) {
 		if (ct <= _gameHeader._numActions && _actions[ct]._vocab != 0)
 			doagain = 0;
 	}
-	if (fl != 0 && disable_sysfunc == 0) {
+	if (fl != 0 && disableSysFunc == 0) {
 		int item;
 		if (_items[LIGHT_SOURCE]._location == MY_LOC ||
 		        _items[LIGHT_SOURCE]._location == CARRIED)
@@ -1112,9 +1117,12 @@ int Scott::performActions(int vb, int no) {
 					while (i <= _gameHeader._numItems) {
 						if (_items[i]._location == MY_LOC && _items[i]._autoGet != nullptr && _items[i]._autoGet[0] != '*') {
 							no = whichWord(_items[i]._autoGet.c_str(), _nouns);
-							disable_sysfunc = 1;    // Don't recurse into auto get !
-							performActions(vb, no); // Recursively check each items table code
-							disable_sysfunc = 0;
+							disableSysFunc = true;    // Don't recurse into auto get !
+							performActions(vb, no);   // Recursively check each items table code
+							disableSysFunc = false;
+							if (shouldQuit())
+								return 0;
+
 							if (countCarried() == _gameHeader._maxCarry) {
 								if (_options & YOUARE)
 									output("You are carrying too much. ");
@@ -1164,9 +1172,12 @@ int Scott::performActions(int vb, int no) {
 						if (_items[i]._location == CARRIED && !_items[i]._autoGet.empty()
 								&& !_items[i]._autoGet.hasPrefix("*")) {
 							no = whichWord(_items[i]._autoGet.c_str(), _nouns);
-							disable_sysfunc = 1;
+							disableSysFunc = true;
 							performActions(vb, no);
-							disable_sysfunc = 0;
+							disableSysFunc = false;
+							if (shouldQuit())
+								return 0;
+
 							_items[i]._location = MY_LOC;
 							output(_items[i]._text);
 							output(": O.K.\n");


Commit: bc4df65bd7b81dc5cec98f6681e9d83f7c22dc8e
    https://github.com/scummvm/scummvm/commit/bc4df65bd7b81dc5cec98f6681e9d83f7c22dc8e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix gcc compiler warnings

Changed paths:
    engines/glk/blorb.cpp
    engines/glk/frotz/detection.cpp
    engines/glk/frotz/glk_interface.h
    engines/glk/frotz/mem.h
    engines/glk/frotz/processor.h
    engines/glk/frotz/processor_screen.cpp
    engines/glk/glk_api.h
    engines/glk/window_graphics.cpp


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index e298e1c..ed730bf 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -35,7 +35,7 @@ struct giblorb_chunkdesc_struct {
     glui32 startpos;	///< start of chunk header
     glui32 datpos;		///< start of data (either startpos or startpos+8)
 
-    void *ptr;		///< pointer to malloc'd data, if loaded
+    byte *ptr;		///< pointer to malloc'd data, if loaded
     int auxdatnum;	///< entry in the auxsound/auxpict array; -1 if none. This only applies to chunks that represent resources;
 };
 typedef giblorb_chunkdesc_struct giblorb_chunkdesc_t;
@@ -406,7 +406,7 @@ giblorb_err_t Blorb::giblorb_load_chunk_by_number(giblorb_map_t *map,
 		glui32 method, giblorb_result_t *res, glui32 chunknum) {
 	giblorb_chunkdesc_t *chu;
 
-	if (chunknum < 0 || chunknum >= map->numchunks)
+	if (chunknum >= map->numchunks)
 		return giblorb_err_NotFound;
 
 	chu = &(map->chunks[chunknum]);
@@ -451,7 +451,7 @@ giblorb_err_t Blorb::giblorb_load_chunk_by_number(giblorb_map_t *map,
 giblorb_err_t Blorb::giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum) {
 	giblorb_chunkdesc_t *chu;
 
-	if (chunknum < 0 || chunknum >= map->numchunks)
+	if (chunknum >= map->numchunks)
 		return giblorb_err_NotFound;
 
 	chu = &(map->chunks[chunknum]);
diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index 61d9a14..29eb5d7 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -93,7 +93,7 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 				if (dot)
 					fname = Common::String(fname.c_str(), dot);
 
-				debug("ENTRY0(\"%s\", \"%s-%s\", \"%s\", %u),",
+				debug("ENTRY0(\"%s\", \"%s-%s\", \"%s\", %lu),",
 					folderName.c_str(), fname.c_str(), serial, md5.c_str(), filesize);
 			}
 			const PlainGameDescriptor &desc = FROTZ_GAME_LIST[0];
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index c18ebb8..0103f88 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -174,6 +174,7 @@ public:
 	 * Constructor
 	 */
 	GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc);
+	virtual ~GlkInterface() {}
 
 	/**
 	 * Initialization
diff --git a/engines/glk/frotz/mem.h b/engines/glk/frotz/mem.h
index 43b2727..372c7e7 100644
--- a/engines/glk/frotz/mem.h
+++ b/engines/glk/frotz/mem.h
@@ -128,8 +128,8 @@ public:
 	zbyte h_screen_cols;
 	zword h_screen_width;
 	zword h_screen_height;
-	zbyte h_font_height = 1;
-	zbyte h_font_width = 1;
+	zbyte h_font_height;
+	zbyte h_font_width;
 	zword h_functions_offset;
 	zword h_strings_offset;
 	zbyte h_default_background;
@@ -267,6 +267,7 @@ public:
 	 * Constructor
 	 */
 	Mem();
+	virtual ~Mem() {}
 
 	/**
 	 * Initialize
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index 1a1dfc5..7c8944f 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -1529,6 +1529,7 @@ public:
 	 * Constructor
 	 */
 	Processor(OSystem *syst, const GlkGameDescription &gameDesc);
+	virtual ~Processor() {}
 
 	/**
 	 * Initialization
diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index 6c25ef9..2e07b47 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -74,7 +74,7 @@ void Processor::screen_char(zchar c) {
 			cury ++;
 		} else {
 			if (cury == 1) {
-				if (curx <= ((sizeof statusline / sizeof(zchar)) - 1)) {
+				if (curx <= (int)((sizeof statusline / sizeof(zchar)) - 1)) {
 					statusline[curx - 1] = c;
 					statusline[curx] = 0;
 				}
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
index 8a37df1..400f3c0 100644
--- a/engines/glk/glk_api.h
+++ b/engines/glk/glk_api.h
@@ -44,6 +44,7 @@ public:
 	 * Constructor
 	 */
 	GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc);
+	virtual ~GlkAPI() {}
 
 	void glk_exit(void);
 	void glk_set_interrupt_handler(void(*func)(void));
diff --git a/engines/glk/window_graphics.cpp b/engines/glk/window_graphics.cpp
index e80fc8e..f81313e 100644
--- a/engines/glk/window_graphics.cpp
+++ b/engines/glk/window_graphics.cpp
@@ -40,15 +40,12 @@ GraphicsWindow::~GraphicsWindow() {
 void GraphicsWindow::rearrange(const Rect &box) {
 	int newwid, newhgt;
 	int bothwid, bothhgt;
-	int oldw, oldh;
 	Graphics::ManagedSurface *newSurface;
 
 	_bbox = box;
 
 	newwid = box.width();
 	newhgt = box.height();
-	oldw = _w;
-	oldh = _h;
 
 	if (newwid <= 0 || newhgt <= 0) {
 		_w = 0;


Commit: e344809eeac75e9dbfd943015c083001a2628440
    https://github.com/scummvm/scummvm/commit/e344809eeac75e9dbfd943015c083001a2628440
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix gcc warnings, adding extra method comments

Changed paths:
    engines/glk/frotz/glk_interface.h
    engines/glk/frotz/mem.cpp
    engines/glk/frotz/processor.h
    engines/glk/frotz/processor_buffer.cpp
    engines/glk/frotz/processor_screen.cpp
    engines/glk/frotz/processor_streams.cpp


diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index 0103f88..5e2fa62 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -94,10 +94,33 @@ public:
 	bool _soundLocked;
 	bool _soundPlaying;
 protected:
+	/**
+	 * Return the length of the character in screen units.
+	 */
 	int os_char_width(zchar z);
+
+	/**
+	 * Calculate the length of a word in screen units. Apart from letters,
+	 * the word may contain special codes:
+	 *
+	 *    ZC_NEW_STYLE - next character is a new text style
+	 *    ZC_NEW_FONT  - next character is a new font
+	 */
 	int os_string_width(const zchar *s);
+
+	/**
+	 * Return the length of a string
+	 */
 	int os_string_length(zchar *s);
+
+	/**
+	 * Prepare a sample for playing
+	 */
 	void os_prepare_sample(int a);
+
+	/**
+	 * Signal that a given sample is finished with
+	 */
 	void os_finish_with_sample(int a);
 
 	/**
diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index 378e496..6ee6781 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -174,10 +174,10 @@ void Mem::initializeStoryFile() {
 }
 
 void Mem::initializeUndo() {
-	void *reserved = nullptr;
+	byte *reserved = nullptr;
 
 	if (reserve_mem != 0) {
-		if ((reserved = malloc(reserve_mem)) == NULL)
+		if ((reserved = new byte[reserve_mem]) == nullptr)
 			return;
 	}
 
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index 7c8944f..5f61a2e 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -314,11 +314,32 @@ private:
 	 * @{
 	 */
 
+	/**
+	 * Start printing a so-called debugging message. The contents of the
+	 * message are passed to the message stream, a Frotz specific output
+	 * stream with maximum priority.
+	 */
+	void screen_mssg_on();
+
+	/**
+	 * Stop printing a "debugging" message
+	 */
+	void screen_mssg_off();
+
+	/**
+	 * Display a single character on the screen.
+	 */
 	void screen_char(zchar c);
+
+	/**
+	 * Print a newline to the screen.
+	 */
 	void screen_new_line();
+
+	/**
+	 * Print a newline to the screen.
+	 */
 	void screen_word(const zchar *s);
-	void screen_mssg_on();
-	void screen_mssg_off();
 
 	/**@}*/
 
diff --git a/engines/glk/frotz/processor_buffer.cpp b/engines/glk/frotz/processor_buffer.cpp
index c4eaeaa..b8b37ed 100644
--- a/engines/glk/frotz/processor_buffer.cpp
+++ b/engines/glk/frotz/processor_buffer.cpp
@@ -158,7 +158,7 @@ void Processor::runtimeError(ErrorCode errNum) {
 	if (_err_report_mode == ERR_REPORT_FATAL
 		|| (!_ignore_errors && errNum <= ERR_MAX_FATAL)) {
 		flush_buffer();
-		error(ERR_MESSAGES[errNum - 1]);
+		error("%s", ERR_MESSAGES[errNum - 1]);
 		return;
 	}
 
diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index 2e07b47..c0d8ca1 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -122,7 +122,6 @@ void Processor::screen_word(const zchar *s) {
 	}
 }
 
-
 void Processor::z_buffer_mode() {
 	// No implementation
 }
diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
index 93b9c0b..ebeb303 100644
--- a/engines/glk/frotz/processor_streams.cpp
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -174,7 +174,7 @@ zchar Processor::stream_read_input(int max, zchar *buf, zword timeout, zword rou
 
     // Remove initial input from the transscript file or from the screen
     if (ostream_script && enable_scripting && !no_scripting)
-	script_erase_input(buf);
+		script_erase_input(buf);
 
     // Read input line from current input stream
 continue_input:


Commit: 3d34cd151f9f56d24299eba4372e71e4a6b26131
    https://github.com/scummvm/scummvm/commit/3d34cd151f9f56d24299eba4372e71e4a6b26131
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Remove unused local variable

Changed paths:
    engines/glk/frotz/processor_streams.cpp


diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
index ebeb303..091165e 100644
--- a/engines/glk/frotz/processor_streams.cpp
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -165,11 +165,6 @@ continue_input:
 zchar Processor::stream_read_input(int max, zchar *buf, zword timeout, zword routine,
 			  bool hot_keys, bool no_scripting) {
     zchar key = ZC_BAD;
-    bool no_scrollback = no_scripting;
-
-    if (h_version == V6 && _storyId == UNKNOWN && !ostream_script)
-		no_scrollback = false;
-
     flush_buffer();
 
     // Remove initial input from the transscript file or from the screen


Commit: 4b011b2f1cfdc3aac0468248f4feb9faea3501a2
    https://github.com/scummvm/scummvm/commit/4b011b2f1cfdc3aac0468248f4feb9faea3501a2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Freeing of data on exit

Changed paths:
    engines/glk/selection.cpp
    engines/glk/selection.h
    engines/glk/windows.cpp
    engines/glk/windows.h


diff --git a/engines/glk/selection.cpp b/engines/glk/selection.cpp
index 566ae65..f251171 100644
--- a/engines/glk/selection.cpp
+++ b/engines/glk/selection.cpp
@@ -65,14 +65,21 @@ WindowMask::WindowMask() : _hor(0), _ver(0), _links(nullptr) {
 	resize(g_system->getWidth(), g_system->getHeight());
 }
 
-void WindowMask::resize(size_t x, size_t y) {
-	// Deallocate old storage
+WindowMask::~WindowMask() {
+	clear();
+}
+
+void WindowMask::clear() {
 	for (size_t i = 0; i < _hor; i++) {
 		if (_links[i])
 			delete _links[i];
 	}
 
 	delete _links;
+}
+
+void WindowMask::resize(size_t x, size_t y) {
+	clear();
 
 	_hor = x + 1;
 	_ver = y + 1;
diff --git a/engines/glk/selection.h b/engines/glk/selection.h
index 4497d68..10d6d5c 100644
--- a/engines/glk/selection.h
+++ b/engines/glk/selection.h
@@ -63,6 +63,11 @@ public:
  * Manages hyperlinks for the screen
  */
 class WindowMask {
+private:
+	/**
+	 * Clear the links data
+	 */
+	void clear();
 public:
 	size_t _hor, _ver;
 	glui32 **_links;
@@ -75,6 +80,11 @@ public:
 	WindowMask();
 
 	/**
+	 * Destructor
+	 */
+	~WindowMask();
+
+	/**
 	 * Resize the links array
 	 */
 	void resize(size_t x, size_t y);
diff --git a/engines/glk/windows.cpp b/engines/glk/windows.cpp
index 02298ad..ce1929f 100644
--- a/engines/glk/windows.cpp
+++ b/engines/glk/windows.cpp
@@ -70,6 +70,10 @@ Windows::Windows(Graphics::Screen *screen) : _screen(screen), _windowList(nullpt
 	_zcolor_Bright[0] = _zcolor_Bright[1] = _zcolor_Bright[2] = 0;
 }
 
+Windows::~Windows() {
+	delete _rootWin;
+}
+
 Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
                             glui32 wintype, glui32 rock) {
 	Window *newwin, *oldparent;
diff --git a/engines/glk/windows.h b/engines/glk/windows.h
index 98f0d2d..7abb9e5 100644
--- a/engines/glk/windows.h
+++ b/engines/glk/windows.h
@@ -152,6 +152,11 @@ public:
 	Windows(Graphics::Screen *screen);
 
 	/**
+	 * Destructor
+	 */
+	~Windows();
+
+	/**
 	 * Open a new window
 	 */
 	Window *windowOpen(Window *splitwin, glui32 method, glui32 size,


Commit: 813918aaba8c2f86d55c4b256da7ee79c155b12f
    https://github.com/scummvm/scummvm/commit/813918aaba8c2f86d55c4b256da7ee79c155b12f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Memory leak fixes

Changed paths:
    engines/glk/selection.cpp
    engines/glk/streams.cpp
    engines/glk/streams.h


diff --git a/engines/glk/selection.cpp b/engines/glk/selection.cpp
index f251171..9109776 100644
--- a/engines/glk/selection.cpp
+++ b/engines/glk/selection.cpp
@@ -72,10 +72,10 @@ WindowMask::~WindowMask() {
 void WindowMask::clear() {
 	for (size_t i = 0; i < _hor; i++) {
 		if (_links[i])
-			delete _links[i];
+			delete[] _links[i];
 	}
 
-	delete _links;
+	delete[] _links;
 }
 
 void WindowMask::resize(size_t x, size_t y) {
diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
index 801cd54..cd7130f 100644
--- a/engines/glk/streams.cpp
+++ b/engines/glk/streams.cpp
@@ -75,6 +75,10 @@ void Stream::setReverseVideo(bool reverse) {
 
 /*--------------------------------------------------------------------------*/
 
+WindowStream::~WindowStream() {
+	_window->_stream = nullptr;
+}
+
 void WindowStream::close(StreamResult *result) {
 	warning("cannot close window stream");
 }
@@ -1407,8 +1411,10 @@ Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) {
 }
 
 Streams::~Streams() {
-	while (_streamList)
-		delete _streamList;
+	for (Stream *currStream = _streamList, *nextStream; currStream; currStream = nextStream) {
+		nextStream = currStream->_next;
+		delete currStream;
+	}
 }
 
 FileStream *Streams::openFileStream(frefid_t fref, glui32 fmode, glui32 rock, bool unicode) {
diff --git a/engines/glk/streams.h b/engines/glk/streams.h
index 32bde70..22a0a59 100644
--- a/engines/glk/streams.h
+++ b/engines/glk/streams.h
@@ -300,6 +300,11 @@ public:
 		Stream(streams, false, true, rock, unicode), _window(window) {}
 
 	/**
+	 * Destructor
+	 */
+	virtual ~WindowStream();
+
+	/**
 	 * Close the stream
 	 */
 	virtual void close(StreamResult *result = nullptr);


Commit: 326f69136eb4fee72391a8175ad722a317f84ca6
    https://github.com/scummvm/scummvm/commit/326f69136eb4fee72391a8175ad722a317f84ca6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Fix memory corruption loading game data

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


diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index 77fd9cc..fed31f0 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -104,7 +104,7 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 		}
 
 		// Brian Howarth games seem to use -1 for forever
-		if (_items[LIGHT_SOURCE]._location/*==-1*/ != DESTROYED && _gameHeader._lightTime != -1) {
+		if (_items[LIGHT_SOURCE]._location != DESTROYED && _gameHeader._lightTime != -1) {
 			_gameHeader._lightTime--;
 			if (_gameHeader._lightTime < 1) {
 				_bitFlags |= (1 << LIGHTOUTBIT);
@@ -282,9 +282,6 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	int unused, ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
 	int ct;
 	int lo;
-	Action *ap;
-	Room *rp;
-	Item *ip;
 
 	// Load the header
 	readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, &lt, &mn, &trm);
@@ -309,89 +306,66 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	_gameHeader._treasureRoom = trm;
 
 	// Load the actions
-	ct = 0;
-	ap = &_actions[0];
 	if (loud)
 		debug("Reading %d actions.", na);
-	while (ct < na + 1) {
+
+	for (uint idx = 0; idx < na + 1; ++idx) {
+		Action &a = _actions[idx];
 		readInts(f, 8,
-		         &ap->_vocab,
-		         &ap->_condition[0],
-		         &ap->_condition[1],
-		         &ap->_condition[2],
-		         &ap->_condition[3],
-		         &ap->_condition[4],
-		         &ap->_action[0],
-		         &ap->_action[1]);
-		ap++;
-		ct++;
+			&a._vocab, &a._condition[0], &a._condition[1], &a._condition[2],
+			&a._condition[3], &a._condition[4], &a._action[0], &a._action[1]);
 	}
 
-	ct = 0;
 	if (loud)
 		debug("Reading %d word pairs.", nw);
-	while (ct < nw + 1) {
-		_verbs[ct] = readString(f);
-		_nouns[ct] = readString(f);
-		ct++;
+	for (int idx = 0; idx < nw + 1; ++idx) {
+		_verbs[idx] = readString(f);
+		_nouns[idx] = readString(f);
 	}
-	ct = 0;
-	rp = &_rooms[0];
+
 	if (loud)
 		debug("Reading %d rooms.", nr);
-	while (ct < nr + 1) {
-		readInts(f, 6,
-		         &rp->_exits[0], &rp->_exits[1], &rp->_exits[2],
-		         &rp->_exits[3], &rp->_exits[4], &rp->_exits[5]);
-
-		rp->_text = readString(f);
-		ct++;
-		rp++;
+	for (int idx = 0; idx < nr + 1; ++idx) {
+		Room &r = _rooms[idx];
+		readInts(f, 6, &r._exits[0], &r._exits[1], &r._exits[2],
+		         &r._exits[3], &r._exits[4], &r._exits[5]);
+		r._text =  readString(f);
 	}
 
-	ct = 0;
 	if (loud)
 		debug("Reading %d messages.", mn);
-	while (ct < mn + 1) {
-		_messages[ct] = readString(f);
-		ct++;
-	}
+	for (int idx = 0; idx < mn + 1; ++idx)
+		_messages[idx] = readString(f);
 
-	ct = 0;
 	if (loud)
 		debug("Reading %d items.", ni);
-	ip = &_items[0];
-	while (ct < ni + 1) {
-		ip->_text = readString(f);
+	for (int idx = 0; idx < ni + 1; ++idx) {
+		Item &i = _items[idx];
+		i._text = readString(f);
 
-		const char *p = strchr(ip->_text.c_str(), '/');
+		const char *p = strchr(i._text.c_str(), '/');
 		if (p) {
-			ip->_autoGet = Common::String(p);
+			i._autoGet = Common::String(p);
 
 			// Some games use // to mean no auto get/drop word!
-			if (!ip->_autoGet.hasPrefix("//") && !ip->_autoGet.hasPrefix("/*")) {
-				ip->_text = Common::String(ip->_text.c_str(), p);
-				ip->_autoGet.deleteChar(0);
+			if (!i._autoGet.hasPrefix("//") && !i._autoGet.hasPrefix("/*")) {
+				i._text = Common::String(i._text.c_str(), p);
+				i._autoGet.deleteChar(0);
 
-				const char *t = strchr(ip->_autoGet.c_str(), '/');
+				const char *t = strchr(i._autoGet.c_str(), '/');
 				if (t)
-					ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+					i._autoGet = Common::String(i._autoGet.c_str(), t);
 			}
 		}
 
 		readInts(f, 1, &lo);
-		ip->_location = (unsigned char)lo;
-		ip->_initialLoc = ip->_location;
-		ip++;
-		ct++;
+		i._location = (unsigned char)lo;
+		i._initialLoc = i._location;
 	}
-	ct = 0;
 
 	// Skip Comment Strings
-	while (ct < na + 1) {
+	for (int idx = 0; idx < na + 1; ++idx)
 		readString(f);
-		ct++;
-	}
 
 	readInts(f, 1, &ct);
 	if (loud)
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 955ebb9..4741892 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -70,9 +70,9 @@ struct Header {
 };
 
 struct Action {
-	uint _vocab;
-	uint _condition[5];
-	uint _action[2];
+	int _vocab;
+	int _condition[5];
+	int _action[2];
 
 	Action() : _vocab(0) {
 		Common::fill(&_condition[0], &_condition[5], 0);
@@ -82,7 +82,7 @@ struct Action {
 
 struct Room {
 	Common::String _text;
-	short _exits[6];
+	int _exits[6];
 
 	Room() {
 		Common::fill(&_exits[0], &_exits[6], 0);


Commit: b05a16a0ad0dcc2881aeda81e0d1a84a752eccab
    https://github.com/scummvm/scummvm/commit/b05a16a0ad0dcc2881aeda81e0d1a84a752eccab
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Centralizing more of the savegame code in GlkEngine

Changed paths:
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/frotz.h
    engines/glk/frotz/processor_streams.cpp
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/scott/scott.cpp
    engines/glk/scott/scott.h
    engines/glk/streams.h


diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index a6371b6..344aa92 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -84,13 +84,174 @@ void Frotz::initialize() {
 	z_restart();
 }
 
-Common::Error Frotz::loadGameState(int slot) {
-	// TODO
+Common::Error Frotz::saveGameData(strid_t file) {
+#ifdef TODO
+	long pc;
+	zword addr;
+	zword nsp, nfp;
+	int skip;
+	int i;
+
+	/* Open game file */
+
+	if ((gfp = frotzopenprompt(FILE_SAVE)) == nullptr)
+		goto finished;
+
+	if (_save_quetzal) {
+		success = save_quetzal(gfp, story_fp, blorb_ofs);
+	}
+	else {
+		/* Write game file */
+
+		fputc((int)hi(h_release), gfp);
+		fputc((int)lo(h_release), gfp);
+		fputc((int)hi(h_checksum), gfp);
+		fputc((int)lo(h_checksum), gfp);
+
+		GET_PC(pc)
+
+			fputc((int)(pc >> 16) & 0xff, gfp);
+		fputc((int)(pc >> 8) & 0xff, gfp);
+		fputc((int)(pc)& 0xff, gfp);
+
+		nsp = (int)(_sp - _stack);
+		nfp = (int)(_fp - _stack);
+
+		fputc((int)hi(nsp), gfp);
+		fputc((int)lo(nsp), gfp);
+		fputc((int)hi(nfp), gfp);
+		fputc((int)lo(nfp), gfp);
+
+		for (i = nsp; i < STACK_SIZE; i++) {
+			fputc((int)hi(_stack[i]), gfp);
+			fputc((int)lo(_stack[i]), gfp);
+		}
+
+		fseek(story_fp, blorb_ofs, SEEK_SET);
+
+		for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
+			if (zmp[addr] != fgetc(story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
+				fputc(skip, gfp);
+				fputc(zmp[addr], gfp);
+				skip = 0;
+			}
+			else skip++;
+	}
+
+	/* Close game file and check for errors */
+
+	if (fclose(gfp) == EOF || ferror(story_fp)) {
+		print_string("Error writing save file\n");
+		goto finished;
+	}
+
+	/* Success */
+
+	success = 1;
+#endif
 	return Common::kNoError;
 }
 
-Common::Error Frotz::saveGameState(int slot, const Common::String &desc) {
-	// TODO
+Common::Error Frotz::loadGameData(strid_t file) {
+#ifdef TODO
+	long pc;
+	zword release;
+	zword addr;
+	int i;
+
+	/* Open game file */
+
+	if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
+		goto finished;
+
+	if (_save_quetzal) {
+		success = restore_quetzal (gfp, story_fp, blorb_ofs);
+
+	} else {
+		/* Load game file */
+
+		release = (unsigned) fgetc (gfp) << 8;
+		release |= fgetc (gfp);
+
+		() fgetc (gfp);
+		() fgetc (gfp);
+
+		/* Check the release number */
+
+		if (release == h_release) {
+
+			pc = (long) fgetc (gfp) << 16;
+			pc |= (unsigned) fgetc (gfp) << 8;
+			pc |= fgetc (gfp);
+
+			SET_PC (pc);
+
+			_sp = _stack + (fgetc (gfp) << 8);
+			_sp += fgetc (gfp);
+			_fp = _stack + (fgetc (gfp) << 8);
+			_fp += fgetc (gfp);
+
+			for (i = (int) (_sp - _stack); i < STACK_SIZE; i++) {
+				_stack[i] = (unsigned) fgetc (gfp) << 8;
+				_stack[i] |= fgetc (gfp);
+			}
+
+			fseek (story_fp, blorb_ofs, SEEK_SET);
+
+			for (addr = 0; addr < h_dynamic_size; addr++) {
+				int skip = fgetc (gfp);
+				for (i = 0; i < skip; i++)
+					zmp[addr++] = fgetc (story_fp);
+				zmp[addr] = fgetc (gfp);
+				() fgetc (story_fp);
+			}
+
+			/* Check for errors */
+
+			if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
+				success = -1;
+			else
+
+				/* Success */
+
+				success = 2;
+
+		} else print_string ("Invalid save file\n");
+	}
+
+	if ((short) success >= 0) {
+
+		/* Close game file */
+
+		fclose (gfp);
+
+		if ((short) success > 0) {
+			zbyte old_screen_rows;
+			zbyte old_screen_cols;
+
+			/* In V3, reset the upper window. */
+			if (h_version == V3)
+				split_window (0);
+
+			LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
+			LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
+
+			/* Reload cached header fields. */
+			restart_header ();
+
+			/*
+				* Since QUETZAL files may be saved on many different machines,
+				* the screen sizes may vary a lot. Erasing the status window
+				* seems to cover up most of the resulting badness.
+				*/
+			if (h_version > V3 && h_version != V6
+					&& (h_screen_rows != old_screen_rows
+						|| h_screen_cols != old_screen_cols))
+				erase_window (1);
+		}
+	} else
+		os_fatal ("Error reading save file");
+#endif
 	return Common::kNoError;
 }
 
diff --git a/engines/glk/frotz/frotz.h b/engines/glk/frotz/frotz.h
index e45abc0..00aeb59 100644
--- a/engines/glk/frotz/frotz.h
+++ b/engines/glk/frotz/frotz.h
@@ -54,14 +54,14 @@ public:
 	virtual void runGame(Common::SeekableReadStream *gameFile) override;
 
 	/**
-	 * Load a savegame
+	 * Load a savegame from the passed stream
 	 */
-	virtual Common::Error loadGameState(int slot) override;
+	virtual Common::Error loadGameData(strid_t file) override;
 
 	/**
-	 * Save the game
+	 * Save the game to the passed stream
 	 */
-	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
+	virtual Common::Error saveGameData(strid_t file) override;
 };
 
 extern Frotz *g_vm;
diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
index 091165e..cbef38a 100644
--- a/engines/glk/frotz/processor_streams.cpp
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -545,227 +545,54 @@ void Processor::z_restart() {
 }
 
 void Processor::z_save() {
-#ifdef TODO
 	bool success = false;
 
 	if (zargc != 0) {
 		// Open auxilary file
 		frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
 			filemode_Write, 0);
-		if (ref == nullptr)
-			goto finished;
+		if (ref != nullptr) {
+			// Write data
+			strid_t f = glk_stream_open_file(ref, filemode_Write);
 
-		// Write data
-		strid_t f = glk_stream_open_file(ref, filemode_Write);
+			glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]);
 
-		glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]);
-
-		glk_stream_close(f);
-
-	} else {
-		long pc;
-		zword addr;
-		zword nsp, nfp;
-		int skip;
-		int i;
-
-		/* Open game file */
-
-		if ((gfp = frotzopenprompt (FILE_SAVE)) == nullptr)
-			goto finished;
-
-		if (_save_quetzal) {
-			success = save_quetzal (gfp, story_fp, blorb_ofs);
-		} else {
-			/* Write game file */
-
-			fputc ((int) hi (h_release), gfp);
-			fputc ((int) lo (h_release), gfp);
-			fputc ((int) hi (h_checksum), gfp);
-			fputc ((int) lo (h_checksum), gfp);
-
-			GET_PC (pc)
-
-				fputc ((int) (pc >> 16) & 0xff, gfp);
-			fputc ((int) (pc >> 8) & 0xff, gfp);
-			fputc ((int) (pc) & 0xff, gfp);
-
-			nsp = (int) (_sp - _stack);
-			nfp = (int) (_fp - _stack);
-
-			fputc ((int) hi (nsp), gfp);
-			fputc ((int) lo (nsp), gfp);
-			fputc ((int) hi (nfp), gfp);
-			fputc ((int) lo (nfp), gfp);
-
-			for (i = nsp; i < STACK_SIZE; i++) {
-				fputc ((int) hi (_stack[i]), gfp);
-				fputc ((int) lo (_stack[i]), gfp);
-			}
-
-			fseek (story_fp, blorb_ofs, SEEK_SET);
-
-			for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
-				if (zmp[addr] != fgetc (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
-					fputc (skip, gfp);
-					fputc (zmp[addr], gfp);
-					skip = 0;
-				} else skip++;
+			glk_stream_close(f);
+			success = true;
 		}
-
-		/* Close game file and check for errors */
-
-		if (fclose (gfp) == EOF || ferror (story_fp)) {
-			print_string ("Error writing save file\n");
-			goto finished;
-		}
-
-		/* Success */
-
-		success = 1;
-
+	} else {
+		success = saveGame().getCode() == Common::kNoError;
 	}
 
-finished:
-
 	if (h_version <= V3)
 		branch (success);
 	else
 		store (success);
-#endif
 }
 
 void Processor::z_restore() {
-#ifdef TODO
-	FILE *gfp;
-
-	zword success = 0;
+	bool success = false;
 
 	if (zargc != 0) {
+		frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
+			filemode_Read, 0);
+		if (ref != nullptr) {
+			// Write data
+			strid_t f = glk_stream_open_file(ref, filemode_Read);
 
-		/* Get the file name */
-
-		/* Open auxilary file */
-
-		if ((gfp = frotzopenprompt(FILE_LOAD_AUX)) == nullptr)
-			goto finished;
-
-		/* Load auxilary file */
-
-		success = fread (zmp + zargs[0], 1, zargs[1], gfp);
-
-		/* Close auxilary file */
-
-		fclose (gfp);
-
-	} else {
-
-		long pc;
-		zword release;
-		zword addr;
-		int i;
-
-		/* Open game file */
-
-		if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
-			goto finished;
-
-		if (_save_quetzal) {
-			success = restore_quetzal (gfp, story_fp, blorb_ofs);
-
-		} else {
-			/* Load game file */
-
-			release = (unsigned) fgetc (gfp) << 8;
-			release |= fgetc (gfp);
-
-			() fgetc (gfp);
-			() fgetc (gfp);
-
-			/* Check the release number */
-
-			if (release == h_release) {
-
-				pc = (long) fgetc (gfp) << 16;
-				pc |= (unsigned) fgetc (gfp) << 8;
-				pc |= fgetc (gfp);
-
-				SET_PC (pc);
-
-				_sp = _stack + (fgetc (gfp) << 8);
-				_sp += fgetc (gfp);
-				_fp = _stack + (fgetc (gfp) << 8);
-				_fp += fgetc (gfp);
-
-				for (i = (int) (_sp - _stack); i < STACK_SIZE; i++) {
-					_stack[i] = (unsigned) fgetc (gfp) << 8;
-					_stack[i] |= fgetc (gfp);
-				}
-
-				fseek (story_fp, blorb_ofs, SEEK_SET);
-
-				for (addr = 0; addr < h_dynamic_size; addr++) {
-					int skip = fgetc (gfp);
-					for (i = 0; i < skip; i++)
-						zmp[addr++] = fgetc (story_fp);
-					zmp[addr] = fgetc (gfp);
-					() fgetc (story_fp);
-				}
-
-				/* Check for errors */
-
-				if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
-					success = -1;
-				else
-
-					/* Success */
-
-					success = 2;
+			glk_get_buffer_stream(f, (char *)zmp + zargs[0], zargs[1]);
 
-			} else print_string ("Invalid save file\n");
+			glk_stream_close(f);
+			success = true;
 		}
-
-		if ((short) success >= 0) {
-
-			/* Close game file */
-
-			fclose (gfp);
-
-			if ((short) success > 0) {
-				zbyte old_screen_rows;
-				zbyte old_screen_cols;
-
-				/* In V3, reset the upper window. */
-				if (h_version == V3)
-					split_window (0);
-
-				LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
-				LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
-
-				/* Reload cached header fields. */
-				restart_header ();
-
-				/*
-				 * Since QUETZAL files may be saved on many different machines,
-				 * the screen sizes may vary a lot. Erasing the status window
-				 * seems to cover up most of the resulting badness.
-				 */
-				if (h_version > V3 && h_version != V6
-						&& (h_screen_rows != old_screen_rows
-							|| h_screen_cols != old_screen_cols))
-					erase_window (1);
-			}
-		} else
-			os_fatal ("Error reading save file");
+	} else {
+		success = loadGame().getCode() == Common::kNoError;
 	}
 
-finished:
-
 	if (h_version <= V3)
 		branch (success);
 	else
 		store (success);
-#endif
 }
 
 void Processor::z_verify() {
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 987fbc8..a3cd167 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -117,4 +117,56 @@ void GlkEngine::GUIError(const char *msg, ...) {
 	GUIErrorMessage(buffer);
 }
 
+Common::Error GlkEngine::loadGame() {
+	frefid_t ref = _streams->createByPrompt(fileusage_BinaryMode | fileusage_SavedGame,
+		filemode_Read, 0);
+	if (ref == nullptr)
+		return Common::kReadingFailed;
+
+	int slotNumber = ref->_slotNumber;
+	_streams->deleteRef(ref);
+
+	return loadGameState(slotNumber);
+}
+
+Common::Error GlkEngine::saveGame() {
+	frefid_t ref = _streams->createByPrompt(fileusage_BinaryMode | fileusage_SavedGame,
+		filemode_Write, 0);
+	if (ref == nullptr)
+		return Common::kWritingFailed;
+
+	int slot = ref->_slotNumber;
+	Common::String desc = ref->_description;
+	_streams->deleteRef(ref);
+
+	return saveGameState(slot, desc);
+}
+
+Common::Error GlkEngine::loadGameState(int slot) {
+	FileReference ref(slot, "", fileusage_SavedGame | fileusage_TextMode);
+
+	strid_t file = _streams->openFileStream(&ref, filemode_Read);
+	if (file == nullptr)
+		return Common::kReadingFailed;
+
+	Common::Error result = saveGameData(file);
+
+	file->close();
+	return result;
+}
+
+Common::Error GlkEngine::saveGameState(int slot, const Common::String &desc) {
+	Common::String msg;
+	FileReference ref(slot, desc, fileusage_BinaryMode | fileusage_SavedGame);
+
+	strid_t file = _streams->openFileStream(&ref, filemode_Write);
+	if (file == nullptr)
+		return Common::kWritingFailed;
+
+	Common::Error result = loadGameData(file);
+
+	file->close();
+	return result;
+}
+
 } // End of namespace Glk
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 8dce78f..f925fd0 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -30,6 +30,7 @@
 #include "engines/advancedDetector.h"
 #include "engines/engine.h"
 #include "glk/glk_types.h"
+#include "glk/streams.h"
 
 namespace Glk {
 
@@ -181,6 +182,11 @@ public:
 	}
 
 	/**
+	 * Display a message in a GUI dialog
+	 */
+	void GUIError(const char *msg, ...);
+
+	/**
 	 * Return the filename for a given save slot
 	 */
 	Common::String getSaveName(uint slot) const {
@@ -188,9 +194,34 @@ public:
 	}
 
 	/**
-	 * Display a message in a GUI dialog
+	 * Prompt the user for a savegame to load, and then load it
 	 */
-	void GUIError(const char *msg, ...);
+	Common::Error loadGame();
+
+	/**
+	 * Prompt the user to save their game, and then save it
+	 */
+	Common::Error saveGame();
+
+	/**
+	 * Load a savegame from a given slot
+	 */
+	virtual Common::Error loadGameState(int slot) override;
+
+	/**
+	 * Save the game to a given slot
+	 */
+	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
+
+	/**
+	 * Load a savegame from the passed file
+	 */
+	virtual Common::Error loadGameData(strid_t file) = 0;
+
+	/**
+	 * Save the game to the passed file
+	 */
+	virtual Common::Error saveGameData(strid_t file) = 0;
 };
 
 extern GlkEngine *g_vm;
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index fed31f0..cbc5886 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -309,7 +309,7 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
 	if (loud)
 		debug("Reading %d actions.", na);
 
-	for (uint idx = 0; idx < na + 1; ++idx) {
+	for (int idx = 0; idx < na + 1; ++idx) {
 		Action &a = _actions[idx];
 		readInts(f, 8,
 			&a._vocab, &a._condition[0], &a._condition[1], &a._condition[2],
@@ -503,26 +503,8 @@ void Scott::lineInput(char *buf, size_t n) {
 	buf[ev.val1] = 0;
 }
 
-void Scott::saveGame(void) {
-	frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
-	               filemode_Write, 0);
-	if (ref == nullptr)
-		return;
-
-	int slot = ref->_slotNumber;
-	Common::String desc = ref->_description;
-	glk_fileref_destroy(ref);
-
-	saveGameState(slot, desc);
-}
-
-Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
+Common::Error Scott::saveGameData(strid_t file) {
 	Common::String msg;
-	FileReference ref(slot, desc, fileusage_TextMode | fileusage_SavedGame);
-
-	strid_t file = glk_stream_open_file(&ref, filemode_Write, 0);
-	if (file == nullptr)
-		return Common::kWritingFailed;
 
 	for (int ct = 0; ct < 16; ct++) {
 		msg = Common::String::format("%d %d\n", _counters[ct], _roomSaved[ct]);
@@ -539,37 +521,16 @@ Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
 		glk_put_string_stream(file, msg.c_str());
 	}
 
-	glk_stream_close(file, nullptr);
 	output("Saved.\n");
-
 	return Common::kNoError;
 }
 
-void Scott::loadGame(void) {
-	frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
-	               filemode_Read, 0);
-	if (ref == nullptr)
-		return;
-
-	int slotNumber = ref->_slotNumber;
-	glk_fileref_destroy(ref);
-
-	loadGameState(slotNumber);
-}
-
-Common::Error Scott::loadGameState(int slot) {
-	strid_t file;
+Common::Error Scott::loadGameData(strid_t file) {
 	char buf[128];
 	int ct = 0;
 	short lo;
 	short darkFlag;
 
-	FileReference ref(slot, "", fileusage_SavedGame | fileusage_TextMode);
-
-	file = glk_stream_open_file(&ref, filemode_Read, 0);
-	if (file == nullptr)
-		return Common::kReadingFailed;
-
 	for (ct = 0; ct < 16; ct++) {
 		glk_get_line_stream(file, buf, sizeof buf);
 		sscanf(buf, "%d %d", &_counters[ct], &_roomSaved[ct]);
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 4741892..a22f88b 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -153,8 +153,6 @@ private:
 	void look(void);
 	int whichWord(const char *word, const Common::StringArray &list);
 	void lineInput(char *buf, size_t n);
-	void saveGame(void);
-	void loadGame(void);
 	int getInput(int *vb, int *no);
 	int performLine(int ct);
 	int performActions(int vb, int no);
@@ -174,14 +172,14 @@ public:
 	virtual void runGame(Common::SeekableReadStream *gameFile) override;
 
 	/**
-	 * Load a savegame
+	 * Load a savegame from the passed stream
 	 */
-	virtual Common::Error loadGameState(int slot) override;
+	virtual Common::Error loadGameData(strid_t file) override;
 
 	/**
-	 * Save the game
+	 * Save the game to the passed stream
 	 */
-	virtual Common::Error saveGameState(int slot, const Common::String &desc) override;
+	virtual Common::Error saveGameData(strid_t file) override;
 };
 
 } // End of namespace Scott
diff --git a/engines/glk/streams.h b/engines/glk/streams.h
index 22a0a59..3150ce9 100644
--- a/engines/glk/streams.h
+++ b/engines/glk/streams.h
@@ -563,7 +563,7 @@ public:
 	/**
 	 * Open a file stream
 	 */
-	FileStream *openFileStream(frefid_t fref, glui32 fmode, glui32 rock, bool unicode);
+	FileStream *openFileStream(frefid_t fref, glui32 fmode, glui32 rock = 0, bool unicode = false);
 
 	/**
 	 * Open a window stream


Commit: 3b906cb2bb302d9256d07cb462f445af16c7d129
    https://github.com/scummvm/scummvm/commit/3b906cb2bb302d9256d07cb462f445af16c7d129
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fixes for combined loading/saving code

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


diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index e6a129f..a91c603 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -120,13 +120,22 @@ Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
 			return Common::kNoGameDataFoundError;
 	}
 
+	// Get the MD5
+	Common::File f;
+	if (!f.open(Common::FSNode(ConfMan.get("path")).getChild(gameDesc._filename)))
+		return Common::kNoGameDataFoundError;
+
+	gameDesc._md5 = Common::computeStreamMD5AsString(f, 5000);
+	f.close();
+
 	// Correct the correct engine
-	if (Glk::Frotz::FrotzMetaEngine::findGame(gameDesc._gameId.c_str()).description)
+	if (Glk::Frotz::FrotzMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
 		*engine = new Glk::Frotz::Frotz(syst, gameDesc);
-	else if (Glk::Scott::ScottMetaEngine::findGame(gameDesc._gameId.c_str()).description)
+	} else if (Glk::Scott::ScottMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
 		*engine = new Glk::Scott::Scott(syst, gameDesc);
-	else
+	} else {
 		return Common::kNoGameDataFoundError;
+	}
 
 	return Common::kNoError;
 }
diff --git a/engines/glk/frotz/frotz.h b/engines/glk/frotz/frotz.h
index 00aeb59..0dd8b69 100644
--- a/engines/glk/frotz/frotz.h
+++ b/engines/glk/frotz/frotz.h
@@ -49,6 +49,11 @@ public:
 	void initialize();
 
 	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_FROTZ; }
+
+	/**
 	 * Execute the game
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) override;
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index a3cd167..9488c66 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -149,7 +149,7 @@ Common::Error GlkEngine::loadGameState(int slot) {
 	if (file == nullptr)
 		return Common::kReadingFailed;
 
-	Common::Error result = saveGameData(file);
+	Common::Error result = loadGameData(file);
 
 	file->close();
 	return result;
@@ -163,7 +163,7 @@ Common::Error GlkEngine::saveGameState(int slot, const Common::String &desc) {
 	if (file == nullptr)
 		return Common::kWritingFailed;
 
-	Common::Error result = loadGameData(file);
+	Common::Error result = saveGameData(file);
 
 	file->close();
 	return result;
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index f925fd0..63b5727 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -76,7 +76,6 @@ struct GlkGameDescription {
 	Common::Language _language;
 	Common::Platform _platform;
 	Common::String _filename;
-	InterpreterType _interpType;
 	Common::String _md5;
 };
 
@@ -162,7 +161,7 @@ public:
 	/**
 	 * Returns the running interpreter type
 	 */
-	InterpreterType getInterpreterType() const { return _gameDescription._interpType; }
+	virtual InterpreterType getInterpreterType() const = 0;
 
 	/**
 	 * Returns the game's md5
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index a22f88b..ad1313d 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -167,6 +167,11 @@ public:
 	Scott(OSystem *syst, const GlkGameDescription &gameDesc);
 
 	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_SCOTT; }
+
+	/**
 	 * Execute the game
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) override;


Commit: fc86b3705bf2e46f03e27ed7b3934bc4b306a3a2
    https://github.com/scummvm/scummvm/commit/fc86b3705bf2e46f03e27ed7b3934bc4b306a3a2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix initializing fields of created streams

Changed paths:
    engines/glk/streams.cpp


diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
index cd7130f..b491b89 100644
--- a/engines/glk/streams.cpp
+++ b/engines/glk/streams.cpp
@@ -33,8 +33,8 @@
 namespace Glk {
 
 Stream::Stream(Streams *streams, bool readable, bool writable, uint32 rock, bool unicode) :
-	_streams(streams), _readable(readable), _writable(writable), _readCount(0),
-	_writeCount(0), _prev(nullptr), _next(nullptr), _rock(0) {
+	_streams(streams), _readable(readable), _writable(writable), _rock(0), _unicode(unicode),
+	_readCount(0), _writeCount(0), _prev(nullptr), _next(nullptr) {
 }
 
 Stream::~Stream() {


Commit: 0167d988fae3a54f526e004ac866db6636307f6f
    https://github.com/scummvm/scummvm/commit/0167d988fae3a54f526e004ac866db6636307f6f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Free windows on exit

Changed paths:
    engines/glk/window_pair.cpp
    engines/glk/window_pair.h
    engines/glk/windows.cpp


diff --git a/engines/glk/window_pair.cpp b/engines/glk/window_pair.cpp
index a0fdbb4..41b4e20 100644
--- a/engines/glk/window_pair.cpp
+++ b/engines/glk/window_pair.cpp
@@ -38,6 +38,11 @@ PairWindow::PairWindow(Windows *windows, glui32 method, Window *key, glui32 size
 	_type = wintype_Pair;
 }
 
+PairWindow::~PairWindow() {
+	delete _child1;
+	delete _child2;
+}
+
 void PairWindow::rearrange(const Rect &box) {
 	Rect box1, box2;
 	int min, diff, split, splitwid, max;
diff --git a/engines/glk/window_pair.h b/engines/glk/window_pair.h
index f77ae92..dabae77 100644
--- a/engines/glk/window_pair.h
+++ b/engines/glk/window_pair.h
@@ -49,6 +49,11 @@ public:
 	PairWindow(Windows *windows, glui32 method, Window *key, glui32 size);
 
 	/**
+	 * Destructor
+	 */
+	~PairWindow();
+
+	/**
 	 * Rearranges the window
 	 */
 	virtual void rearrange(const Rect &box) override;
diff --git a/engines/glk/windows.cpp b/engines/glk/windows.cpp
index ce1929f..4db231c 100644
--- a/engines/glk/windows.cpp
+++ b/engines/glk/windows.cpp
@@ -509,12 +509,20 @@ Window::~Window() {
 	if (g_vm->gli_unregister_obj)
 		(*g_vm->gli_unregister_obj)(this, gidisp_Class_Window, _dispRock);
 
-
+	// Remove the window from any parent
+	PairWindow *parent = dynamic_cast<PairWindow *>(_parent);
+	if (parent && parent->_child1 == this)
+		parent->_child1 = nullptr;
+	if (parent && parent->_child2 == this)
+		parent->_child2 = nullptr;
+
+	// Delete any attached window stream
 	_echoStream = nullptr;
 	delete _stream;
 
 	delete[] _lineTerminatorsBase;
 
+	// Remove the window from the master list of windows
 	Window *prev = _prev;
 	Window *next = _next;
 


Commit: 88b0e5d52fa26f64b089f1b8d406ada46434e6d3
    https://github.com/scummvm/scummvm/commit/88b0e5d52fa26f64b089f1b8d406ada46434e6d3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Change text buffer history array to use Array of U32Strings

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


diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index 21de0d9..2a98581 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -37,13 +37,13 @@ namespace Glk {
 
 
 TextBufferWindow::TextBufferWindow(Windows *windows, uint32 rock) : Window(windows, rock),
-	_historyPos(0), _historyFirst(0), _historyPresent(0), _lastSeen(0), _scrollPos(0),
-	_scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1), _inBuf(nullptr),
-	_lineTerminators(nullptr), _echoLineInput(true), _ladjw(0), _radjw(0), _ladjn(0),
-	_radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr),
-	_spaced(0), _dashed(0), _copyBuf(0), _copyPos(0) {
+		_historyPos(0), _historyFirst(0), _historyPresent(0), _lastSeen(0), _scrollPos(0),
+		_scrollMax(0), _scrollBack(SCROLLBACK), _width(-1), _height(-1), _inBuf(nullptr),
+		_lineTerminators(nullptr), _echoLineInput(true), _ladjw(0), _radjw(0), _ladjn(0),
+		_radjn(0), _numChars(0), _chars(nullptr), _attrs(nullptr),
+		_spaced(0), _dashed(0), _copyBuf(0), _copyPos(0) {
 	_type = wintype_TextBuffer;
-	Common::fill(&_history[0], &_history[HISTORYLEN], (glui32 *)nullptr);
+	_history.resize(HISTORYLEN);
 
 	_lines.resize(SCROLLBACK);
 	_chars = _lines[0]._chars;
@@ -1239,6 +1239,7 @@ void TextBufferWindow::acceptReadChar(glui32 arg) {
 
 void TextBufferWindow::acceptReadLine(glui32 arg) {
 	glui32 *cx;
+	Common::U32String s;
 	int len;
 
 	if (_height < 2)
@@ -1268,23 +1269,17 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 			return;
 		if (_historyPos == _historyPresent) {
 			len = _numChars - _inFence;
-			if (len > 0) {
-				cx = new glui32[len + 1];
-				memcpy(cx, &(_chars[_inFence]), len * 4);
-				cx[len] = 0;
-			} else {
-				cx = nullptr;
-			}
-			if (_history[_historyPos])
-				free(_history[_historyPos]);
-			_history[_historyPos] = cx;
+
+			if (len > 0)
+				s = Common::U32String(&(_chars[_inFence]), len);
+			_history[_historyPos] = s;
 		}
+
 		_historyPos--;
 		if (_historyPos < 0)
 			_historyPos += HISTORYLEN;
-		cx = _history[_historyPos];
-		putTextUni(cx, cx ? strlen_uni(cx) : 0, _inFence,
-		           _numChars - _inFence);
+		s = _history[_historyPos];
+		putTextUni(s.c_str(), s.size(), _inFence, _numChars - _inFence);
 		break;
 
 	case keycode_Down:
@@ -1293,9 +1288,8 @@ void TextBufferWindow::acceptReadLine(glui32 arg) {
 		_historyPos++;
 		if (_historyPos >= HISTORYLEN)
 			_historyPos -= HISTORYLEN;
-		cx = _history[_historyPos];
-		putTextUni(cx, cx ? strlen_uni(cx) : 0, _inFence,
-		           _numChars - _inFence);
+		s = _history[_historyPos];
+		putTextUni(s.c_str(), s.size(), _inFence, _numChars - _inFence);
 		break;
 
 	// Cursor movement keys, during line input.
@@ -1377,7 +1371,7 @@ void TextBufferWindow::acceptLine(glui32 keycode) {
 	int ix;
 	int len, olen;
 	void *inbuf;
-	glui32 *s, *o;
+	Common::U32String s, o;
 	int inmax;
 	gidispatch_rock_t inarrayrock;
 	int unicode = _lineRequestUni;
@@ -1407,17 +1401,13 @@ void TextBufferWindow::acceptLine(glui32 keycode) {
 	* A history entry should not repeat the string from the entry before it.
 	*/
 	if (len) {
-		s = new glui32[len + 1];
-		memcpy(s, _chars + _inFence, len * sizeof(glui32));
-		s[len] = 0;
-
-		free(_history[_historyPresent]);
-		_history[_historyPresent] = nullptr;
+		s = Common::U32String(_chars + _inFence, len);
+		_history[_historyPresent].clear();
 
 		o = _history[(_historyPresent == 0 ? HISTORYLEN : _historyPresent) - 1];
-		olen = o ? strlen_uni(o) : 0;
+		olen = o.size();
 
-		if (len != olen || memcmp(s, o, olen * sizeof(glui32))) {
+		if (len != olen || !s.equals(o)) {
 			_history[_historyPresent] = s;
 
 			_historyPresent++;
@@ -1429,8 +1419,6 @@ void TextBufferWindow::acceptLine(glui32 keycode) {
 				if (_historyFirst == HISTORYLEN)
 					_historyFirst = 0;
 			}
-		} else {
-			free(s);
 		}
 	}
 
diff --git a/engines/glk/window_text_buffer.h b/engines/glk/window_text_buffer.h
index 4384986..728919f 100644
--- a/engines/glk/window_text_buffer.h
+++ b/engines/glk/window_text_buffer.h
@@ -26,6 +26,8 @@
 #include "glk/windows.h"
 #include "glk/picture.h"
 #include "glk/speech.h"
+#include "common/array.h"
+#include "common/ustr.h"
 
 namespace Glk {
 
@@ -106,7 +108,7 @@ public:
 	int _radjn;
 
 	/* Command history. */
-	glui32 *_history[HISTORYLEN];
+	Common::Array<Common::U32String> _history;
 	int _historyPos;
 	int _historyFirst, _historyPresent;
 


Commit: 41a437816999c913f30f3ef2c1c751f646399c91
    https://github.com/scummvm/scummvm/commit/41a437816999c913f30f3ef2c1c751f646399c91
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: SCOTT: Don't show another line prompt when game is exiting

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


diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index 6094015..e04040f 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -62,7 +62,10 @@ void GlkAPI::glk_exit(void) {
 	glk_put_string("[ press any key to exit ]");
 	_events->waitForPress();
 
+	// Trigger a ScumMVM shutdown of game
 	quitGame();
+	Common::Event e;
+	g_system->getEventManager()->pollEvent(e);
 }
 
 void GlkAPI::glk_set_interrupt_handler(void(*func)(void)) {
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index cbc5886..dc0ae0b 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -102,6 +102,8 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 		default:
 			break;
 		}
+		if (shouldQuit())
+			return;
 
 		// Brian Howarth games seem to use -1 for forever
 		if (_items[LIGHT_SOURCE]._location != DESTROYED && _gameHeader._lightTime != -1) {
@@ -789,7 +791,7 @@ int Scott::performLine(int ct) {
 doneit:
 				output("The game is now over.\n");
 				glk_exit();
-				break;
+				return 0;
 			case 64:
 				break;
 			case 65: {
@@ -1022,6 +1024,9 @@ int Scott::performActions(int vb, int no) {
 					if (vb != 0 && doagain == 0)
 						return 0;
 				}
+
+				if (shouldQuit())
+					return 0;
 			}
 		}
 		ct++;


Commit: 79f3d946ef23ff121f30d9e429d2c4ab985ce29c
    https://github.com/scummvm/scummvm/commit/79f3d946ef23ff121f30d9e429d2c4ab985ce29c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Shift Header class into it's own file

Changed paths:
  A engines/glk/frotz/header.cpp
  A engines/glk/frotz/header.h
    engines/glk/frotz/mem.cpp
    engines/glk/frotz/mem.h
    engines/glk/module.mk


diff --git a/engines/glk/frotz/header.cpp b/engines/glk/frotz/header.cpp
new file mode 100644
index 0000000..f7ebb5e
--- /dev/null
+++ b/engines/glk/frotz/header.cpp
@@ -0,0 +1,112 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/frotz/header.h"
+#include "common/textconsole.h"
+
+namespace Glk {
+namespace Frotz {
+
+const Header::StoryEntry Header::RECORDS[25] = {
+	{       SHERLOCK,  21, "871214" },
+	{       SHERLOCK,  26, "880127" },
+	{    BEYOND_ZORK,  47, "870915" },
+	{    BEYOND_ZORK,  49, "870917" },
+	{    BEYOND_ZORK,  51, "870923" },
+	{    BEYOND_ZORK,  57, "871221" },
+	{      ZORK_ZERO, 296, "881019" },
+	{      ZORK_ZERO, 366, "890323" },
+	{      ZORK_ZERO, 383, "890602" },
+	{      ZORK_ZERO, 393, "890714" },
+	{         SHOGUN, 292, "890314" },
+	{         SHOGUN, 295, "890321" },
+	{         SHOGUN, 311, "890510" },
+	{         SHOGUN, 322, "890706" },
+	{         ARTHUR,  54, "890606" },
+	{         ARTHUR,  63, "890622" },
+	{         ARTHUR,  74, "890714" },
+	{        JOURNEY,  26, "890316" },
+	{        JOURNEY,  30, "890322" },
+	{        JOURNEY,  77, "890616" },
+	{        JOURNEY,  83, "890706" },
+	{ LURKING_HORROR, 203, "870506" },
+	{ LURKING_HORROR, 219, "870912" },
+	{ LURKING_HORROR, 221, "870918" },
+	{        UNKNOWN,   0, "------" }
+};
+
+void Header::loadHeader(Common::SeekableReadStream &f) {
+	h_version = f.readByte();
+	h_config = f.readByte();
+
+	if (h_version < V1 || h_version > V8)
+		error("Unknown Z-code version");
+
+	if (h_version == V6)
+		error("Cannot play Z-code version 6");
+
+	if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
+		error("Byte swapped story file");
+
+	h_release = f.readUint16BE();
+	h_resident_size = f.readUint16BE();
+	h_start_pc = f.readUint16BE();
+	h_dictionary = f.readUint16BE();
+	h_objects = f.readUint16BE();
+	h_globals = f.readUint16BE();
+	h_dynamic_size = f.readUint16BE();
+	h_flags = f.readUint16BE();
+	f.read(h_serial, 6);
+	
+	/* Auto-detect buggy story files that need special fixes */
+	_storyId = UNKNOWN;
+
+	for (int i = 0; RECORDS[i]._storyId != UNKNOWN; ++i) {
+		if (h_release == RECORDS[i]._release) {
+			if (!strncmp((const char *)h_serial, RECORDS[i]._serial, 6)) {
+				_storyId = RECORDS[i]._storyId;
+				break;
+			}
+		}
+	}
+
+	h_abbreviations = f.readUint16BE();
+	h_file_size = f.readUint16BE();
+	h_checksum = f.readUint16BE();
+	
+	f.seek(H_FUNCTIONS_OFFSET);
+	h_functions_offset = f.readUint16BE();
+	h_strings_offset = f.readUint16BE();
+	f.seek(H_TERMINATING_KEYS);
+	h_terminating_keys = f.readUint16BE();
+	f.seek(H_ALPHABET);
+	h_alphabet = f.readUint16BE();
+	h_extension_table = f.readUint16BE();
+
+
+	// Zork Zero Macintosh doesn't have the graphics flag set
+	if (_storyId == ZORK_ZERO && h_release == 296)
+		h_flags |= GRAPHICS_FLAG;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/frotz/header.h b/engines/glk/frotz/header.h
new file mode 100644
index 0000000..cf7ab64
--- /dev/null
+++ b/engines/glk/frotz/header.h
@@ -0,0 +1,159 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_FROTZ_HEADER
+#define GLK_FROTZ_HEADER
+
+#include "glk/frotz/frotz_types.h"
+
+namespace Glk {
+namespace Frotz {
+
+enum HeaderByte {
+	H_VERSION             = 0,
+	H_CONFIG              = 1,
+	H_RELEASE             = 2,
+	H_RESIDENT_SIZE       = 4,
+	H_START_PC            = 6,
+	H_DICTIONARY          = 8,
+	H_OBJECTS             = 10,
+	H_GLOBALS             = 12,
+	H_DYNAMIC_SIZE        = 14,
+	H_FLAGS               = 16,
+	H_SERIAL              = 18,
+	H_ABBREVIATIONS       = 24,
+	H_FILE_SIZE           = 26,
+	H_CHECKSUM            = 28,
+	H_INTERPRETER_NUMBER  = 30,
+	H_INTERPRETER_VERSION = 31,
+	H_SCREEN_ROWS         = 32,
+	H_SCREEN_COLS         = 33,
+	H_SCREEN_WIDTH        = 34,
+	H_SCREEN_HEIGHT       = 36,
+	H_FONT_HEIGHT         = 38,		///< this is the font width in V5
+	H_FONT_WIDTH          = 39,		///< this is the font height in V5
+	H_FUNCTIONS_OFFSET    = 40,
+	H_STRINGS_OFFSET      = 42,
+	H_DEFAULT_BACKGROUND  = 44,
+	H_DEFAULT_FOREGROUND  = 45,
+	H_TERMINATING_KEYS    = 46,
+	H_LINE_WIDTH          = 48,
+	H_STANDARD_HIGH       = 50,
+	H_STANDARD_LOW        = 51,
+	H_ALPHABET            = 52,
+	H_EXTENSION_TABLE     = 54,
+	H_USER_NAME           = 56
+};
+
+enum {
+	HX_TABLE_SIZE    = 0,
+	HX_MOUSE_X       = 1,
+	HX_MOUSE_Y       = 2,
+	HX_UNICODE_TABLE = 3,
+	HX_FLAGS         = 4,
+	HX_FORE_COLOUR   = 5,
+	HX_BACK_COLOUR   = 6
+};
+
+/**
+ * Story file header data
+ */
+struct Header {
+private:
+	struct StoryEntry {
+		Story _storyId;
+		zword _release;
+		char _serial[7];
+	};
+	static const StoryEntry RECORDS[25];
+public:
+	zbyte h_version;
+	zbyte h_config;
+	zword h_release;
+	zword h_resident_size;
+	zword h_start_pc;
+	zword h_dictionary;
+	zword h_objects;
+	zword h_globals;
+	zword h_dynamic_size;
+	zword h_flags;
+	zbyte h_serial[6];
+	zword h_abbreviations;
+	zword h_file_size;
+	zword h_checksum;
+	zbyte h_interpreter_number;
+	zbyte h_interpreter_version;
+	zbyte h_screen_rows;
+	zbyte h_screen_cols;
+	zword h_screen_width;
+	zword h_screen_height;
+	zbyte h_font_height;
+	zbyte h_font_width;
+	zword h_functions_offset;
+	zword h_strings_offset;
+	zbyte h_default_background;
+	zbyte h_default_foreground;
+	zword h_terminating_keys;
+	zword h_line_width;
+	zbyte h_standard_high;
+	zbyte h_standard_low;
+	zword h_alphabet;
+	zword h_extension_table;
+	zbyte h_user_name[8];
+
+	zword hx_table_size;
+	zword hx_mouse_x;
+	zword hx_mouse_y;
+	zword hx_unicode_table;
+	zword hx_flags;
+	zword hx_fore_colour;
+	zword hx_back_colour;
+
+	Story _storyId;
+
+	/**
+	 * Constructor
+	 */
+	Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
+			h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
+			h_abbreviations(0), h_file_size(0), h_checksum(0), h_interpreter_number(0),
+			h_interpreter_version(0), h_screen_rows(0), h_screen_cols(0), h_screen_width(0),
+			h_screen_height(0), h_font_height(1), h_font_width(1), h_functions_offset(0),
+			h_strings_offset(0), h_default_background(0), h_default_foreground(0),
+			h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
+			h_alphabet(0), h_extension_table(0),
+			hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
+			hx_flags(0), hx_fore_colour(0), hx_back_colour(0), _storyId(UNKNOWN) {
+		Common::fill(&h_serial[0], &h_serial[6], '\0');
+		Common::fill(&h_user_name[0], &h_user_name[8], '\0');
+	}
+
+	/**
+	 * Load the header
+	 */
+	void loadHeader(Common::SeekableReadStream &f);
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index 6ee6781..da0acaa 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -28,90 +28,6 @@
 namespace Glk {
 namespace Frotz {
 
-const Header::StoryEntry Header::RECORDS[25] = {
-	{       SHERLOCK,  21, "871214" },
-	{       SHERLOCK,  26, "880127" },
-	{    BEYOND_ZORK,  47, "870915" },
-	{    BEYOND_ZORK,  49, "870917" },
-	{    BEYOND_ZORK,  51, "870923" },
-	{    BEYOND_ZORK,  57, "871221" },
-	{      ZORK_ZERO, 296, "881019" },
-	{      ZORK_ZERO, 366, "890323" },
-	{      ZORK_ZERO, 383, "890602" },
-	{      ZORK_ZERO, 393, "890714" },
-	{         SHOGUN, 292, "890314" },
-	{         SHOGUN, 295, "890321" },
-	{         SHOGUN, 311, "890510" },
-	{         SHOGUN, 322, "890706" },
-	{         ARTHUR,  54, "890606" },
-	{         ARTHUR,  63, "890622" },
-	{         ARTHUR,  74, "890714" },
-	{        JOURNEY,  26, "890316" },
-	{        JOURNEY,  30, "890322" },
-	{        JOURNEY,  77, "890616" },
-	{        JOURNEY,  83, "890706" },
-	{ LURKING_HORROR, 203, "870506" },
-	{ LURKING_HORROR, 219, "870912" },
-	{ LURKING_HORROR, 221, "870918" },
-	{        UNKNOWN,   0, "------" }
-};
-
-void Header::loadHeader(Common::SeekableReadStream &f) {
-	h_version = f.readByte();
-	h_config = f.readByte();
-
-	if (h_version < V1 || h_version > V8)
-		error("Unknown Z-code version");
-
-	if (h_version == V6)
-		error("Cannot play Z-code version 6");
-
-	if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
-		error("Byte swapped story file");
-
-	h_release = f.readUint16BE();
-	h_resident_size = f.readUint16BE();
-	h_start_pc = f.readUint16BE();
-	h_dictionary = f.readUint16BE();
-	h_objects = f.readUint16BE();
-	h_globals = f.readUint16BE();
-	h_dynamic_size = f.readUint16BE();
-	h_flags = f.readUint16BE();
-	f.read(h_serial, 6);
-	
-	/* Auto-detect buggy story files that need special fixes */
-	_storyId = UNKNOWN;
-
-	for (int i = 0; RECORDS[i]._storyId != UNKNOWN; ++i) {
-		if (h_release == RECORDS[i]._release) {
-			if (!strncmp((const char *)h_serial, RECORDS[i]._serial, 6)) {
-				_storyId = RECORDS[i]._storyId;
-				break;
-			}
-		}
-	}
-
-	h_abbreviations = f.readUint16BE();
-	h_file_size = f.readUint16BE();
-	h_checksum = f.readUint16BE();
-	
-	f.seek(H_FUNCTIONS_OFFSET);
-	h_functions_offset = f.readUint16BE();
-	h_strings_offset = f.readUint16BE();
-	f.seek(H_TERMINATING_KEYS);
-	h_terminating_keys = f.readUint16BE();
-	f.seek(H_ALPHABET);
-	h_alphabet = f.readUint16BE();
-	h_extension_table = f.readUint16BE();
-
-
-	// Zork Zero Macintosh doesn't have the graphics flag set
-	if (_storyId == ZORK_ZERO && h_release == 296)
-		h_flags |= GRAPHICS_FLAG;
-}
-
-/*--------------------------------------------------------------------------*/
-
 Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0), story_size(0),
 		first_undo(nullptr), last_undo(nullptr), curr_undo(nullptr),
 		undo_mem(nullptr), prev_zmp(nullptr), undo_diff(nullptr),
diff --git a/engines/glk/frotz/mem.h b/engines/glk/frotz/mem.h
index 372c7e7..3b1b81b 100644
--- a/engines/glk/frotz/mem.h
+++ b/engines/glk/frotz/mem.h
@@ -24,6 +24,7 @@
 #define GLK_FROTZ_MEM
 
 #include "glk/frotz/frotz_types.h"
+#include "glk/frotz/header.h"
 
 namespace Glk {
 namespace Frotz {
@@ -35,52 +36,6 @@ namespace Frotz {
 #define SET_BYTE(addr,v)   zmp[addr] = v
 #define LOW_BYTE(addr,v)   v = zmp[addr]
 
-enum HeaderByte {
-	H_VERSION             = 0,
-	H_CONFIG              = 1,
-	H_RELEASE             = 2,
-	H_RESIDENT_SIZE       = 4,
-	H_START_PC            = 6,
-	H_DICTIONARY          = 8,
-	H_OBJECTS             = 10,
-	H_GLOBALS             = 12,
-	H_DYNAMIC_SIZE        = 14,
-	H_FLAGS               = 16,
-	H_SERIAL              = 18,
-	H_ABBREVIATIONS       = 24,
-	H_FILE_SIZE           = 26,
-	H_CHECKSUM            = 28,
-	H_INTERPRETER_NUMBER  = 30,
-	H_INTERPRETER_VERSION = 31,
-	H_SCREEN_ROWS         = 32,
-	H_SCREEN_COLS         = 33,
-	H_SCREEN_WIDTH        = 34,
-	H_SCREEN_HEIGHT       = 36,
-	H_FONT_HEIGHT         = 38,		///< this is the font width in V5
-	H_FONT_WIDTH          = 39,		///< this is the font height in V5
-	H_FUNCTIONS_OFFSET    = 40,
-	H_STRINGS_OFFSET      = 42,
-	H_DEFAULT_BACKGROUND  = 44,
-	H_DEFAULT_FOREGROUND  = 45,
-	H_TERMINATING_KEYS    = 46,
-	H_LINE_WIDTH          = 48,
-	H_STANDARD_HIGH       = 50,
-	H_STANDARD_LOW        = 51,
-	H_ALPHABET            = 52,
-	H_EXTENSION_TABLE     = 54,
-	H_USER_NAME           = 56
-};
-
-enum {
-	HX_TABLE_SIZE    = 0,
-	HX_MOUSE_X       = 1,
-	HX_MOUSE_Y       = 2,
-	HX_UNICODE_TABLE = 3,
-	HX_FLAGS         = 4,
-	HX_FORE_COLOUR   = 5,
-	HX_BACK_COLOUR   = 6
-};
-
 /**
  * Stores undo information
  */
@@ -97,84 +52,8 @@ struct undo_struct {
 typedef undo_struct undo_t;
 
 /**
- * Story file header data
+ * Handles the memory, header, and user options
  */
-struct Header {
-private:
-	struct StoryEntry {
-		Story _storyId;
-		zword _release;
-		char _serial[7];
-	};
-	static const StoryEntry RECORDS[25];
-public:
-	zbyte h_version;
-	zbyte h_config;
-	zword h_release;
-	zword h_resident_size;
-	zword h_start_pc;
-	zword h_dictionary;
-	zword h_objects;
-	zword h_globals;
-	zword h_dynamic_size;
-	zword h_flags;
-	zbyte h_serial[6];
-	zword h_abbreviations;
-	zword h_file_size;
-	zword h_checksum;
-	zbyte h_interpreter_number;
-	zbyte h_interpreter_version;
-	zbyte h_screen_rows;
-	zbyte h_screen_cols;
-	zword h_screen_width;
-	zword h_screen_height;
-	zbyte h_font_height;
-	zbyte h_font_width;
-	zword h_functions_offset;
-	zword h_strings_offset;
-	zbyte h_default_background;
-	zbyte h_default_foreground;
-	zword h_terminating_keys;
-	zword h_line_width;
-	zbyte h_standard_high;
-	zbyte h_standard_low;
-	zword h_alphabet;
-	zword h_extension_table;
-	zbyte h_user_name[8];
-
-	zword hx_table_size;
-	zword hx_mouse_x;
-	zword hx_mouse_y;
-	zword hx_unicode_table;
-	zword hx_flags;
-	zword hx_fore_colour;
-	zword hx_back_colour;
-
-	Story _storyId;
-
-	/**
-	 * Constructor
-	 */
-	Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
-			h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
-			h_abbreviations(0), h_file_size(0), h_checksum(0), h_interpreter_number(0),
-			h_interpreter_version(0), h_screen_rows(0), h_screen_cols(0), h_screen_width(0),
-			h_screen_height(0), h_font_height(1), h_font_width(1), h_functions_offset(0),
-			h_strings_offset(0), h_default_background(0), h_default_foreground(0),
-			h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
-			h_alphabet(0), h_extension_table(0),
-			hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
-			hx_flags(0), hx_fore_colour(0), hx_back_colour(0), _storyId(UNKNOWN) {
-		Common::fill(&h_serial[0], &h_serial[6], '\0');
-		Common::fill(&h_user_name[0], &h_user_name[8], '\0');
-	}
-
-	/**
-	 * Load the header
-	 */
-	void loadHeader(Common::SeekableReadStream &f);
-};
-
 class Mem : public Header, public virtual UserOptions {
 protected:
 	Common::SeekableReadStream *story_fp;
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index b523034..10cef32 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -24,6 +24,7 @@ MODULE_OBJS := \
 	frotz/detection.o \
 	frotz/frotz.o \
 	frotz/glk_interface.o \
+	frotz/header.o \
 	frotz/mem.o \
 	frotz/processor.o \
 	frotz/processor_buffer.o \


Commit: ada80dedc0d39d76a2cd3c3b4af6c3219738d0bf
    https://github.com/scummvm/scummvm/commit/ada80dedc0d39d76a2cd3c3b4af6c3219738d0bf
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Further merging together of configuration into a new config.cpp file

Changed paths:
  A engines/glk/frotz/config.cpp
  A engines/glk/frotz/config.h
  R engines/glk/frotz/header.cpp
  R engines/glk/frotz/header.h
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/frotz_types.h
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/mem.h
    engines/glk/frotz/processor.cpp
    engines/glk/module.mk


diff --git a/engines/glk/frotz/config.cpp b/engines/glk/frotz/config.cpp
new file mode 100644
index 0000000..0461e25
--- /dev/null
+++ b/engines/glk/frotz/config.cpp
@@ -0,0 +1,196 @@
+/* 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/frotz/config.h"
+#include "common/config-manager.h"
+#include "common/textconsole.h"
+
+namespace Glk {
+namespace Frotz {
+
+const Header::StoryEntry Header::RECORDS[25] = {
+	{       SHERLOCK,  21, "871214" },
+	{       SHERLOCK,  26, "880127" },
+	{    BEYOND_ZORK,  47, "870915" },
+	{    BEYOND_ZORK,  49, "870917" },
+	{    BEYOND_ZORK,  51, "870923" },
+	{    BEYOND_ZORK,  57, "871221" },
+	{      ZORK_ZERO, 296, "881019" },
+	{      ZORK_ZERO, 366, "890323" },
+	{      ZORK_ZERO, 383, "890602" },
+	{      ZORK_ZERO, 393, "890714" },
+	{         SHOGUN, 292, "890314" },
+	{         SHOGUN, 295, "890321" },
+	{         SHOGUN, 311, "890510" },
+	{         SHOGUN, 322, "890706" },
+	{         ARTHUR,  54, "890606" },
+	{         ARTHUR,  63, "890622" },
+	{         ARTHUR,  74, "890714" },
+	{        JOURNEY,  26, "890316" },
+	{        JOURNEY,  30, "890322" },
+	{        JOURNEY,  77, "890616" },
+	{        JOURNEY,  83, "890706" },
+	{ LURKING_HORROR, 203, "870506" },
+	{ LURKING_HORROR, 219, "870912" },
+	{ LURKING_HORROR, 221, "870918" },
+	{        UNKNOWN,   0, "------" }
+};
+
+static uint getConfigBool(const Common::String &profileName, bool defaultVal = false) {
+	return ConfMan.hasKey(profileName) ? ConfMan.getBool(profileName) : defaultVal;
+}
+
+static uint getConfigInt(const Common::String &profileName, uint defaultVal, uint maxVal) {
+	uint val = ConfMan.hasKey(profileName) ? ConfMan.getInt(profileName) : defaultVal;
+	if (val > maxVal)
+		error("Invalid value for configuration value %s", profileName.c_str());
+
+	return val;
+}
+
+Header::Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
+		h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
+		h_abbreviations(0), h_file_size(0), h_checksum(0),
+		h_interpreter_version(0), h_screen_rows(0), h_screen_cols(0), h_screen_width(0),
+		h_screen_height(0), h_font_height(1), h_font_width(1), h_functions_offset(0),
+		h_strings_offset(0), h_default_background(0), h_default_foreground(0),
+		h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
+		h_alphabet(0), h_extension_table(0),
+		hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
+		hx_flags(0), hx_fore_colour(0), hx_back_colour(0), _storyId(UNKNOWN) {
+	Common::fill(&h_serial[0], &h_serial[6], '\0');
+	Common::fill(&h_user_name[0], &h_user_name[8], '\0');
+
+	h_interpreter_number = getConfigInt("interpreter_number", INTERP_AMIGA, INTERP_TANDY);
+	
+
+	if (ConfMan.hasKey("username")) {
+		Common::String username = ConfMan.get("username");
+		strncpy((char *)h_user_name, username.c_str(), 7);
+	}
+}
+
+void Header::loadHeader(Common::SeekableReadStream &f) {
+	h_version = f.readByte();
+	h_config = f.readByte();
+
+	if (h_version < V1 || h_version > V8)
+		error("Unknown Z-code version");
+
+	if (h_version == V6)
+		error("Cannot play Z-code version 6");
+
+	if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
+		error("Byte swapped story file");
+
+	h_release = f.readUint16BE();
+	h_resident_size = f.readUint16BE();
+	h_start_pc = f.readUint16BE();
+	h_dictionary = f.readUint16BE();
+	h_objects = f.readUint16BE();
+	h_globals = f.readUint16BE();
+	h_dynamic_size = f.readUint16BE();
+	h_flags = f.readUint16BE();
+	f.read(h_serial, 6);
+	
+	/* Auto-detect buggy story files that need special fixes */
+	_storyId = UNKNOWN;
+
+	for (int i = 0; RECORDS[i]._storyId != UNKNOWN; ++i) {
+		if (h_release == RECORDS[i]._release) {
+			if (!strncmp((const char *)h_serial, RECORDS[i]._serial, 6)) {
+				_storyId = RECORDS[i]._storyId;
+				break;
+			}
+		}
+	}
+
+	h_abbreviations = f.readUint16BE();
+	h_file_size = f.readUint16BE();
+	h_checksum = f.readUint16BE();
+	
+	f.seek(H_FUNCTIONS_OFFSET);
+	h_functions_offset = f.readUint16BE();
+	h_strings_offset = f.readUint16BE();
+	f.seek(H_TERMINATING_KEYS);
+	h_terminating_keys = f.readUint16BE();
+	f.seek(H_ALPHABET);
+	h_alphabet = f.readUint16BE();
+	h_extension_table = f.readUint16BE();
+
+
+	// Zork Zero Macintosh doesn't have the graphics flag set
+	if (_storyId == ZORK_ZERO && h_release == 296)
+		h_flags |= GRAPHICS_FLAG;
+}
+
+/*--------------------------------------------------------------------------*/
+
+UserOptions::UserOptions() : _undo_slots(MAX_UNDO_SLOTS), _sound(true) {
+	_err_report_mode = getConfigInt("err_report_mode", ERR_REPORT_ONCE, ERR_REPORT_FATAL);
+	_ignore_errors = getConfigBool("ignore_errors");
+	_expand_abbreviations = getConfigBool("expand_abbreviations");
+	_quetzal = getConfigBool("quetzal", true);
+	_tandyBit = getConfigBool("tandy_bit");
+	_piracy = getConfigBool("piracy");
+	_script_cols = getConfigInt("wrap_script_lines", 80, 999);
+	_left_margin = getConfigInt("left_margin", 0, 999);
+	_right_margin = getConfigInt("right_margin", 0, 999);
+
+	// Debugging flags
+	_attribute_assignment = getConfigBool("attribute_assignment");
+	_attribute_testing = getConfigBool("attribute_testing");
+	_object_locating = getConfigBool("object_locating");
+	_object_movement = getConfigBool("object_movement");
+}
+
+#ifdef REMOVE_ME
+if (ConfMan.hasKey("attribute_assignment") && ConfMan.getBool("attribute_assignment"))
+_attribute_assignment = true;
+if (ConfMan.hasKey("attribute_testing") && ConfMan.getBool("attribute_testing"))
+_attribute_testing = true;
+if (ConfMan.hasKey("object_movement") && ConfMan.getBool("object_movement"))
+_object_movement = true;
+if (ConfMan.hasKey("object_locating") && ConfMan.getBool("object_locating"))
+_object_locating = true;
+if (ConfMan.hasKey("piracy") && ConfMan.getBool("piracy"))
+_piracy = true;
+if (ConfMan.hasKey("random_seed"))
+_random.setSeed(ConfMan.getInt("random_seed"));
+if (ConfMan.hasKey("script_cols"))
+_script_cols = ConfMan.getInt("script_cols");
+if (ConfMan.hasKey("tandy_bit") && ConfMan.getBool("tandy_bit"))
+_user_tandy_bit = true;
+if (ConfMan.hasKey("undo_slots"))
+_undo_slots = ConfMan.getInt("undo_slots");
+if (ConfMan.hasKey("expand_abbreviations") && ConfMan.getBool("expand_abbreviations"))
+_expand_abbreviations = true;
+if (ConfMan.hasKey("err_report_mode")) {
+	_err_report_mode = ConfMan.getInt("err_report_mode");
+	if ((_err_report_mode < ERR_REPORT_NEVER) || (_err_report_mode > ERR_REPORT_FATAL))
+		_err_report_mode = ERR_DEFAULT_REPORT_MODE;
+}
+
+#endif
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/frotz/config.h b/engines/glk/frotz/config.h
new file mode 100644
index 0000000..a210ba2
--- /dev/null
+++ b/engines/glk/frotz/config.h
@@ -0,0 +1,228 @@
+/* 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_FROTZ_CONFIG
+#define GLK_FROTZ_CONFIG
+
+#include "glk/frotz/frotz_types.h"
+
+namespace Glk {
+namespace Frotz {
+
+/**
+ * Configuration flags
+ */
+enum ConfigFlag {
+	CONFIG_BYTE_SWAPPED = 0x01, ///< Story file is byte swapped         - V3 
+	CONFIG_TIME         = 0x02, ///< Status line displays time          - V3 
+	CONFIG_TWODISKS     = 0x04, ///< Story file occupied two disks      - V3 
+	CONFIG_TANDY        = 0x08, ///< Tandy licensed game                - V3 
+	CONFIG_NOSTATUSLINE = 0x10, ///< Interpr can't support status lines - V3 
+	CONFIG_SPLITSCREEN  = 0x20, ///< Interpr supports split screen mode - V3 
+	CONFIG_PROPORTIONAL = 0x40, ///< Interpr uses proportional font     - V3 
+	
+	CONFIG_COLOUR       = 0x01, ///< Interpr supports colour            - V5+
+	CONFIG_PICTURES	    = 0x02, ///< Interpr supports pictures	        - V6 
+	CONFIG_BOLDFACE     = 0x04, ///< Interpr supports boldface style    - V4+
+	CONFIG_EMPHASIS     = 0x08, ///< Interpr supports emphasis style    - V4+
+	CONFIG_FIXED        = 0x10, ///< Interpr supports fixed width style - V4+
+	CONFIG_SOUND	    = 0x20, ///< Interpr supports sound             - V6 
+	CONFIG_TIMEDINPUT   = 0x80, ///< Interpr supports timed input       - V4+
+	
+	SCRIPTING_FLAG	  = 0x0001, ///< Outputting to transscription file  - V1+
+	FIXED_FONT_FLAG   = 0x0002, ///< Use fixed width font               - V3+
+	REFRESH_FLAG 	  = 0x0004, ///< Refresh the screen                 - V6 
+	GRAPHICS_FLAG	  = 0x0008, ///< Game wants to use graphics         - V5+
+	OLD_SOUND_FLAG	  = 0x0010, ///< Game wants to use sound effects    - V3 
+	UNDO_FLAG	  = 0x0010, ///< Game wants to use UNDO feature     - V5+
+	MOUSE_FLAG	  = 0x0020, ///< Game wants to use a mouse          - V5+
+	COLOUR_FLAG	  = 0x0040, ///< Game wants to use colours          - V5+
+	SOUND_FLAG	  = 0x0080, ///< Game wants to use sound effects    - V5+
+	MENU_FLAG	  = 0x0100  ///< Game wants to use menus            - V6 
+};
+
+/**
+ * There are four error reporting modes: never report errors;
+ * report only the first time a given error type occurs;
+ * report every time an error occurs;
+ * or treat all errors as fatal errors, killing the interpreter.
+ * I strongly recommend "report once" as the default. But you can compile in a
+ * different default by changing the definition of ERR_DEFAULT_REPORT_MODE.
+ */
+enum ErrorReport {
+	ERR_REPORT_NEVER  = 0,
+	ERR_REPORT_ONCE   = 1,
+	ERR_REPORT_ALWAYS = 2,
+	ERR_REPORT_FATAL  = 3,
+
+	ERR_DEFAULT_REPORT_MODE = ERR_REPORT_NEVER
+};
+
+/**
+ * Enumeration of the game header byte indexes
+ */
+enum HeaderByte {
+	H_VERSION             = 0,
+	H_CONFIG              = 1,
+	H_RELEASE             = 2,
+	H_RESIDENT_SIZE       = 4,
+	H_START_PC            = 6,
+	H_DICTIONARY          = 8,
+	H_OBJECTS             = 10,
+	H_GLOBALS             = 12,
+	H_DYNAMIC_SIZE        = 14,
+	H_FLAGS               = 16,
+	H_SERIAL              = 18,
+	H_ABBREVIATIONS       = 24,
+	H_FILE_SIZE           = 26,
+	H_CHECKSUM            = 28,
+	H_INTERPRETER_NUMBER  = 30,
+	H_INTERPRETER_VERSION = 31,
+	H_SCREEN_ROWS         = 32,
+	H_SCREEN_COLS         = 33,
+	H_SCREEN_WIDTH        = 34,
+	H_SCREEN_HEIGHT       = 36,
+	H_FONT_HEIGHT         = 38,		///< this is the font width in V5
+	H_FONT_WIDTH          = 39,		///< this is the font height in V5
+	H_FUNCTIONS_OFFSET    = 40,
+	H_STRINGS_OFFSET      = 42,
+	H_DEFAULT_BACKGROUND  = 44,
+	H_DEFAULT_FOREGROUND  = 45,
+	H_TERMINATING_KEYS    = 46,
+	H_LINE_WIDTH          = 48,
+	H_STANDARD_HIGH       = 50,
+	H_STANDARD_LOW        = 51,
+	H_ALPHABET            = 52,
+	H_EXTENSION_TABLE     = 54,
+	H_USER_NAME           = 56
+};
+
+/**
+ * Header extension fields
+ */
+enum {
+	HX_TABLE_SIZE    = 0,
+	HX_MOUSE_X       = 1,
+	HX_MOUSE_Y       = 2,
+	HX_UNICODE_TABLE = 3,
+	HX_FLAGS         = 4,
+	HX_FORE_COLOUR   = 5,
+	HX_BACK_COLOUR   = 6
+};
+
+/**
+ * User options
+ */
+struct UserOptions {
+	bool _attribute_assignment;
+	bool _attribute_testing;
+	bool _object_locating;
+	bool _object_movement;
+	bool _expand_abbreviations;
+	bool _ignore_errors;
+	bool _piracy;
+	bool _quetzal;
+	bool _sound;
+	bool _tandyBit;
+	int _left_margin;
+	int _right_margin;
+	int _undo_slots;
+	int _script_cols;
+	int _err_report_mode;
+
+	/**
+	 * Constructor
+	 */
+	UserOptions();
+};
+
+/**
+ * Story file header data
+ */
+struct Header {
+private:
+	struct StoryEntry {
+		Story _storyId;
+		zword _release;
+		char _serial[7];
+	};
+	static const StoryEntry RECORDS[25];
+public:
+	zbyte h_version;
+	zbyte h_config;
+	zword h_release;
+	zword h_resident_size;
+	zword h_start_pc;
+	zword h_dictionary;
+	zword h_objects;
+	zword h_globals;
+	zword h_dynamic_size;
+	zword h_flags;
+	zbyte h_serial[6];
+	zword h_abbreviations;
+	zword h_file_size;
+	zword h_checksum;
+	zbyte h_interpreter_number;
+	zbyte h_interpreter_version;
+	zbyte h_screen_rows;
+	zbyte h_screen_cols;
+	zword h_screen_width;
+	zword h_screen_height;
+	zbyte h_font_height;
+	zbyte h_font_width;
+	zword h_functions_offset;
+	zword h_strings_offset;
+	zbyte h_default_background;
+	zbyte h_default_foreground;
+	zword h_terminating_keys;
+	zword h_line_width;
+	zbyte h_standard_high;
+	zbyte h_standard_low;
+	zword h_alphabet;
+	zword h_extension_table;
+	zbyte h_user_name[8];
+
+	zword hx_table_size;
+	zword hx_mouse_x;
+	zword hx_mouse_y;
+	zword hx_unicode_table;
+	zword hx_flags;
+	zword hx_fore_colour;
+	zword hx_back_colour;
+
+	Story _storyId;
+
+	/**
+	 * Constructor
+	 */
+	Header();
+
+	/**
+	 * Load the header
+	 */
+	void loadHeader(Common::SeekableReadStream &f);
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 344aa92..5828790 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -47,36 +47,6 @@ void Frotz::runGame(Common::SeekableReadStream *gameFile) {
 }
 
 void Frotz::initialize() {
-	if (ConfMan.hasKey("attribute_assignment") && ConfMan.getBool("attribute_assignment"))
-		_attribute_assignment = true;
-	if (ConfMan.hasKey("attribute_testing") && ConfMan.getBool("attribute_testing"))
-		_attribute_testing = true;
-	if (ConfMan.hasKey("ignore_errors") && ConfMan.getBool("ignore_errors"))
-		_ignore_errors = true;
-	if (ConfMan.hasKey("object_movement") && ConfMan.getBool("object_movement"))
-		_object_movement = true;
-	if (ConfMan.hasKey("object_locating") && ConfMan.getBool("object_locating"))
-		_object_locating = true;
-	if (ConfMan.hasKey("piracy") && ConfMan.getBool("piracy"))
-		_piracy = true;
-	if (ConfMan.hasKey("save_quetzal") && ConfMan.getBool("save_quetzal"))
-		_save_quetzal = true;
-	if (ConfMan.hasKey("random_seed"))
-		_random.setSeed(ConfMan.getInt("random_seed"));
-	if (ConfMan.hasKey("script_cols"))
-		_script_cols = ConfMan.getInt("script_cols");
-	if (ConfMan.hasKey("tandy_bit") && ConfMan.getBool("tandy_bit"))
-		_user_tandy_bit = true;
-	if (ConfMan.hasKey("undo_slots"))
-		_undo_slots = ConfMan.getInt("undo_slots");
-	if (ConfMan.hasKey("expand_abbreviations") && ConfMan.getBool("expand_abbreviations"))
-		_expand_abbreviations = true;
-	if (ConfMan.hasKey("err_report_mode")) {
-		_err_report_mode = ConfMan.getInt("err_report_mode");
-		if ((_err_report_mode < ERR_REPORT_NEVER) || (_err_report_mode > ERR_REPORT_FATAL))
-			_err_report_mode = ERR_DEFAULT_REPORT_MODE;
-	}
-
 	// Call process initialization
 	Processor::initialize();
 
@@ -97,7 +67,7 @@ Common::Error Frotz::saveGameData(strid_t file) {
 	if ((gfp = frotzopenprompt(FILE_SAVE)) == nullptr)
 		goto finished;
 
-	if (_save_quetzal) {
+	if (_quetzal) {
 		success = save_quetzal(gfp, story_fp, blorb_ofs);
 	}
 	else {
@@ -164,7 +134,7 @@ Common::Error Frotz::loadGameData(strid_t file) {
 	if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
 		goto finished;
 
-	if (_save_quetzal) {
+	if (_quetzal) {
 		success = restore_quetzal (gfp, story_fp, blorb_ofs);
 
 	} else {
diff --git a/engines/glk/frotz/frotz_types.h b/engines/glk/frotz/frotz_types.h
index e68f55e..bf3067f 100644
--- a/engines/glk/frotz/frotz_types.h
+++ b/engines/glk/frotz/frotz_types.h
@@ -35,22 +35,6 @@ namespace Frotz {
 #define lo(v)	(v & 0xff)
 #define hi(v)	(v >> 8)
 
-/* There are four error reporting modes: never report errors;
- * report only the first time a given error type occurs;
- * report every time an error occurs;
- * or treat all errors as fatal errors, killing the interpreter.
- * I strongly recommend "report once" as the default. But you can compile in a
- * different default by changing the definition of ERR_DEFAULT_REPORT_MODE.
- */
-enum ErrorReport {
-	ERR_REPORT_NEVER  = 0,
-	ERR_REPORT_ONCE   = 1,
-	ERR_REPORT_ALWAYS = 2,
-	ERR_REPORT_FATAL  = 3,
-
-	ERR_DEFAULT_REPORT_MODE = ERR_REPORT_NEVER
-};
-
 /**
  * Character codes
  */
@@ -116,35 +100,6 @@ enum Version {
 	V9 = 9
 };
 
-enum ConfigFlag {
-	CONFIG_BYTE_SWAPPED = 0x01, ///< Story file is byte swapped         - V3 
-	CONFIG_TIME         = 0x02, ///< Status line displays time          - V3 
-	CONFIG_TWODISKS     = 0x04, ///< Story file occupied two disks      - V3 
-	CONFIG_TANDY        = 0x08, ///< Tandy licensed game                - V3 
-	CONFIG_NOSTATUSLINE = 0x10, ///< Interpr can't support status lines - V3 
-	CONFIG_SPLITSCREEN  = 0x20, ///< Interpr supports split screen mode - V3 
-	CONFIG_PROPORTIONAL = 0x40, ///< Interpr uses proportional font     - V3 
-	
-	CONFIG_COLOUR       = 0x01, ///< Interpr supports colour            - V5+
-	CONFIG_PICTURES	    = 0x02, ///< Interpr supports pictures	        - V6 
-	CONFIG_BOLDFACE     = 0x04, ///< Interpr supports boldface style    - V4+
-	CONFIG_EMPHASIS     = 0x08, ///< Interpr supports emphasis style    - V4+
-	CONFIG_FIXED        = 0x10, ///< Interpr supports fixed width style - V4+
-	CONFIG_SOUND	    = 0x20, ///< Interpr supports sound             - V6 
-	CONFIG_TIMEDINPUT   = 0x80, ///< Interpr supports timed input       - V4+
-	
-	SCRIPTING_FLAG	  = 0x0001, ///< Outputting to transscription file  - V1+
-	FIXED_FONT_FLAG   = 0x0002, ///< Use fixed width font               - V3+
-	REFRESH_FLAG 	  = 0x0004, ///< Refresh the screen                 - V6 
-	GRAPHICS_FLAG	  = 0x0008, ///< Game wants to use graphics         - V5+
-	OLD_SOUND_FLAG	  = 0x0010, ///< Game wants to use sound effects    - V3 
-	UNDO_FLAG	  = 0x0010, ///< Game wants to use UNDO feature     - V5+
-	MOUSE_FLAG	  = 0x0020, ///< Game wants to use a mouse          - V5+
-	COLOUR_FLAG	  = 0x0040, ///< Game wants to use colours          - V5+
-	SOUND_FLAG	  = 0x0080, ///< Game wants to use sound effects    - V5+
-	MENU_FLAG	  = 0x0100  ///< Game wants to use menus            - V6 
-};
-
 enum {
 	TRANSPARENT_FLAG = 0x0001 ///< Game wants to use transparency     - V6
 };
@@ -249,36 +204,6 @@ typedef byte zbyte;
 typedef uint zchar;
 typedef uint16 zword;
 
-/**
- * User options
- */
-struct UserOptions {
-	int _attribute_assignment;
-	int _attribute_testing;
-	int _context_lines;
-	int _object_locating;
-	int _object_movement;
-	int _left_margin;
-	int _right_margin;
-	bool _ignore_errors;
-	bool _piracy;
-	int _undo_slots;
-	int _expand_abbreviations;
-	int _script_cols;
-	bool _save_quetzal;
-	int _err_report_mode;
-	bool _sound;
-	bool _user_tandy_bit;
-
-	UserOptions() : _attribute_assignment(0), _attribute_testing(0),
-		_context_lines(0), _object_locating(0), _object_movement(0),
-		_left_margin(0), _right_margin(0), _ignore_errors(false), _piracy(false),
-		_undo_slots(MAX_UNDO_SLOTS), _expand_abbreviations(0), _script_cols(80),
-		_save_quetzal(true), _err_report_mode(ERR_DEFAULT_REPORT_MODE), _sound(true),
-		_user_tandy_bit(false) {
-	}
-};
-
 #define MAX_NESTING 16
 struct Redirect {
 	zword _xSize;
diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index 292409f..477891a 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -110,7 +110,7 @@ void GlkInterface::initialize() {
 	 * Icky magic bit setting
 	 */
 
-	if (h_version == V3 && _user_tandy_bit)
+	if (h_version == V3 && _tandyBit)
 		h_config |= CONFIG_TANDY;
 
 	if (h_version == V3 && gos_upper)
diff --git a/engines/glk/frotz/header.cpp b/engines/glk/frotz/header.cpp
deleted file mode 100644
index f7ebb5e..0000000
--- a/engines/glk/frotz/header.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "glk/frotz/header.h"
-#include "common/textconsole.h"
-
-namespace Glk {
-namespace Frotz {
-
-const Header::StoryEntry Header::RECORDS[25] = {
-	{       SHERLOCK,  21, "871214" },
-	{       SHERLOCK,  26, "880127" },
-	{    BEYOND_ZORK,  47, "870915" },
-	{    BEYOND_ZORK,  49, "870917" },
-	{    BEYOND_ZORK,  51, "870923" },
-	{    BEYOND_ZORK,  57, "871221" },
-	{      ZORK_ZERO, 296, "881019" },
-	{      ZORK_ZERO, 366, "890323" },
-	{      ZORK_ZERO, 383, "890602" },
-	{      ZORK_ZERO, 393, "890714" },
-	{         SHOGUN, 292, "890314" },
-	{         SHOGUN, 295, "890321" },
-	{         SHOGUN, 311, "890510" },
-	{         SHOGUN, 322, "890706" },
-	{         ARTHUR,  54, "890606" },
-	{         ARTHUR,  63, "890622" },
-	{         ARTHUR,  74, "890714" },
-	{        JOURNEY,  26, "890316" },
-	{        JOURNEY,  30, "890322" },
-	{        JOURNEY,  77, "890616" },
-	{        JOURNEY,  83, "890706" },
-	{ LURKING_HORROR, 203, "870506" },
-	{ LURKING_HORROR, 219, "870912" },
-	{ LURKING_HORROR, 221, "870918" },
-	{        UNKNOWN,   0, "------" }
-};
-
-void Header::loadHeader(Common::SeekableReadStream &f) {
-	h_version = f.readByte();
-	h_config = f.readByte();
-
-	if (h_version < V1 || h_version > V8)
-		error("Unknown Z-code version");
-
-	if (h_version == V6)
-		error("Cannot play Z-code version 6");
-
-	if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
-		error("Byte swapped story file");
-
-	h_release = f.readUint16BE();
-	h_resident_size = f.readUint16BE();
-	h_start_pc = f.readUint16BE();
-	h_dictionary = f.readUint16BE();
-	h_objects = f.readUint16BE();
-	h_globals = f.readUint16BE();
-	h_dynamic_size = f.readUint16BE();
-	h_flags = f.readUint16BE();
-	f.read(h_serial, 6);
-	
-	/* Auto-detect buggy story files that need special fixes */
-	_storyId = UNKNOWN;
-
-	for (int i = 0; RECORDS[i]._storyId != UNKNOWN; ++i) {
-		if (h_release == RECORDS[i]._release) {
-			if (!strncmp((const char *)h_serial, RECORDS[i]._serial, 6)) {
-				_storyId = RECORDS[i]._storyId;
-				break;
-			}
-		}
-	}
-
-	h_abbreviations = f.readUint16BE();
-	h_file_size = f.readUint16BE();
-	h_checksum = f.readUint16BE();
-	
-	f.seek(H_FUNCTIONS_OFFSET);
-	h_functions_offset = f.readUint16BE();
-	h_strings_offset = f.readUint16BE();
-	f.seek(H_TERMINATING_KEYS);
-	h_terminating_keys = f.readUint16BE();
-	f.seek(H_ALPHABET);
-	h_alphabet = f.readUint16BE();
-	h_extension_table = f.readUint16BE();
-
-
-	// Zork Zero Macintosh doesn't have the graphics flag set
-	if (_storyId == ZORK_ZERO && h_release == 296)
-		h_flags |= GRAPHICS_FLAG;
-}
-
-} // End of namespace Scott
-} // End of namespace Glk
diff --git a/engines/glk/frotz/header.h b/engines/glk/frotz/header.h
deleted file mode 100644
index cf7ab64..0000000
--- a/engines/glk/frotz/header.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GLK_FROTZ_HEADER
-#define GLK_FROTZ_HEADER
-
-#include "glk/frotz/frotz_types.h"
-
-namespace Glk {
-namespace Frotz {
-
-enum HeaderByte {
-	H_VERSION             = 0,
-	H_CONFIG              = 1,
-	H_RELEASE             = 2,
-	H_RESIDENT_SIZE       = 4,
-	H_START_PC            = 6,
-	H_DICTIONARY          = 8,
-	H_OBJECTS             = 10,
-	H_GLOBALS             = 12,
-	H_DYNAMIC_SIZE        = 14,
-	H_FLAGS               = 16,
-	H_SERIAL              = 18,
-	H_ABBREVIATIONS       = 24,
-	H_FILE_SIZE           = 26,
-	H_CHECKSUM            = 28,
-	H_INTERPRETER_NUMBER  = 30,
-	H_INTERPRETER_VERSION = 31,
-	H_SCREEN_ROWS         = 32,
-	H_SCREEN_COLS         = 33,
-	H_SCREEN_WIDTH        = 34,
-	H_SCREEN_HEIGHT       = 36,
-	H_FONT_HEIGHT         = 38,		///< this is the font width in V5
-	H_FONT_WIDTH          = 39,		///< this is the font height in V5
-	H_FUNCTIONS_OFFSET    = 40,
-	H_STRINGS_OFFSET      = 42,
-	H_DEFAULT_BACKGROUND  = 44,
-	H_DEFAULT_FOREGROUND  = 45,
-	H_TERMINATING_KEYS    = 46,
-	H_LINE_WIDTH          = 48,
-	H_STANDARD_HIGH       = 50,
-	H_STANDARD_LOW        = 51,
-	H_ALPHABET            = 52,
-	H_EXTENSION_TABLE     = 54,
-	H_USER_NAME           = 56
-};
-
-enum {
-	HX_TABLE_SIZE    = 0,
-	HX_MOUSE_X       = 1,
-	HX_MOUSE_Y       = 2,
-	HX_UNICODE_TABLE = 3,
-	HX_FLAGS         = 4,
-	HX_FORE_COLOUR   = 5,
-	HX_BACK_COLOUR   = 6
-};
-
-/**
- * Story file header data
- */
-struct Header {
-private:
-	struct StoryEntry {
-		Story _storyId;
-		zword _release;
-		char _serial[7];
-	};
-	static const StoryEntry RECORDS[25];
-public:
-	zbyte h_version;
-	zbyte h_config;
-	zword h_release;
-	zword h_resident_size;
-	zword h_start_pc;
-	zword h_dictionary;
-	zword h_objects;
-	zword h_globals;
-	zword h_dynamic_size;
-	zword h_flags;
-	zbyte h_serial[6];
-	zword h_abbreviations;
-	zword h_file_size;
-	zword h_checksum;
-	zbyte h_interpreter_number;
-	zbyte h_interpreter_version;
-	zbyte h_screen_rows;
-	zbyte h_screen_cols;
-	zword h_screen_width;
-	zword h_screen_height;
-	zbyte h_font_height;
-	zbyte h_font_width;
-	zword h_functions_offset;
-	zword h_strings_offset;
-	zbyte h_default_background;
-	zbyte h_default_foreground;
-	zword h_terminating_keys;
-	zword h_line_width;
-	zbyte h_standard_high;
-	zbyte h_standard_low;
-	zword h_alphabet;
-	zword h_extension_table;
-	zbyte h_user_name[8];
-
-	zword hx_table_size;
-	zword hx_mouse_x;
-	zword hx_mouse_y;
-	zword hx_unicode_table;
-	zword hx_flags;
-	zword hx_fore_colour;
-	zword hx_back_colour;
-
-	Story _storyId;
-
-	/**
-	 * Constructor
-	 */
-	Header() : h_version(0), h_config(0), h_release(0), h_resident_size(0), h_start_pc(0),
-			h_dictionary(0), h_objects(0), h_globals(0), h_dynamic_size(0), h_flags(0),
-			h_abbreviations(0), h_file_size(0), h_checksum(0), h_interpreter_number(0),
-			h_interpreter_version(0), h_screen_rows(0), h_screen_cols(0), h_screen_width(0),
-			h_screen_height(0), h_font_height(1), h_font_width(1), h_functions_offset(0),
-			h_strings_offset(0), h_default_background(0), h_default_foreground(0),
-			h_terminating_keys(0), h_line_width(0), h_standard_high(1), h_standard_low(1),
-			h_alphabet(0), h_extension_table(0),
-			hx_table_size(0), hx_mouse_x(0), hx_mouse_y(0), hx_unicode_table(0),
-			hx_flags(0), hx_fore_colour(0), hx_back_colour(0), _storyId(UNKNOWN) {
-		Common::fill(&h_serial[0], &h_serial[6], '\0');
-		Common::fill(&h_user_name[0], &h_user_name[8], '\0');
-	}
-
-	/**
-	 * Load the header
-	 */
-	void loadHeader(Common::SeekableReadStream &f);
-};
-
-} // End of namespace Frotz
-} // End of namespace Glk
-
-#endif
diff --git a/engines/glk/frotz/mem.h b/engines/glk/frotz/mem.h
index 3b1b81b..d973890 100644
--- a/engines/glk/frotz/mem.h
+++ b/engines/glk/frotz/mem.h
@@ -24,7 +24,7 @@
 #define GLK_FROTZ_MEM
 
 #include "glk/frotz/frotz_types.h"
-#include "glk/frotz/header.h"
+#include "glk/frotz/config.h"
 
 namespace Glk {
 namespace Frotz {
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
index 343299e..09eb82b 100644
--- a/engines/glk/frotz/processor.cpp
+++ b/engines/glk/frotz/processor.cpp
@@ -304,7 +304,7 @@ void Processor::call(zword routine, int argc, zword *args, int ct) {
 	*--_sp = (zword)(pc >> 9);
 	*--_sp = (zword)(pc & 0x1ff);
 	*--_sp = (zword)(_fp - _stack - 1);
-	*--_sp = (zword)(argc | (ct << (_save_quetzal ? 12 : 8)));
+	*--_sp = (zword)(argc | (ct << (_quetzal ? 12 : 8)));
 
 	_fp = _sp;
 	_frameCount++;
@@ -337,7 +337,7 @@ void Processor::call(zword routine, int argc, zword *args, int ct) {
 	if (_sp - _stack < count)
 		runtimeError(ERR_STK_OVF);
 
-	if (_save_quetzal)
+	if (_quetzal)
 		_fp[0] |= (zword)count << 8;	// Save local var count for Quetzal.
 
 	value = 0;
@@ -363,7 +363,7 @@ void Processor::ret(zword value) {
 
 	_sp = _fp;
 
-	ct = *_sp++ >> (_save_quetzal ? 12 : 8);
+	ct = *_sp++ >> (_quetzal ? 12 : 8);
 	_frameCount--;
 	_fp = _stack + 1 + *_sp++;
 	pc = *_sp++;
@@ -496,11 +496,11 @@ void Processor::__illegal__() {
 }
 
 void Processor::z_catch() {
-	store(_save_quetzal ? _frameCount : (zword)(_fp - _stack));
+	store(_quetzal ? _frameCount : (zword)(_fp - _stack));
 }
 
 void Processor::z_throw() {
-	if (_save_quetzal) {
+	if (_quetzal) {
 		if (zargs[1] > _frameCount)
 			runtimeError(ERR_BAD_FRAME);
 
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 10cef32..eedab57 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -21,10 +21,10 @@ MODULE_OBJS := \
 	window_pair.o \
 	window_text_buffer.o \
 	window_text_grid.o \
+	frotz/config.o \
 	frotz/detection.o \
 	frotz/frotz.o \
 	frotz/glk_interface.o \
-	frotz/header.o \
 	frotz/mem.o \
 	frotz/processor.o \
 	frotz/processor_buffer.o \


Commit: 2a3e6c49bf6d664d7c91dd17e41e97f042f70206
    https://github.com/scummvm/scummvm/commit/2a3e6c49bf6d664d7c91dd17e41e97f042f70206
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Set up default Frotz white on blue screen colors

Changed paths:
    engines/glk/frotz/config.cpp
    engines/glk/frotz/config.h
    engines/glk/frotz/frotz.cpp


diff --git a/engines/glk/frotz/config.cpp b/engines/glk/frotz/config.cpp
index 0461e25..21cfb03 100644
--- a/engines/glk/frotz/config.cpp
+++ b/engines/glk/frotz/config.cpp
@@ -161,36 +161,10 @@ UserOptions::UserOptions() : _undo_slots(MAX_UNDO_SLOTS), _sound(true) {
 	_attribute_testing = getConfigBool("attribute_testing");
 	_object_locating = getConfigBool("object_locating");
 	_object_movement = getConfigBool("object_movement");
-}
 
-#ifdef REMOVE_ME
-if (ConfMan.hasKey("attribute_assignment") && ConfMan.getBool("attribute_assignment"))
-_attribute_assignment = true;
-if (ConfMan.hasKey("attribute_testing") && ConfMan.getBool("attribute_testing"))
-_attribute_testing = true;
-if (ConfMan.hasKey("object_movement") && ConfMan.getBool("object_movement"))
-_object_movement = true;
-if (ConfMan.hasKey("object_locating") && ConfMan.getBool("object_locating"))
-_object_locating = true;
-if (ConfMan.hasKey("piracy") && ConfMan.getBool("piracy"))
-_piracy = true;
-if (ConfMan.hasKey("random_seed"))
-_random.setSeed(ConfMan.getInt("random_seed"));
-if (ConfMan.hasKey("script_cols"))
-_script_cols = ConfMan.getInt("script_cols");
-if (ConfMan.hasKey("tandy_bit") && ConfMan.getBool("tandy_bit"))
-_user_tandy_bit = true;
-if (ConfMan.hasKey("undo_slots"))
-_undo_slots = ConfMan.getInt("undo_slots");
-if (ConfMan.hasKey("expand_abbreviations") && ConfMan.getBool("expand_abbreviations"))
-_expand_abbreviations = true;
-if (ConfMan.hasKey("err_report_mode")) {
-	_err_report_mode = ConfMan.getInt("err_report_mode");
-	if ((_err_report_mode < ERR_REPORT_NEVER) || (_err_report_mode > ERR_REPORT_FATAL))
-		_err_report_mode = ERR_DEFAULT_REPORT_MODE;
+	_defaultForeground = getConfigInt("foreground", 0xffffff, 0xffffff);
+	_defaultBackground = getConfigInt("background", 0x000080, 0xffffff);
 }
 
-#endif
-
 } // End of namespace Scott
 } // End of namespace Glk
diff --git a/engines/glk/frotz/config.h b/engines/glk/frotz/config.h
index a210ba2..17e787d 100644
--- a/engines/glk/frotz/config.h
+++ b/engines/glk/frotz/config.h
@@ -148,6 +148,8 @@ struct UserOptions {
 	int _undo_slots;
 	int _script_cols;
 	int _err_report_mode;
+	uint _defaultForeground;
+	uint _defaultBackground;
 
 	/**
 	 * Constructor
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 5828790..6b79cfa 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -50,6 +50,9 @@ void Frotz::initialize() {
 	// Call process initialization
 	Processor::initialize();
 
+	// Set the screen colors
+	garglk_set_zcolors(_defaultForeground, _defaultBackground);
+
 	// Restart the game
 	z_restart();
 }


Commit: dfe497cb79b52a4835700f806e5eb5a17543dd37
    https://github.com/scummvm/scummvm/commit/dfe497cb79b52a4835700f806e5eb5a17543dd37
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Move setting default colors to GlkInterface

Changed paths:
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/glk_interface.cpp


diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 6b79cfa..5828790 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -50,9 +50,6 @@ void Frotz::initialize() {
 	// Call process initialization
 	Processor::initialize();
 
-	// Set the screen colors
-	garglk_set_zcolors(_defaultForeground, _defaultBackground);
-
 	// Restart the game
 	z_restart();
 }
diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index 477891a..a8b25a2 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -106,6 +106,9 @@ void GlkInterface::initialize() {
 	glk_set_window(gos_lower);
 	gos_curwin = gos_lower;
 
+	// Set the screen colors
+	garglk_set_zcolors(_defaultForeground, _defaultBackground);
+
 	/*
 	 * Icky magic bit setting
 	 */
@@ -148,13 +151,13 @@ void GlkInterface::initialize() {
 	h_font_width = 1;
 	h_font_height = 1;
 
-	/* Must be after screen dimensions are computed.  */
+	// Must be after screen dimensions are computed
 	if (h_version == V6) {
 		h_flags &= ~GRAPHICS_FLAG;
 	}
 
 	// Use the ms-dos interpreter number for v6, because that's the
-	// kind of graphics files we understand.  Otherwise, use DEC.
+	// kind of graphics files we understand
 	h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_AMIGA;
 	h_interpreter_version = 'F';
 


Commit: 9bc637947603643c02b27a5c0954564811603bd7
    https://github.com/scummvm/scummvm/commit/9bc637947603643c02b27a5c0954564811603bd7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Change lots of comments from multiline to single line

Changed paths:
    engines/glk/blorb.h
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/mem.cpp
    engines/glk/frotz/processor_screen.cpp
    engines/glk/frotz/processor_table.cpp
    engines/glk/frotz/processor_text.cpp
    engines/glk/streams.cpp
    engines/glk/unicode.cpp
    engines/glk/unicode.h
    engines/glk/window_graphics.cpp
    engines/glk/window_pair.h
    engines/glk/window_text_buffer.h
    engines/glk/windows.cpp


diff --git a/engines/glk/blorb.h b/engines/glk/blorb.h
index 352d01a..ae9c503 100644
--- a/engines/glk/blorb.h
+++ b/engines/glk/blorb.h
@@ -85,7 +85,9 @@ enum {
  */
 typedef struct giblorb_map_struct giblorb_map_t;
 
-/* giblorb_result_t: Result when you try to load a chunk. */
+/**
+ * giblorb_result_t: Result when you try to load a chunk.
+ */
 typedef struct giblorb_result_struct {
 	glui32 chunknum; // The chunk number (for use in giblorb_unload_chunk(), etc.)
 	union {
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 5828790..95cdb22 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -62,7 +62,7 @@ Common::Error Frotz::saveGameData(strid_t file) {
 	int skip;
 	int i;
 
-	/* Open game file */
+	// Open game file
 
 	if ((gfp = frotzopenprompt(FILE_SAVE)) == nullptr)
 		goto finished;
@@ -71,7 +71,7 @@ Common::Error Frotz::saveGameData(strid_t file) {
 		success = save_quetzal(gfp, story_fp, blorb_ofs);
 	}
 	else {
-		/* Write game file */
+		// Write game file
 
 		fputc((int)hi(h_release), gfp);
 		fputc((int)lo(h_release), gfp);
@@ -108,15 +108,14 @@ Common::Error Frotz::saveGameData(strid_t file) {
 			else skip++;
 	}
 
-	/* Close game file and check for errors */
+	// Close game file and check for errors
 
 	if (fclose(gfp) == EOF || ferror(story_fp)) {
 		print_string("Error writing save file\n");
 		goto finished;
 	}
 
-	/* Success */
-
+	// Success
 	success = 1;
 #endif
 	return Common::kNoError;
@@ -129,8 +128,7 @@ Common::Error Frotz::loadGameData(strid_t file) {
 	zword addr;
 	int i;
 
-	/* Open game file */
-
+	// Open game file
 	if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
 		goto finished;
 
@@ -138,7 +136,7 @@ Common::Error Frotz::loadGameData(strid_t file) {
 		success = restore_quetzal (gfp, story_fp, blorb_ofs);
 
 	} else {
-		/* Load game file */
+		// Load game file
 
 		release = (unsigned) fgetc (gfp) << 8;
 		release |= fgetc (gfp);
@@ -146,8 +144,7 @@ Common::Error Frotz::loadGameData(strid_t file) {
 		() fgetc (gfp);
 		() fgetc (gfp);
 
-		/* Check the release number */
-
+		// Check the release number
 		if (release == h_release) {
 
 			pc = (long) fgetc (gfp) << 16;
@@ -176,14 +173,12 @@ Common::Error Frotz::loadGameData(strid_t file) {
 				() fgetc (story_fp);
 			}
 
-			/* Check for errors */
-
+			// Check for errors
 			if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
 				success = -1;
 			else
 
-				/* Success */
-
+				// Success
 				success = 2;
 
 		} else print_string ("Invalid save file\n");
@@ -191,15 +186,14 @@ Common::Error Frotz::loadGameData(strid_t file) {
 
 	if ((short) success >= 0) {
 
-		/* Close game file */
-
+		// Close game file
 		fclose (gfp);
 
 		if ((short) success > 0) {
 			zbyte old_screen_rows;
 			zbyte old_screen_cols;
 
-			/* In V3, reset the upper window. */
+			// In V3, reset the upper window.
 			if (h_version == V3)
 				split_window (0);
 
diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index da0acaa..12e860a 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -176,8 +176,8 @@ void Mem::restart_header(void) {
 		SET_BYTE(H_SCREEN_COLS, h_screen_cols);
 	}
 
-	/* It's less trouble to use font size 1x1 for V5 games, especially
-	because of a bug in the unreleased German version of "Zork 1" */
+	// It's less trouble to use font size 1x1 for V5 games, especially because of
+	// a bug in the unreleased German version of "Zork 1"
 
 	if (h_version != V6) {
 		screen_x_size = (zword)h_screen_cols;
diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index c0d8ca1..6ee7f1d 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -312,7 +312,8 @@ void Processor::z_set_font() {
 			store (curr_font);
 			break;
 
-		case 1: /* normal font */
+		case 1:
+			// normal font
 			prev_font = curr_font;
 			curr_font = 1;
 			zargs[0] = 0xf000;	// tickle tickle!
@@ -320,7 +321,8 @@ void Processor::z_set_font() {
 			store (prev_font);
 			break; 
 
-		case 4: /* fixed-pitch font*/
+		case 4:
+			// fixed-pitch font
 			prev_font = curr_font;
 			curr_font = 4;
 			zargs[0] = 0xf000;	// tickle tickle!
@@ -355,7 +357,8 @@ void Processor::z_set_text_style() {
 
 	if (zargs[0] == 0)
 		curstyle = 0;
-	else if (zargs[0] != 0xf000) /* not tickle time */
+	else if (zargs[0] != 0xf000)
+		// not tickle time
 		curstyle |= zargs[0];
 
 	if (h_flags & FIXED_FONT_FLAG || curr_font == 4)
diff --git a/engines/glk/frotz/processor_table.cpp b/engines/glk/frotz/processor_table.cpp
index c586355..57afc76 100644
--- a/engines/glk/frotz/processor_table.cpp
+++ b/engines/glk/frotz/processor_table.cpp
@@ -31,17 +31,17 @@ void Processor::z_copy_table() {
     zbyte value;
     int i;
 
-    if (zargs[1] == 0)      				/* zero table */
-
-	for (i = 0; i < size; i++)
-	    storeb((zword) (zargs[0] + i), 0);
-
-    else if ((short) size < 0 || zargs[0] > zargs[1])	/* copy forwards */
-
-	for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) {
-	    addr = zargs[0] + i;
-		LOW_BYTE(addr, value);
-	    storeb((zword) (zargs[1] + i), value);
+	if (zargs[1] == 0) {
+		// zero table
+		for (i = 0; i < size; i++)
+			storeb((zword)(zargs[0] + i), 0);
+	} else if ((short) size < 0 || zargs[0] > zargs[1])	{
+		// copy forwards
+		for (i = 0; i < (((short)size < 0) ? -(short)size : size); i++) {
+			addr = zargs[0] + i;
+			LOW_BYTE(addr, value);
+			storeb((zword)(zargs[1] + i), value);
+		}
 	} else {
 		// copy backwards
 		for (i = size - 1; i >= 0; i--) {
diff --git a/engines/glk/frotz/processor_text.cpp b/engines/glk/frotz/processor_text.cpp
index 3cfe50b..abb0430 100644
--- a/engines/glk/frotz/processor_text.cpp
+++ b/engines/glk/frotz/processor_text.cpp
@@ -444,15 +444,13 @@ void Processor::decode_text(enum string_type st, zword addr) {
 void Processor::print_num(zword value) {
 	int i;
 
-	/* Print sign */
-
+	// Print sign
 	if ((short)value < 0) {
 		print_char('-');
 		value = -(short)value;
 	}
 
-	/* Print absolute value */
-
+	// Print absolute value
 	for (i = 10000; i != 0; i /= 10)
 		if (value >= i || i == 1)
 			print_char('0' + (value / i) % 10);
diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
index b491b89..911f6d9 100644
--- a/engines/glk/streams.cpp
+++ b/engines/glk/streams.cpp
@@ -652,7 +652,7 @@ glui32 MemoryStream::getLine(char *buf, glui32 len) {
 	if (len == 0)
 		return 0;
 
-	len -= 1; /* for the terminal null */
+	len -= 1; // for the terminal null
 	if (!_unicode) {
 		if (_bufPtr >= _bufEnd) {
 			len = 0;
@@ -1294,7 +1294,7 @@ glui32 FileStream::getLineUni(glui32 *ubuf, glui32 len) {
 		ubuf[lx] = '\0';
 		return lx;
 	} else if (_textFile) {
-		len -= 1; /* for the terminal null */
+		len -= 1; // for the terminal null
 		gotNewline = false;
 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
 			glui32 ch;
diff --git a/engines/glk/unicode.cpp b/engines/glk/unicode.cpp
index d9f2ee1..909f4ee 100644
--- a/engines/glk/unicode.cpp
+++ b/engines/glk/unicode.cpp
@@ -93,7 +93,7 @@ glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCa
 		}
 
 		if (res != 0xFFFFFFFF || res == ch) {
-			/* simple case */
+			// simple case
 			if (outcount < len)
 				outbuf[outcount] = res;
 			outcount++;
@@ -102,7 +102,7 @@ glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCa
 
 		target = (isfirst ? dest_spec_first : dest_spec_rest);
 
-		/* complicated cases */
+		// complicated cases
 		GET_CASE_SPECIAL(ch, &special);
 		if (!special) {
 			warning("inconsistency in cgunigen.c");
@@ -112,7 +112,7 @@ glui32 bufferChangeCase(glui32 *buf, glui32 len, glui32 numchars, BufferChangeCa
 		speccount = *(ptr++);
 
 		if (speccount == 1) {
-			/* simple after all */
+			// simple after all
 			if (outcount < len)
 				outbuf[outcount] = ptr[0];
 			outcount++;
diff --git a/engines/glk/unicode.h b/engines/glk/unicode.h
index 3165d39..f49a841 100644
--- a/engines/glk/unicode.h
+++ b/engines/glk/unicode.h
@@ -27,7 +27,7 @@
 
 namespace Glk {
 
-typedef glui32 gli_case_block_t[2]; /* upper, lower */
+typedef glui32 gli_case_block_t[2]; // upper, lower
 enum BufferChangeCase { CASE_UPPER = 0, CASE_LOWER = 1, CASE_TITLE = 2, CASE_IDENT = 3 };
 enum BufferChangeCond { COND_ALL = 0, COND_LINESTART = 1 };
 
diff --git a/engines/glk/window_graphics.cpp b/engines/glk/window_graphics.cpp
index f81313e..b18e2b2 100644
--- a/engines/glk/window_graphics.cpp
+++ b/engines/glk/window_graphics.cpp
@@ -143,7 +143,7 @@ void GraphicsWindow::eraseRect(bool whole, const Rect &box) {
 	hy0 = _bbox.top + y0;
 	hy1 = _bbox.top + y1;
 
-	/* zero out hyperlinks for these coordinates */
+	// zero out hyperlinks for these coordinates
 	g_vm->_selection->putHyperlink(0, hx0, hy0, hx1, hy1);
 
 	_surface->fillRect(Rect(x0, y0, x1, y1), MKTAG(_bgnd[0], _bgnd[1], _bgnd[2], 0));
@@ -173,7 +173,7 @@ void GraphicsWindow::fillRect(glui32 color, const Rect &box) {
 	hy0 = _bbox.top + y0;
 	hy1 = _bbox.top + y1;
 
-	/* zero out hyperlinks for these coordinates */
+	// zero out hyperlinks for these coordinates
 	g_vm->_selection->putHyperlink(0, hx0, hy0, hx1, hy1);
 
 	_surface->fillRect(Rect(x0, y0, x1, y1), MKTAG(col[0], col[1], col[2], 0));
@@ -225,7 +225,7 @@ void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int h
 	hy0 = _bbox.top + y0;
 	hy1 = _bbox.top + y1;
 
-	/* zero out or set hyperlink for these coordinates */
+	// zero out or set hyperlink for these coordinates
 	g_vm->_selection->putHyperlink(linkval, hx0, hy0, hx1, hy1);
 
 	w = sx1 - sx0;
diff --git a/engines/glk/window_pair.h b/engines/glk/window_pair.h
index dabae77..b588192 100644
--- a/engines/glk/window_pair.h
+++ b/engines/glk/window_pair.h
@@ -34,7 +34,7 @@ class PairWindow : public Window {
 public:
 	Window *_child1, *_child2;
 
-	/* split info... */
+	// split info...
 	glui32 _dir;               ///< winmethod_Left, Right, Above, or Below
 	bool _vertical, _backward; ///< flags
 	glui32 _division;          ///< winmethod_Fixed or winmethod_Proportional
diff --git a/engines/glk/window_text_buffer.h b/engines/glk/window_text_buffer.h
index 728919f..86b0319 100644
--- a/engines/glk/window_text_buffer.h
+++ b/engines/glk/window_text_buffer.h
@@ -107,17 +107,17 @@ public:
 	int _radjw;
 	int _radjn;
 
-	/* Command history. */
+	// Command history.
 	Common::Array<Common::U32String> _history;
 	int _historyPos;
 	int _historyFirst, _historyPresent;
 
-	/* for paging */
+	// for paging
 	int _lastSeen;
 	int _scrollPos;
 	int _scrollMax;
 
-	/* for line input */
+	// for line input
 	void *_inBuf;        ///< unsigned char* for latin1, glui32* for unicode
 	int _inMax;
 	long _inFence;
@@ -128,10 +128,10 @@ public:
 	glui32 _echoLineInput;
 	glui32 *_lineTerminators;
 
-	/* style hints and settings */
+	// style hints and settings
 	WindowStyle _styles[style_NUMSTYLES];
 
-	/* for copy selection */
+	// for copy selection
 	glui32 *_copyBuf;
 	int _copyPos;
 public:
diff --git a/engines/glk/windows.cpp b/engines/glk/windows.cpp
index 4db231c..1e53f25 100644
--- a/engines/glk/windows.cpp
+++ b/engines/glk/windows.cpp
@@ -88,7 +88,7 @@ Window *Windows::windowOpen(Window *splitwin, glui32 method, glui32 size,
 			return nullptr;
 		}
 
-		/* ignore method and size now */
+		// ignore method and size now
 		oldparent = nullptr;
 	} else {
 		if (!splitwin) {


Commit: 0f0b8ae3b72d066259332a728eab1edc760de5df
    https://github.com/scummvm/scummvm/commit/0f0b8ae3b72d066259332a728eab1edc760de5df
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Don't have a Y margin above title bar

Changed paths:
    engines/glk/conf.cpp
    engines/glk/conf.h
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/glk_types.h


diff --git a/engines/glk/conf.cpp b/engines/glk/conf.cpp
index ae99e6b..89b08fb 100644
--- a/engines/glk/conf.cpp
+++ b/engines/glk/conf.cpp
@@ -64,7 +64,7 @@ WindowStyle G_STYLES[style_NUMSTYLES] = {
 
 Conf *g_conf;
 
-Conf::Conf() {
+Conf::Conf(InterpreterType interpType) {
 	g_conf = this;
 	_imageW = g_system->getWidth();
 	_imageH = g_system->getHeight();
@@ -108,10 +108,13 @@ Conf::Conf() {
 	if (ConfMan.hasKey("maxcols"))
 		_cols = MIN(_cols, strToInt(ConfMan.get("maxcols").c_str()));
 
+	const int DEFAULT_MARGIN_X = (interpType == INTERPRETER_FROTZ) ? 2 : 15;
+	const int DEFAULT_MARGIN_Y = (interpType == INTERPRETER_FROTZ) ? 0 : 15;
+
 	get("lockrows", _lockRows);
 	get("lockcols", _lockCols);
-	get("wmarginx", _wMarginX, 15);
-	get("wmarginy", _wMarginY, 15);
+	get("wmarginx", _wMarginX, DEFAULT_MARGIN_X);
+	get("wmarginy", _wMarginY, DEFAULT_MARGIN_Y);
 	_wMarginSaveX = _wMarginX;
 	_wMarginSaveY = _wMarginY;
 
diff --git a/engines/glk/conf.h b/engines/glk/conf.h
index fb68427..bf68e34 100644
--- a/engines/glk/conf.h
+++ b/engines/glk/conf.h
@@ -29,6 +29,11 @@
 
 namespace Glk {
 
+
+
+/**
+ * Engine configuration
+ */
 class Conf {
 private:
 	/**
@@ -126,7 +131,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Conf();
+	Conf(InterpreterType interpType);
 };
 
 extern Conf *g_conf;
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 9488c66..0c90fc7 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -68,7 +68,7 @@ void GlkEngine::initialize() {
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
 
 	initGraphicsMode();
-	_conf = new Conf();
+	_conf = new Conf(getInterpreterType());
 	_screen = new Screen();
 
 	_clipboard = new Clipboard();
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 63b5727..8f973f0 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -43,24 +43,6 @@ class Selection;
 class Streams;
 class Windows;
 
-enum InterpreterType {
-	INTERPRETER_ADVSYS = 0,
-	INTERPRETER_AGILITY = 1,
-	INTERPRETER_ALAN2 = 2,
-	INTERPRETER_ALAN3 = 3,
-	INTERPRETER_BOCFEL = 4,
-	INTERPRETER_FROTZ = 5,
-	INTERPRETER_GEAS = 6,
-	INTERPRETER_HUGO = 7,
-	INTERPRETER_JACL = 8,
-	INTERPRETER_LEVEL9 = 9,
-	INTERPRETER_MAGNETIC = 10,
-	INTERPRETER_NITFOL = 11,
-	INTERPRETER_SCARE = 12,
-	INTERPRETER_SCOTT = 13,
-	INTERPRETER_TADS = 14
-};
-
 enum GlkDebugChannels {
 	kDebugCore      = 1 << 0,
 	kDebugScripts   = 1 << 1,
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index 1745178..4ac9566 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -33,6 +33,27 @@ typedef int32 glsi32;
 class Window;
 
 /**
+ * List of the different sub-engines the engine will likely eventually support
+ */
+enum InterpreterType {
+	INTERPRETER_ADVSYS = 0,
+	INTERPRETER_AGILITY = 1,
+	INTERPRETER_ALAN2 = 2,
+	INTERPRETER_ALAN3 = 3,
+	INTERPRETER_BOCFEL = 4,
+	INTERPRETER_FROTZ = 5,
+	INTERPRETER_GEAS = 6,
+	INTERPRETER_HUGO = 7,
+	INTERPRETER_JACL = 8,
+	INTERPRETER_LEVEL9 = 9,
+	INTERPRETER_MAGNETIC = 10,
+	INTERPRETER_NITFOL = 11,
+	INTERPRETER_SCARE = 12,
+	INTERPRETER_SCOTT = 13,
+	INTERPRETER_TADS = 14
+};
+
+/**
  * These are the compile-time conditionals that reveal various Glk optional modules.
  */
 #define GLK_MODULE_LINE_ECHO


Commit: bd86fd7bbf19324e06834ee8f2e08e7e0df02e14
    https://github.com/scummvm/scummvm/commit/bd86fd7bbf19324e06834ee8f2e08e7e0df02e14
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Implementing picture loading

Changed paths:
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/glk_api.cpp
    engines/glk/picture.cpp
    engines/glk/picture.h
    engines/glk/window_graphics.cpp
    engines/glk/window_text_buffer.cpp


diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 0c90fc7..0e44f04 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -43,7 +43,7 @@ GlkEngine *g_vm;
 
 GlkEngine::GlkEngine(OSystem *syst, const GlkGameDescription &gameDesc) :
 	_gameDescription(gameDesc), Engine(syst), _random("Glk"), _clipboard(nullptr),
-	_conf(nullptr), _events(nullptr), _picList(nullptr), _screen(nullptr),
+	_conf(nullptr), _events(nullptr), _pictures(nullptr), _screen(nullptr),
 	_selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
 	gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
 	g_vm = this;
@@ -53,7 +53,7 @@ GlkEngine::~GlkEngine() {
 	delete _clipboard;
 	delete _conf;
 	delete _events;
-	delete _picList;
+	delete _pictures;
 	delete _screen;
 	delete _selection;
 	delete _streams;
@@ -73,7 +73,7 @@ void GlkEngine::initialize() {
 
 	_clipboard = new Clipboard();
 	_events = new Events();
-	_picList = new PicList();
+	_pictures = new Pictures();
 	_selection = new Selection();
 	_streams = new Streams();
 	_windows = new Windows(_screen);
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 8f973f0..50722ae 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -37,7 +37,7 @@ namespace Glk {
 class Clipboard;
 class Conf;
 class Events;
-class PicList;
+class Pictures;
 class Screen;
 class Selection;
 class Streams;
@@ -96,7 +96,7 @@ public:
 	Clipboard *_clipboard;
 	Conf *_conf;
 	Events *_events;
-	PicList *_picList;
+	Pictures *_pictures;
 	Screen *_screen;
 	Selection *_selection;
 	Streams *_streams;
diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index e04040f..ec98ab6 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -924,7 +924,7 @@ glui32 GlkAPI::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
 	if (!g_conf->_graphics)
 		return false;
 
-	Picture *pic = Picture::load(image);
+	Picture *pic = g_vm->_pictures->load(image);
 	if (!pic)
 		return false;
 
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index b87b129..8461983 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -21,15 +21,109 @@
  */
 
 #include "glk/picture.h"
+#include "common/file.h"
+#include "image/jpeg.h"
+#include "image/png.h"
 
 namespace Glk {
 
-void PicList::increment() {
-	// TODO
+void Pictures::clear() {
+	for (uint idx = 0; idx < _store.size(); ++idx) {
+		delete _store[idx]._picture;
+		delete _store[idx]._scaled;
+	}
+
+	_store.clear();
+}
+
+void Pictures::increment() {
+	++_refCount;
+}
+
+void Pictures::decrement() {
+	if (_refCount > 0 && --_refCount == 0)
+		clear();
+}
+
+PictureEntry *Pictures::search(uint id) {
+	Picture *pic;
+
+	for (uint idx = 0; idx < _store.size(); ++idx) {
+		pic = _store[idx]._picture;
+
+		if (pic && pic->_id == id)
+			return &_store[idx];
+	}
+
+	return nullptr;
+}
+
+void Pictures::storeOriginal(Picture *pic) {
+	PictureEntry newPic;
+	newPic._picture = pic;
+
+	_store.push_back(newPic);
+}
+
+void Pictures::storeScaled(Picture *pic) {
+	PictureEntry *entry = search(pic->_id);
+	if (!entry)
+		return;
+
+	delete entry->_scaled;
+	entry->_scaled = pic;
 }
 
-void PicList::decrement() {
-	// TODO
+void Pictures::store(Picture *pic) {
+	if (!pic)
+		return;
+
+	if (!pic->_scaled)
+		storeOriginal(pic);
+	else
+		storeScaled(pic);
+}
+
+Picture *Pictures::retrieve(uint id, bool scaled) {
+	Picture *pic;
+
+	for (uint idx = 0; idx < _store.size(); ++idx) {
+		pic = scaled ? _store[idx]._scaled : _store[idx]._picture;
+
+		if (pic && pic->_id == id)
+			return pic;
+	}
+
+	return nullptr;
+}
+
+Picture *Pictures::load(uint32 id) {
+	::Image::PNGDecoder png;
+	::Image::JPEGDecoder jpg;
+	const Graphics::Surface *img;
+	Picture *pic;
+
+	// Check if the picture is already in the store
+    pic = retrieve(id, false);
+    if (pic)
+        return pic;
+
+	Common::File f;
+	if (f.open(Common::String::format("PIC%lu.png", id))) {
+		png.loadStream(f);
+		img = png.getSurface();
+	} else if (f.open(Common::String::format("PIC%lu.jpg", id))) {
+		jpg.loadStream(f);
+		img = jpg.getSurface();
+	}
+
+	pic = new Picture();
+	pic->_refCount = 1;
+    pic->_id = id;
+    pic->_scaled = false;
+
+    store(pic);
+    return pic;
 }
 
 /*--------------------------------------------------------------------------*/
@@ -40,16 +134,11 @@ void Picture::increment() {
 
 void Picture::decrement() {
 	if (_refCount > 0 && --_refCount == 0) {
-		free();
+		// No longer any references to this picture, so remove it
 		delete this;
 	}
 }
 
-Picture *Picture::load(uint32 id) {
-	// TODO: gli_picture_load
-	return nullptr;
-}
-
 Picture *Picture::scale(int sx, int sy) {
 	// TODO: gli_picture_scale
 	return nullptr;
diff --git a/engines/glk/picture.h b/engines/glk/picture.h
index 3ca615b..ca4f999 100644
--- a/engines/glk/picture.h
+++ b/engines/glk/picture.h
@@ -23,20 +23,15 @@
 #ifndef GLK_PICTURE_H
 #define GLK_PICTURE_H
 
-#include "graphics/surface.h"
+#include "common/array.h"
+#include "graphics/managed_surface.h"
 
 namespace Glk {
 
-class PicList {
-public:
-	void increment();
-
-	void decrement();
-};
-
-struct Picture : Graphics::Surface {
-public:
-	static Picture *load(uint32 id);
+/**
+ * Picture/image class
+ */
+struct Picture : Graphics::ManagedSurface {
 public:
 	int _refCount;
 	uint32 _id;
@@ -45,7 +40,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Picture() : Graphics::Surface(), _refCount(0), _id(0), _scaled(0) {}
+	Picture() : Graphics::ManagedSurface(), _refCount(0), _id(0), _scaled(0) {}
 
 	/**
 	 * Increment reference counter
@@ -68,6 +63,79 @@ public:
 	void drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1);
 };
 
+/**
+ * Picture entry in the in-memory store
+ */
+struct PictureEntry {
+	Picture *_picture;
+	Picture *_scaled;
+	PictureEntry() : _picture(nullptr), _scaled(nullptr) {}
+};
+
+/**
+ * Pictures manager
+ */
+class Pictures {
+private:
+	int _refCount;
+	Common::Array<PictureEntry> _store;
+private:
+	/**
+	 * Stores an original picture in the store
+	 */
+	void storeOriginal(Picture *pic);
+
+	/**
+	 * Stores a scaled picture in the store
+	 */
+	void storeScaled(Picture *pic);
+public:
+	/**
+	 * Constructor
+	 */
+	Pictures() : _refCount(0) {}
+
+	/**
+	 * Destructor
+	 */
+	~Pictures() { clear(); }
+
+	/**
+	 * Clear the picture list
+	 */
+	void clear();
+
+	/**
+	 * Increments the count of the number of pictures in use
+	 */
+	void increment();
+
+	/**
+	 * Decrements the count of the number of pictures in use
+	 */
+	void decrement();
+
+	/**
+	 * Searches for an existing picture entry
+	 */
+	PictureEntry *search(uint id);
+
+	/**
+	 * Stores a picture in the store
+	 */
+	void store(Picture *pic);
+
+	/**
+	 * Retrieves a picture from the store
+	 */
+	Picture *retrieve(uint id, bool scaled);
+
+	/**
+	 * Load a given picture
+	 */
+	Picture *load(uint32 id);
+};
+
 } // End of namespace Glk
 
 #endif
diff --git a/engines/glk/window_graphics.cpp b/engines/glk/window_graphics.cpp
index b18e2b2..13e6645 100644
--- a/engines/glk/window_graphics.cpp
+++ b/engines/glk/window_graphics.cpp
@@ -96,14 +96,14 @@ void GraphicsWindow::redraw() {
 
 glui32 GraphicsWindow::drawPicture(glui32 image, glsi32 xpos, glsi32 ypos, int scale,
                                    glui32 imagewidth, glui32 imageheight) {
-	Picture *pic = Picture::load(image);
+	Picture *pic = g_vm->_pictures->load(image);
 	glui32 hyperlink = _attr.hyper;
 
 	if (!pic)
 		return false;
 
 	if (!_imageLoaded) {
-		g_vm->_picList->increment();
+		g_vm->_pictures->increment();
 		_imageLoaded = true;
 	}
 
diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index 2a98581..6f861fb 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -274,13 +274,13 @@ glui32 TextBufferWindow::drawPicture(glui32 image, glui32 align, glui32 scaled,
 	glui32 hyperlink;
 	int error;
 
-	pic = Picture::load(image);
+	pic = g_vm->_pictures->load(image);
 
 	if (!pic)
 		return false;
 
 	if (!_imageLoaded) {
-		g_vm->_picList->increment();
+		g_vm->_pictures->increment();
 		_imageLoaded = true;
 	}
 


Commit: 5a05140ac34a6a177b90a22f8601f2adff4831d5
    https://github.com/scummvm/scummvm/commit/5a05140ac34a6a177b90a22f8601f2adff4831d5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add picture scaling

Changed paths:
    engines/glk/picture.cpp
    engines/glk/picture.h
    engines/glk/window_graphics.cpp
    engines/glk/window_text_buffer.cpp


diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 8461983..89e75fa 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -126,6 +126,21 @@ Picture *Pictures::load(uint32 id) {
     return pic;
 }
 
+Picture *Pictures::scale(Picture *src, size_t sx, size_t sy) {
+	// Check for the presence of an already scaled version of that size
+	Picture *dst = retrieve(src->_id, true);
+	if (dst && dst->w == sx && dst->h == sy)
+		return dst;
+
+	// Create a new picture of the destination size and rescale the source picture
+	dst = new Picture(sx, sy, src->format);
+	dst->_id = src->_id;
+	dst->_scaled = true;
+	dst->transBlitFrom(*src, src->getBounds(), dst->getBounds(), (uint)-1);
+
+	storeScaled(dst);
+}
+
 /*--------------------------------------------------------------------------*/
 
 void Picture::increment() {
@@ -139,11 +154,6 @@ void Picture::decrement() {
 	}
 }
 
-Picture *Picture::scale(int sx, int sy) {
-	// TODO: gli_picture_scale
-	return nullptr;
-}
-
 void Picture::drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1) {
 	// TODO: drawPicture
 }
diff --git a/engines/glk/picture.h b/engines/glk/picture.h
index ca4f999..c0108dd 100644
--- a/engines/glk/picture.h
+++ b/engines/glk/picture.h
@@ -40,7 +40,13 @@ public:
 	/**
 	 * Constructor
 	 */
-	Picture() : Graphics::ManagedSurface(), _refCount(0), _id(0), _scaled(0) {}
+	Picture() : Graphics::ManagedSurface(), _refCount(0), _id(0), _scaled(false) {}
+
+	/**
+	 * Constructor
+	 */
+	Picture(int width, int height, const Graphics::PixelFormat &format) :
+		Graphics::ManagedSurface(width, height, format), _refCount(0), _id(0), _scaled(false) {}
 
 	/**
 	 * Increment reference counter
@@ -53,11 +59,6 @@ public:
 	void decrement();
 
 	/**
-	 * Rescale the picture to a new picture of a given size
-	 */
-	Picture *scale(int sx, int sy);
-
-	/**
 	 * Draw the picture
 	 */
 	void drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1);
@@ -134,6 +135,11 @@ public:
 	 * Load a given picture
 	 */
 	Picture *load(uint32 id);
+
+	/**
+	 * Rescale the passed picture to a new picture of a given size
+	 */
+	Picture *scale(Picture *src, size_t sx, size_t sy);
 };
 
 } // End of namespace Glk
diff --git a/engines/glk/window_graphics.cpp b/engines/glk/window_graphics.cpp
index 13e6645..92224b6 100644
--- a/engines/glk/window_graphics.cpp
+++ b/engines/glk/window_graphics.cpp
@@ -186,7 +186,7 @@ void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int h
 	int w, h;
 
 	if (width != src->w || height != src->h) {
-		src = src->scale(width, height);
+		src = g_vm->_pictures->scale(src, width, height);
 		if (!src)
 			return;
 	}
diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index 6f861fb..4c55066 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -286,7 +286,7 @@ glui32 TextBufferWindow::drawPicture(glui32 image, glui32 align, glui32 scaled,
 
 	if (scaled) {
 		Picture *tmp;
-		tmp = pic->scale(width, height);
+		tmp = g_vm->_pictures->scale(pic, width, height);
 		pic = tmp;
 	}
 


Commit: f7cb4170847373a84268479bd7487c20c58d5639
    https://github.com/scummvm/scummvm/commit/f7cb4170847373a84268479bd7487c20c58d5639
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add picture drawing

Changed paths:
    engines/glk/picture.cpp
    engines/glk/picture.h
    engines/glk/window_text_buffer.cpp


diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 89e75fa..122d9c3 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -21,6 +21,8 @@
  */
 
 #include "glk/picture.h"
+#include "glk/glk.h"
+#include "glk/screen.h"
 #include "common/file.h"
 #include "image/jpeg.h"
 #include "image/png.h"
@@ -29,8 +31,8 @@ namespace Glk {
 
 void Pictures::clear() {
 	for (uint idx = 0; idx < _store.size(); ++idx) {
-		delete _store[idx]._picture;
-		delete _store[idx]._scaled;
+		_store[idx]._picture->decrement();
+		_store[idx]._scaled->decrement();
 	}
 
 	_store.clear();
@@ -139,6 +141,7 @@ Picture *Pictures::scale(Picture *src, size_t sx, size_t sy) {
 	dst->transBlitFrom(*src, src->getBounds(), dst->getBounds(), (uint)-1);
 
 	storeScaled(dst);
+	return dst;
 }
 
 /*--------------------------------------------------------------------------*/
@@ -154,9 +157,9 @@ void Picture::decrement() {
 	}
 }
 
-void Picture::drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1) {
-	// TODO: drawPicture
+void Picture::drawPicture(const Common::Point &destPos, const Common::Rect &box) {
+	Graphics::Surface s = g_vm->_screen->getSubArea(box);
+	s.copyRectToSurface(*this, destPos.x - box.left, destPos.y, getBounds());
 }
 
-
 } // End of namespace Glk
diff --git a/engines/glk/picture.h b/engines/glk/picture.h
index c0108dd..f221569 100644
--- a/engines/glk/picture.h
+++ b/engines/glk/picture.h
@@ -61,7 +61,7 @@ public:
 	/**
 	 * Draw the picture
 	 */
-	void drawPicture(int x0, int y0, int dx0, int dy0, int dx1, int dy1);
+	void drawPicture(const Common::Point &destPos, const Common::Rect &box);
 };
 
 /**
diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index 4c55066..35f5af9 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -1070,7 +1070,8 @@ void TextBufferWindow::redraw() {
 
 		if (ln->_lPic) {
 			if (y < y1 && y + ln->_lPic->h > y0) {
-				ln->_lPic->drawPicture(x0 / GLI_SUBPIX, y, x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1);
+				ln->_lPic->drawPicture(Point(x0 / GLI_SUBPIX, y),
+					Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
 				link = ln->_lHyper;
 				hy0 = y > y0 ? y : y0;
 				hy1 = y + ln->_lPic->h < y1 ? y + ln->_lPic->h : y1;
@@ -1084,8 +1085,8 @@ void TextBufferWindow::redraw() {
 
 		if (ln->_rPic) {
 			if (y < y1 && y + ln->_rPic->h > y0) {
-				ln->_rPic->drawPicture(x1 / GLI_SUBPIX - ln->_rPic->w, y,
-				                       x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1);
+				ln->_rPic->drawPicture(Point(x1 / GLI_SUBPIX - ln->_rPic->w, y),
+					Rect(x0 / GLI_SUBPIX, y0, x1 / GLI_SUBPIX, y1));
 				link = ln->_rHyper;
 				hy0 = y > y0 ? y : y0;
 				hy1 = y + ln->_rPic->h < y1 ? y + ln->_rPic->h : y1;


Commit: 525c09ef38c00d71cf3037d61d221783d3b48cf3
    https://github.com/scummvm/scummvm/commit/525c09ef38c00d71cf3037d61d221783d3b48cf3
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: In progress transforming Blorb code to be a Common::Archive

Changed paths:
    engines/glk/blorb.cpp
    engines/glk/blorb.h
    engines/glk/frotz/mem.cpp
    engines/glk/frotz/mem.h
    engines/glk/frotz/processor_streams.cpp
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/glk_api.h


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index ed730bf..2a8ef45 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -54,7 +54,6 @@ struct giblorb_resdesc_struct {
  */
 struct giblorb_map_struct {
     glui32 inited; ///< holds giblorb_Inited_Magic if the map structure is valid
-	Common::SeekableReadStream *file;
 
     uint numchunks;
     giblorb_chunkdesc_t *chunks;	///< list of chunk descriptors
@@ -66,13 +65,68 @@ struct giblorb_map_struct {
 
 /*--------------------------------------------------------------------------*/
 
-giblorb_err_t Blorb::giblorb_initialize() {
-	_file = nullptr;
-	_map = nullptr;
-	return giblorb_err_None;
+Blorb::Blorb(const Common::String &filename, InterpreterType interpType) :
+		Common::Archive(), _interpType(interpType), _map(nullptr) {
+	if (!_file.open(filename))
+		error("Could not open blorb file");
+
+	if (create_map() != giblorb_err_None)
+		error("Could not parse blorb file");
+}
+
+Blorb::~Blorb() {
+	for (uint ix = 0; ix < _map->numchunks; ix++) {
+		giblorb_chunkdesc_t *chu = &(_map->chunks[ix]);
+		if (chu->ptr) {
+			delete chu->ptr;
+			chu->ptr = nullptr;
+		}
+	}
+
+	if (_map->chunks) {
+		delete[] _map->chunks;
+		_map->chunks = nullptr;
+	}
+
+	_map->numchunks = 0;
+
+	if (_map->resources) {
+		delete[] _map->resources;
+		_map->resources = nullptr;
+	}
+
+	if (_map->ressorted) {
+		delete[] _map->ressorted;
+		_map->ressorted = nullptr;
+	}
+
+	_map->numresources = 0;
+	_map->inited = 0;
+
+	delete _map;
+}
+
+bool Blorb::hasFile(const Common::String &name) const {
+	return false;
+}
+
+int Blorb::listMatchingMembers(Common::ArchiveMemberList &list, const Common::String &pattern) const {
+	return 0;
+}
+
+int Blorb::listMembers(Common::ArchiveMemberList &list) const {
+	return 0;
 }
 
-giblorb_err_t Blorb::giblorb_create_map(Common::SeekableReadStream *file, giblorb_map_t **newmap) {
+const Common::ArchiveMemberPtr Blorb::getMember(const Common::String &name) const {
+	return Common::ArchiveMemberPtr();
+}
+
+Common::SeekableReadStream *Blorb::createReadStreamForMember(const Common::String &name) const {
+	return nullptr;
+}
+
+giblorb_err_t Blorb::create_map() {
 	giblorb_err_t err;
 	giblorb_map_t *map;
 	glui32 readlen;
@@ -81,21 +135,12 @@ giblorb_err_t Blorb::giblorb_create_map(Common::SeekableReadStream *file, giblor
 	int chunks_size, numchunks;
 	char buffer[16];
 
-	*newmap = nullptr;
-
-	if (!_libInited) {
-		err = giblorb_initialize();
-		if (err)
-			return err;
-		_libInited = true;
-	}
-
-	/* First, chew through the file and index the chunks. */
-	file->seek(0);
+	// First, chew through the file and index the chunks
+	_file.seek(0);
 
-	readlen = file->read(buffer, 12);
+	readlen = _file.read(buffer, 12);
 	if (readlen != 12)
-		return giblorb_err_Read;
+		return giblorb_err_Format;
 
 	if (READ_BE_INT32(buffer + 0) != giblorb_ID_FORM)
 		return giblorb_err_Format;
@@ -114,9 +159,9 @@ giblorb_err_t Blorb::giblorb_create_map(Common::SeekableReadStream *file, giblor
 		int chunum;
 		giblorb_chunkdesc_t *chu;
 
-		file->seek(nextpos);
+		_file.seek(nextpos);
 
-		readlen = file->read(buffer, 8);
+		readlen = _file.read(buffer, 8);
 		if (readlen != 8) {
 			delete[] chunks;
 			return giblorb_err_Read;
@@ -158,34 +203,29 @@ giblorb_err_t Blorb::giblorb_create_map(Common::SeekableReadStream *file, giblor
 
 	// The basic IFF structure seems to be ok, and we have a list of chunks.
 	// Now we allocate the map structure itself.
-	map = new giblorb_map_t();
-	if (!map) {
+	_map = new giblorb_map_t();
+	if (!_map) {
 		delete[] chunks;
 		return giblorb_err_Alloc;
 	}
 
-	map->inited = giblorb_Inited_Magic;
-	map->file = file;
-	map->chunks = chunks;
-	map->numchunks = numchunks;
-	map->resources = nullptr;
-	map->ressorted = nullptr;
-	map->numresources = 0;
+	_map->inited = giblorb_Inited_Magic;
+	_map->chunks = chunks;
+	_map->numchunks = numchunks;
+	_map->resources = nullptr;
+	_map->ressorted = nullptr;
+	_map->numresources = 0;
 
 	// Now we do everything else involved in loading the Blorb file,
 	// such as building resource lists.
-	err = giblorb_initialize_map(map);
-	if (err) {
-		giblorb_destroy_map(map);
+	err = initialize_map();
+	if (err)
 		return err;
-	}
 
-	*newmap = map;
 	return giblorb_err_None;
 }
 
-
-giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
+giblorb_err_t Blorb::initialize_map() {
 	// It is important that the map structure be kept valid during this function.
 	// If this returns an error, giblorb_destroy_map() will be called.
 	uint ix, jx;
@@ -196,8 +236,8 @@ giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
 	glui32 numres;
 	int gotindex = false;
 
-	for (ix = 0; ix<map->numchunks; ix++) {
-		giblorb_chunkdesc_t *chu = &map->chunks[ix];
+	for (ix = 0; ix < _map->numchunks; ix++) {
+		giblorb_chunkdesc_t *chu = &_map->chunks[ix];
 
 		switch (chu->type) {
 		case giblorb_ID_RIdx:
@@ -205,7 +245,7 @@ giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
 
 			if (gotindex)
 				return giblorb_err_Format; // duplicate index chunk
-			err = giblorb_load_chunk_by_number(map, giblorb_method_Memory, &chunkres, ix);
+			err = load_chunk_by_number(giblorb_method_Memory, &chunkres, ix);
 			if (err)
 				return err;
 
@@ -238,12 +278,12 @@ giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
 					res->resnum = READ_BE_INT32(ptr + jx * 12 + 8);
 					respos = READ_BE_INT32(ptr + jx * 12 + 12);
 
-					while (ix2 < map->numchunks
-						&& map->chunks[ix2].startpos < respos)
+					while (ix2 < _map->numchunks
+						&& _map->chunks[ix2].startpos < respos)
 						ix2++;
 
-					if (ix2 >= map->numchunks
-						|| map->chunks[ix2].startpos != respos) {
+					if (ix2 >= _map->numchunks
+						|| _map->chunks[ix2].startpos != respos) {
 						delete[] resources;
 						delete[] ressorted;
 						return giblorb_err_Format; // start pos does not match a real chunk
@@ -254,16 +294,16 @@ giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
 					ressorted[jx] = res;
 				}
 
-				// Sort a resource list (actually a list of pointers to structures in map->resources.)
+				// Sort a resource list (actually a list of pointers to structures in _map->resources.)
 				// This makes it easy to find resources by usage and resource number.
-				giblorb_qsort(ressorted, numres);
+				qsort(ressorted, numres);
 
-				map->numresources = numres;
-				map->resources = resources;
-				map->ressorted = ressorted;
+				_map->numresources = numres;
+				_map->resources = resources;
+				_map->ressorted = ressorted;
 			}
 
-			giblorb_unload_chunk(map, ix);
+			unload_chunk(ix);
 			gotindex = true;
 			break;
 		}
@@ -272,7 +312,7 @@ giblorb_err_t Blorb::giblorb_initialize_map(giblorb_map_t *map) {
 	return giblorb_err_None;
 }
 
-void Blorb::giblorb_qsort(giblorb_resdesc_t **list, size_t len) {
+void Blorb::qsort(giblorb_resdesc_t **list, size_t len) {
 	int ix, jx, res;
 	giblorb_resdesc_t *tmpptr, *pivot;
 
@@ -306,12 +346,12 @@ void Blorb::giblorb_qsort(giblorb_resdesc_t **list, size_t len) {
 		}
 		ix++;
 		// Sort the halves.
-		giblorb_qsort(list + 0, ix);
-		giblorb_qsort(list + ix, len - ix);
+		qsort(list + 0, ix);
+		qsort(list + ix, len - ix);
 	}
 }
 
-giblorb_resdesc_t *Blorb::giblorb_bsearch(giblorb_resdesc_t *sample,
+giblorb_resdesc_t *Blorb::bsearch(giblorb_resdesc_t *sample,
 	giblorb_resdesc_t **list, int len) {
 	int top, bot, val, res;
 
@@ -345,71 +385,31 @@ int Blorb::sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2) {
 	return 0;
 }
 
-giblorb_err_t Blorb::giblorb_destroy_map(giblorb_map_t *map) {
-	if (!map || !map->chunks || map->inited != giblorb_Inited_Magic)
-		return giblorb_err_NotAMap;
-
-	for (uint ix = 0; ix<map->numchunks; ix++) {
-		giblorb_chunkdesc_t *chu = &(map->chunks[ix]);
-		if (chu->ptr) {
-			delete chu->ptr;
-			chu->ptr = nullptr;
-		}
-	}
-
-	if (map->chunks) {
-		delete[] map->chunks;
-		map->chunks = nullptr;
-	}
-
-	map->numchunks = 0;
-
-	if (map->resources) {
-		delete[] map->resources;
-		map->resources = nullptr;
-	}
-
-	if (map->ressorted) {
-		delete[] map->ressorted;
-		map->ressorted = nullptr;
-	}
-
-	map->numresources = 0;
-	map->file = nullptr;
-	map->inited = 0;
-
-	delete map;
-
-	return giblorb_err_None;
-}
-
-giblorb_err_t Blorb::giblorb_load_chunk_by_type(giblorb_map_t *map,
-		glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count) {
+giblorb_err_t Blorb::load_chunk_by_type(glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count) {
 	uint ix;
 
-	for (ix = 0; ix < map->numchunks; ix++) {
-		if (map->chunks[ix].type == chunktype) {
+	for (ix = 0; ix < _map->numchunks; ix++) {
+		if (_map->chunks[ix].type == chunktype) {
 			if (count == 0)
 				break;
 			count--;
 		}
 	}
 
-	if (ix >= map->numchunks) {
+	if (ix >= _map->numchunks) {
 		return giblorb_err_NotFound;
 	}
 
-	return giblorb_load_chunk_by_number(map, method, res, ix);
+	return load_chunk_by_number(method, res, ix);
 }
 
-giblorb_err_t Blorb::giblorb_load_chunk_by_number(giblorb_map_t *map,
-		glui32 method, giblorb_result_t *res, glui32 chunknum) {
+giblorb_err_t Blorb::load_chunk_by_number(glui32 method, giblorb_result_t *res, glui32 chunknum) {
 	giblorb_chunkdesc_t *chu;
 
-	if (chunknum >= map->numchunks)
+	if (chunknum >= _map->numchunks)
 		return giblorb_err_NotFound;
 
-	chu = &(map->chunks[chunknum]);
+	chu = &(_map->chunks[chunknum]);
 
 	switch (method) {
 	case giblorb_method_DontLoad:
@@ -428,9 +428,9 @@ giblorb_err_t Blorb::giblorb_load_chunk_by_number(giblorb_map_t *map,
 			if (!dat)
 				return giblorb_err_Alloc;
 
-			map->file->seek(chu->datpos);
+			_file.seek(chu->datpos);
 
-			readlen = map->file->read(dat, chu->len);
+			readlen = _file.read(dat, chu->len);
 			if (readlen != chu->len)
 				return giblorb_err_Read;
 
@@ -448,13 +448,13 @@ giblorb_err_t Blorb::giblorb_load_chunk_by_number(giblorb_map_t *map,
 	return giblorb_err_None;
 }
 
-giblorb_err_t Blorb::giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum) {
+giblorb_err_t Blorb::unload_chunk(glui32 chunknum) {
 	giblorb_chunkdesc_t *chu;
 
-	if (chunknum >= map->numchunks)
+	if (chunknum >= _map->numchunks)
 		return giblorb_err_NotFound;
 
-	chu = &(map->chunks[chunknum]);
+	chu = &(_map->chunks[chunknum]);
 
 	if (chu->ptr) {
 		delete chu->ptr;
@@ -464,24 +464,22 @@ giblorb_err_t Blorb::giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum) {
 	return giblorb_err_None;
 }
 
-giblorb_err_t Blorb::giblorb_load_resource(giblorb_map_t *map, glui32 method,
-		giblorb_result_t *res, glui32 usage, glui32 resnum) {
+giblorb_err_t Blorb::load_resource(glui32 method, giblorb_result_t *res, glui32 usage, glui32 resnum) {
 	giblorb_resdesc_t sample;
 	giblorb_resdesc_t *found;
 
 	sample.usage = usage;
 	sample.resnum = resnum;
 
-	found = giblorb_bsearch(&sample, map->ressorted, map->numresources);
+	found = bsearch(&sample, _map->ressorted, _map->numresources);
 
 	if (!found)
 		return giblorb_err_NotFound;
 
-	return giblorb_load_chunk_by_number(map, method, res, found->chunknum);
+	return load_chunk_by_number(method, res, found->chunknum);
 }
 
-giblorb_err_t Blorb::giblorb_count_resources(giblorb_map_t *map,
-		glui32 usage, glui32 *num, glui32 *min, glui32 *max) {
+giblorb_err_t Blorb::count_resources(glui32 usage, glui32 *num, glui32 *min, glui32 *max) {
 	int ix;
 	int count;
 	glui32 val;
@@ -491,9 +489,9 @@ giblorb_err_t Blorb::giblorb_count_resources(giblorb_map_t *map,
 	minval = 0;
 	maxval = 0;
 
-	for (ix = 0; ix<map->numresources; ix++) {
-		if (map->resources[ix].usage == usage) {
-			val = map->resources[ix].resnum;
+	for (ix = 0; ix<_map->numresources; ix++) {
+		if (_map->resources[ix].usage == usage) {
+			val = _map->resources[ix].resnum;
 			if (count == 0) {
 				count++;
 				minval = val;
@@ -519,25 +517,4 @@ giblorb_err_t Blorb::giblorb_count_resources(giblorb_map_t *map,
 	return giblorb_err_None;
 }
 
-giblorb_err_t Blorb::giblorb_set_resource_map(Common::SeekableReadStream *file) {
-	giblorb_err_t err;
-
-	err = giblorb_create_map(file, &_map);
-	if (err) {
-		_map = nullptr;
-		return err;
-	}
-
-	_file = file;
-	return giblorb_err_None;
-}
-
-giblorb_map_t *Blorb::giblorb_get_resource_map(void) {
-	return _map;
-}
-
-bool Blorb::giblorb_is_resource_map(void) const {
-	return _map != nullptr;
-}
-
 } // End of namespace Glk
diff --git a/engines/glk/blorb.h b/engines/glk/blorb.h
index ae9c503..97fd520 100644
--- a/engines/glk/blorb.h
+++ b/engines/glk/blorb.h
@@ -25,10 +25,11 @@
 
 #include "glk/glk_types.h"
 #include "glk/streams.h"
+#include "common/archive.h"
+#include "common/array.h"
 
 namespace Glk {
 
-
 /**
  * Error type
  */
@@ -101,46 +102,81 @@ typedef struct giblorb_result_struct {
 
 typedef struct giblorb_resdesc_struct giblorb_resdesc_t;
 
-class Blorb {
+/**
+ * Blorb file manager
+ */
+class Blorb : public Common::Archive {
 private:
-	bool _libInited;
-	Common::SeekableReadStream *_file;
+	Common::File _file;
+	InterpreterType _interpType;
 	giblorb_map_t *_map;
 private:
 	/**
-	 * Initializes Blorb
+	 * Parses the Blorb file index to load in a list of the chunks
 	 */
-	giblorb_err_t giblorb_initialize();
+	giblorb_err_t create_map();
 
-	giblorb_err_t giblorb_initialize_map(giblorb_map_t *map);
-	void giblorb_qsort(giblorb_resdesc_t **list, size_t len);
-	giblorb_resdesc_t *giblorb_bsearch(giblorb_resdesc_t *sample,
-		giblorb_resdesc_t **list, int len);
+	giblorb_err_t initialize_map();
+	void qsort(giblorb_resdesc_t **list, size_t len);
+	giblorb_resdesc_t *bsearch(giblorb_resdesc_t *sample, giblorb_resdesc_t **list, int len);
 	int sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2);
 public:
 	/**
 	 * Constructor
 	 */
-	Blorb() : _libInited(false), _file(nullptr), _map(nullptr) {}
+	Blorb(const Common::String &filename, InterpreterType interpType);
 
-	giblorb_err_t giblorb_set_resource_map(Common::SeekableReadStream *file);
-	giblorb_map_t *giblorb_get_resource_map(void);
-	bool giblorb_is_resource_map(void) const;
+	/**
+	 * Destructor
+	 */
+	~Blorb();
 
+	/**
+	 * Check if a member with the given name is present in the Archive.
+	 * Patterns are not allowed, as this is meant to be a quick File::exists()
+	 * replacement.
+	 */
+	virtual bool hasFile(const Common::String &name) const override;
 
-	giblorb_err_t giblorb_create_map(Common::SeekableReadStream *file, giblorb_map_t **newmap);
-	giblorb_err_t giblorb_destroy_map(giblorb_map_t *map);
+	/**
+	 * Add all members of the Archive matching the specified pattern to list.
+	 * Must only append to list, and not remove elements from it.
+	 *
+	 * @return the number of members added to list
+	 */
+	virtual int listMatchingMembers(Common::ArchiveMemberList &list, const Common::String &pattern) const override;
+
+	/**
+	 * Add all members of the Archive to list.
+	 * Must only append to list, and not remove elements from it.
+	 *
+	 * @return the number of names added to list
+	 */
+	virtual int listMembers(Common::ArchiveMemberList &list) const override;
+
+	/**
+	 * Returns a ArchiveMember representation of the given file.
+	 */
+	virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const override;
+
+	/**
+	 * Create a stream bound to a member with the specified name in the
+	 * archive. If no member with this name exists, 0 is returned.
+	 * @return the newly created input stream
+	 */
+	virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override;
+public:
+	/**
+	 * Get a pointer to the Blorb's resource map
+	 */
+	giblorb_map_t *get_resource_map() const { return _map; }
 
-	giblorb_err_t giblorb_load_chunk_by_type(giblorb_map_t *map,
-		glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count);
-	giblorb_err_t giblorb_load_chunk_by_number(giblorb_map_t *map,
-		glui32 method, giblorb_result_t *res, glui32 chunknum);
-	giblorb_err_t giblorb_unload_chunk(giblorb_map_t *map, glui32 chunknum);
+	giblorb_err_t load_chunk_by_type(glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count);
+	giblorb_err_t load_chunk_by_number(glui32 method, giblorb_result_t *res, glui32 chunknum);
+	giblorb_err_t unload_chunk(glui32 chunknum);
 
-	giblorb_err_t giblorb_load_resource(giblorb_map_t *map, glui32 method,
-		giblorb_result_t *res, glui32 usage, glui32 resnum);
-	giblorb_err_t giblorb_count_resources(giblorb_map_t *map,
-		glui32 usage, glui32 *num, glui32 *min, glui32 *max);
+	giblorb_err_t load_resource(glui32 method, giblorb_result_t *res, glui32 usage, glui32 resnum);
+	giblorb_err_t count_resources(glui32 usage, glui32 *num, glui32 *min, glui32 *max);
 };
 
 } // End of namespace Glk
diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index 12e860a..bec30b0 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -28,10 +28,9 @@
 namespace Glk {
 namespace Frotz {
 
-Mem::Mem() : story_fp(nullptr), blorb_ofs(0), blorb_len(0), story_size(0),
-		first_undo(nullptr), last_undo(nullptr), curr_undo(nullptr),
-		undo_mem(nullptr), prev_zmp(nullptr), undo_diff(nullptr),
-		undo_count(0), reserve_mem(0) {
+Mem::Mem() : story_fp(nullptr), story_size(0), first_undo(nullptr), last_undo(nullptr),
+		curr_undo(nullptr), undo_mem(nullptr), zmp(nullptr), prev_zmp(nullptr),
+		undo_diff(nullptr), undo_count(0), reserve_mem(0) {
 }
 
 void Mem::initialize() {
@@ -60,32 +59,7 @@ void Mem::initialize() {
 }
 
 void Mem::initializeStoryFile() {
-	Common::SeekableReadStream *f = story_fp;
-	giblorb_map_t *map;
-	giblorb_result_t res;
-	uint32 magic;
-
-	magic = f->readUint32BE();
-
-	if (magic == MKTAG('F', 'O', 'R', 'M')) {
-		if (g_vm->giblorb_set_resource_map(f))
-			error("This Blorb file seems to be invalid.");
-
-		map = g_vm->giblorb_get_resource_map();
-
-		if (g_vm->giblorb_load_resource(map, giblorb_method_FilePos, &res, giblorb_ID_Exec, 0))
-			error("This Blorb file does not contain an executable chunk.");
-		if (res.chunktype != MKTAG('Z', 'C', 'O', 'D'))
-			error("This Blorb file contains an executable chunk, but it is not a Z-code file.");
-
-		blorb_ofs = res.data.startpos;
-		blorb_len = res.length;
-	} else {
-		blorb_ofs = 0;
-		blorb_len = f->size();
-	}
-
-	if (blorb_len < 64)
+	if (story_fp->size() < 64)
 		error("This file is too small to be a Z-code file.");
 }
 
@@ -115,7 +89,7 @@ void Mem::initializeUndo() {
 void Mem::loadGameHeader() {
 	// Load header
 	zmp = new byte[64];
-	story_fp->seek(blorb_ofs);
+	story_fp->seek(0);
 	story_fp->read(zmp, 64);
 
 	Common::MemoryReadStream h(zmp, 64);
@@ -131,7 +105,7 @@ void Mem::loadGameHeader() {
 			story_size *= 2;
 	} else {
 		// Some old games lack the file size entry
-		story_size = blorb_len;
+		story_size = story_fp->size();
 	}
 }
 
@@ -254,8 +228,6 @@ void Mem::free_undo(int count) {
 
 void Mem::reset_memory() {
 	story_fp = nullptr;
-	blorb_ofs = 0;
-	blorb_len = 0;
 
 	if (undo_mem) {
 		free_undo(undo_count);
diff --git a/engines/glk/frotz/mem.h b/engines/glk/frotz/mem.h
index d973890..a632bc6 100644
--- a/engines/glk/frotz/mem.h
+++ b/engines/glk/frotz/mem.h
@@ -57,7 +57,6 @@ typedef undo_struct undo_t;
 class Mem : public Header, public virtual UserOptions {
 protected:
 	Common::SeekableReadStream *story_fp;
-	uint blorb_ofs, blorb_len;
 	uint story_size;
 	byte *pcp;
 	byte *zmp;
diff --git a/engines/glk/frotz/processor_streams.cpp b/engines/glk/frotz/processor_streams.cpp
index cbef38a..095ef24 100644
--- a/engines/glk/frotz/processor_streams.cpp
+++ b/engines/glk/frotz/processor_streams.cpp
@@ -519,7 +519,7 @@ void Processor::z_restart() {
 	seed_random(0);
 
 	if (!first_restart) {
-		story_fp->seek(blorb_ofs);
+		story_fp->seek(0);
 
 		if (story_fp->read(zmp, h_dynamic_size) != h_dynamic_size)
 			error("Story file read error");
@@ -599,7 +599,7 @@ void Processor::z_verify() {
 	zword checksum = 0;
 
 	// Sum all bytes in story file except header bytes
-	story_fp->seek(blorb_ofs + 64);
+	story_fp->seek(64);
 
 	for (uint i = 64; i < story_size; i++)
 		checksum += story_fp->readByte();
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 0e44f04..c2a8264 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -29,6 +29,7 @@
 #include "graphics/scaler.h"
 #include "graphics/thumbnail.h"
 #include "glk/glk.h"
+#include "glk/blorb.h"
 #include "glk/conf.h"
 #include "glk/events.h"
 #include "glk/picture.h"
@@ -42,14 +43,16 @@ namespace Glk {
 GlkEngine *g_vm;
 
 GlkEngine::GlkEngine(OSystem *syst, const GlkGameDescription &gameDesc) :
-	_gameDescription(gameDesc), Engine(syst), _random("Glk"), _clipboard(nullptr),
-	_conf(nullptr), _events(nullptr), _pictures(nullptr), _screen(nullptr),
-	_selection(nullptr), _windows(nullptr), _copySelect(false), _terminated(false),
-	gli_unregister_obj(nullptr), gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
+		_gameDescription(gameDesc), Engine(syst), _random("Glk"), _blorb(nullptr),
+		_clipboard(nullptr), _conf(nullptr), _events(nullptr), _pictures(nullptr),
+		_screen(nullptr), _selection(nullptr), _windows(nullptr), _copySelect(false),
+		_terminated(false), gli_unregister_obj(nullptr), gli_register_arr(nullptr),
+		gli_unregister_arr(nullptr) {
 	g_vm = this;
 }
 
 GlkEngine::~GlkEngine() {
+	delete _blorb;
 	delete _clipboard;
 	delete _conf;
 	delete _events;
@@ -96,11 +99,26 @@ void GlkEngine::initGraphicsMode() {
 }
 
 Common::Error GlkEngine::run() {
+	Common::File f;
+	Common::String filename = getFilename();
+	if (!Common::File::exists(filename))
+		return Common::kNoGameDataFoundError;
+
 	initialize();
 
-	Common::File f;
-	if (f.open(getFilename()))
-		runGame(&f);
+	if (filename.hasSuffixIgnoreCase(".blorb") || filename.hasSuffixIgnoreCase(".zblorb")) {
+		// Blorb archive
+		_blorb = new Blorb(filename, getInterpreterType());
+		SearchMan.add("blorb", _blorb, 99, false);
+
+		if (!f.open("EXEC", *_blorb))
+			return Common::kNoGameDataFoundError;
+	} else {
+		if (!f.open(filename))
+			return Common::kNoGameDataFoundError;
+	}
+
+	runGame(&f);
 
 	return Common::kNoError;
 }
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 50722ae..af9a42b 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -35,6 +35,7 @@
 namespace Glk {
 
 class Clipboard;
+class Blorb;
 class Conf;
 class Events;
 class Pictures;
@@ -93,6 +94,7 @@ protected:
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
 public:
+	Blorb *_blorb;
 	Clipboard *_clipboard;
 	Conf *_conf;
 	Events *_events;
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
index 400f3c0..b625f69 100644
--- a/engines/glk/glk_api.h
+++ b/engines/glk/glk_api.h
@@ -34,7 +34,7 @@ namespace Glk {
 /**
  * Implements the GLK interface
  */
-class GlkAPI : public GlkEngine, public Blorb {
+class GlkAPI : public GlkEngine {
 private:
 	bool _gliFirstEvent;
 	unsigned char _charTolowerTable[256];


Commit: 13e28c0d0dad6727050e282eb475ab0052d892a6
    https://github.com/scummvm/scummvm/commit/13e28c0d0dad6727050e282eb475ab0052d892a6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Finish refactoring Blorb class

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


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index 2a8ef45..ace0e91 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -24,497 +24,162 @@
 
 namespace Glk {
 
-#define giblorb_Inited_Magic 0xB7012BEDU
-
-/**
- * Describes one chunk of the Blorb file.
- */
-struct giblorb_chunkdesc_struct {
-    glui32 type;
-    glui32 len;
-    glui32 startpos;	///< start of chunk header
-    glui32 datpos;		///< start of data (either startpos or startpos+8)
-
-    byte *ptr;		///< pointer to malloc'd data, if loaded
-    int auxdatnum;	///< entry in the auxsound/auxpict array; -1 if none. This only applies to chunks that represent resources;
-};
-typedef giblorb_chunkdesc_struct giblorb_chunkdesc_t;
-
-/**
- * Describes one resource in the Blorb file.
- */
-struct giblorb_resdesc_struct {
-	glui32 usage;
-	glui32 resnum;
-	glui32 chunknum;
-};
-
-/**
- * Holds the complete description of an open Blorb file. 
- */
-struct giblorb_map_struct {
-    glui32 inited; ///< holds giblorb_Inited_Magic if the map structure is valid
-
-    uint numchunks;
-    giblorb_chunkdesc_t *chunks;	///< list of chunk descriptors
-
-    int numresources;
-    giblorb_resdesc_t *resources;	///< list of resource descriptors
-    giblorb_resdesc_t **ressorted;	///< list of pointers to descriptors in map->resources -- sorted by usage and resource number.
+enum {
+	ID_FORM = MKTAG('F', 'O', 'R', 'M'),
+	ID_IFRS = MKTAG('I', 'F', 'R', 'S'),
+	ID_RIdx = MKTAG('R', 'I', 'd', 'x'),
+
+	ID_Snd = MKTAG('S', 'n', 'd', ' '),
+	ID_Exec = MKTAG('E', 'x', 'e', 'c'),
+	ID_Pict = MKTAG('P', 'i', 'c', 't'),
+	ID_Data = MKTAG('D', 'a', 't', 'a'),
+
+	ID_Copyright = MKTAG('(', 'c', ')', ' '),
+	ID_AUTH = MKTAG('A', 'U', 'T', 'H'),
+	ID_ANNO = MKTAG('A', 'N', 'N', 'O'),
+
+	ID_JPEG = MKTAG('J', 'P', 'E', 'G'),
+	ID_PNG  = MKTAG('P', 'N', 'G', ' '),
+
+	ID_MIDI = MKTAG('M', 'I', 'D', 'I'),
+	ID_MP3 = MKTAG('M', 'P', '3', ' '),
+	ID_WAVE = MKTAG('W', 'A', 'V', 'E'),
+	ID_AIFF = MKTAG('A', 'I', 'F', 'F'),
+	ID_OGG = MKTAG('O', 'G', 'G', ' '),
+	ID_MOD = MKTAG('M', 'O', 'D', ' '),
 };
 
 /*--------------------------------------------------------------------------*/
 
 Blorb::Blorb(const Common::String &filename, InterpreterType interpType) :
-		Common::Archive(), _interpType(interpType), _map(nullptr) {
-	if (!_file.open(filename))
-		error("Could not open blorb file");
-
-	if (create_map() != giblorb_err_None)
+		Common::Archive(), _filename(filename), _interpType(interpType) {
+	if (load() != Common::kNoError)
 		error("Could not parse blorb file");
 }
 
-Blorb::~Blorb() {
-	for (uint ix = 0; ix < _map->numchunks; ix++) {
-		giblorb_chunkdesc_t *chu = &(_map->chunks[ix]);
-		if (chu->ptr) {
-			delete chu->ptr;
-			chu->ptr = nullptr;
-		}
-	}
-
-	if (_map->chunks) {
-		delete[] _map->chunks;
-		_map->chunks = nullptr;
-	}
-
-	_map->numchunks = 0;
-
-	if (_map->resources) {
-		delete[] _map->resources;
-		_map->resources = nullptr;
-	}
-
-	if (_map->ressorted) {
-		delete[] _map->ressorted;
-		_map->ressorted = nullptr;
+bool Blorb::hasFile(const Common::String &name) const {
+	for (uint idx = 0; idx < _chunks.size(); ++idx) {
+		if (_chunks[idx]._filename.equalsIgnoreCase(name))
+			return true;
 	}
 
-	_map->numresources = 0;
-	_map->inited = 0;
-
-	delete _map;
-}
-
-bool Blorb::hasFile(const Common::String &name) const {
 	return false;
 }
 
-int Blorb::listMatchingMembers(Common::ArchiveMemberList &list, const Common::String &pattern) const {
-	return 0;
-}
-
 int Blorb::listMembers(Common::ArchiveMemberList &list) const {
-	return 0;
-}
-
-const Common::ArchiveMemberPtr Blorb::getMember(const Common::String &name) const {
-	return Common::ArchiveMemberPtr();
-}
-
-Common::SeekableReadStream *Blorb::createReadStreamForMember(const Common::String &name) const {
-	return nullptr;
-}
-
-giblorb_err_t Blorb::create_map() {
-	giblorb_err_t err;
-	giblorb_map_t *map;
-	glui32 readlen;
-	glui32 nextpos, totallength;
-	giblorb_chunkdesc_t *chunks;
-	int chunks_size, numchunks;
-	char buffer[16];
-
-	// First, chew through the file and index the chunks
-	_file.seek(0);
-
-	readlen = _file.read(buffer, 12);
-	if (readlen != 12)
-		return giblorb_err_Format;
-
-	if (READ_BE_INT32(buffer + 0) != giblorb_ID_FORM)
-		return giblorb_err_Format;
-	if (READ_BE_INT32(buffer + 8) != giblorb_ID_IFRS)
-		return giblorb_err_Format;
-
-	totallength = READ_BE_INT32(buffer + 4) + 8;
-	nextpos = 12;
-
-	chunks_size = 8;
-	numchunks = 0;
-	chunks = new giblorb_chunkdesc_t[chunks_size];
-
-	while (nextpos < totallength) {
-		glui32 type, len;
-		int chunum;
-		giblorb_chunkdesc_t *chu;
-
-		_file.seek(nextpos);
-
-		readlen = _file.read(buffer, 8);
-		if (readlen != 8) {
-			delete[] chunks;
-			return giblorb_err_Read;
-		}
-
-		type = READ_BE_INT32(buffer + 0);
-		len = READ_BE_INT32(buffer + 4);
-
-		if (numchunks >= chunks_size) {
-			chunks_size *= 2;
-			chunks = new giblorb_chunkdesc_t[chunks_size];
-		}
-
-		chunum = numchunks;
-		chu = &(chunks[chunum]);
-		numchunks++;
-
-		chu->type = type;
-		chu->startpos = nextpos;
-		if (type == giblorb_ID_FORM) {
-			chu->datpos = nextpos;
-			chu->len = len + 8;
-		} else {
-			chu->datpos = nextpos + 8;
-			chu->len = len;
-		}
-		chu->ptr = nullptr;
-		chu->auxdatnum = -1;
-
-		nextpos = nextpos + len + 8;
-		if (nextpos & 1)
-			nextpos++;
-
-		if (nextpos > totallength) {
-			delete[] chunks;
-			return giblorb_err_Format;
-		}
-	}
-
-	// The basic IFF structure seems to be ok, and we have a list of chunks.
-	// Now we allocate the map structure itself.
-	_map = new giblorb_map_t();
-	if (!_map) {
-		delete[] chunks;
-		return giblorb_err_Alloc;
+	for (uint idx = 0; idx < _chunks.size(); ++idx) {
+		list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(_chunks[idx]._filename, this)));
 	}
 
-	_map->inited = giblorb_Inited_Magic;
-	_map->chunks = chunks;
-	_map->numchunks = numchunks;
-	_map->resources = nullptr;
-	_map->ressorted = nullptr;
-	_map->numresources = 0;
-
-	// Now we do everything else involved in loading the Blorb file,
-	// such as building resource lists.
-	err = initialize_map();
-	if (err)
-		return err;
-
-	return giblorb_err_None;
-}
-
-giblorb_err_t Blorb::initialize_map() {
-	// It is important that the map structure be kept valid during this function.
-	// If this returns an error, giblorb_destroy_map() will be called.
-	uint ix, jx;
-	giblorb_result_t chunkres;
-	giblorb_err_t err;
-	char *ptr;
-	glui32 len;
-	glui32 numres;
-	int gotindex = false;
-
-	for (ix = 0; ix < _map->numchunks; ix++) {
-		giblorb_chunkdesc_t *chu = &_map->chunks[ix];
-
-		switch (chu->type) {
-		case giblorb_ID_RIdx:
-			// Resource index chunk: build the resource list and sort it.
-
-			if (gotindex)
-				return giblorb_err_Format; // duplicate index chunk
-			err = load_chunk_by_number(giblorb_method_Memory, &chunkres, ix);
-			if (err)
-				return err;
-
-			ptr = (char *)chunkres.data.ptr;
-			len = chunkres.length;
-			numres = READ_BE_INT32(ptr + 0);
-
-			if (numres) {
-				uint ix2;
-				giblorb_resdesc_t *resources;
-				giblorb_resdesc_t **ressorted;
-
-				if (len != numres * 12 + 4)
-					return giblorb_err_Format; // bad length field
-
-				resources = new giblorb_resdesc_t[numres];
-				ressorted = new giblorb_resdesc_t *[numres];
-				if (!ressorted || !resources) {
-					delete[] resources;
-					delete[] ressorted;
-					return giblorb_err_Alloc;
-				}
-
-				ix2 = 0;
-				for (jx = 0; jx < numres; jx++) {
-					giblorb_resdesc_t *res = &(resources[jx]);
-					glui32 respos;
-
-					res->usage = READ_BE_INT32(ptr + jx * 12 + 4);
-					res->resnum = READ_BE_INT32(ptr + jx * 12 + 8);
-					respos = READ_BE_INT32(ptr + jx * 12 + 12);
-
-					while (ix2 < _map->numchunks
-						&& _map->chunks[ix2].startpos < respos)
-						ix2++;
-
-					if (ix2 >= _map->numchunks
-						|| _map->chunks[ix2].startpos != respos) {
-						delete[] resources;
-						delete[] ressorted;
-						return giblorb_err_Format; // start pos does not match a real chunk
-					}
-
-					res->chunknum = ix2;
-
-					ressorted[jx] = res;
-				}
-
-				// Sort a resource list (actually a list of pointers to structures in _map->resources.)
-				// This makes it easy to find resources by usage and resource number.
-				qsort(ressorted, numres);
-
-				_map->numresources = numres;
-				_map->resources = resources;
-				_map->ressorted = ressorted;
-			}
-
-			unload_chunk(ix);
-			gotindex = true;
-			break;
-		}
-	}
-
-	return giblorb_err_None;
+	return (int)_chunks.size();
 }
 
-void Blorb::qsort(giblorb_resdesc_t **list, size_t len) {
-	int ix, jx, res;
-	giblorb_resdesc_t *tmpptr, *pivot;
-
-	if (len < 6) {
-		// The list is short enough for a bubble-sort.
-		for (jx = len - 1; jx>0; jx--) {
-			for (ix = 0; ix<jx; ix++) {
-				res = sortsplot(list[ix], list[ix + 1]);
-				if (res > 0) {
-					tmpptr = list[ix];
-					list[ix] = list[ix + 1];
-					list[ix + 1] = tmpptr;
-				}
-			}
-		}
-	} else {
-		// Split the list.
-		pivot = list[len / 2];
-		ix = 0;
-		jx = len;
-		for (;;) {
-			while (ix < jx - 1 && sortsplot(list[ix], pivot) < 0)
-				ix++;
-			while (ix < jx - 1 && sortsplot(list[jx - 1], pivot) > 0)
-				jx--;
-			if (ix >= jx - 1)
-				break;
-			tmpptr = list[ix];
-			list[ix] = list[jx - 1];
-			list[jx - 1] = tmpptr;
-		}
-		ix++;
-		// Sort the halves.
-		qsort(list + 0, ix);
-		qsort(list + ix, len - ix);
-	}
-}
-
-giblorb_resdesc_t *Blorb::bsearch(giblorb_resdesc_t *sample,
-	giblorb_resdesc_t **list, int len) {
-	int top, bot, val, res;
-
-	bot = 0;
-	top = len;
-
-	while (bot < top) {
-		val = (top + bot) / 2;
-		res = sortsplot(list[val], sample);
-		if (res == 0)
-			return list[val];
-		if (res < 0) {
-			bot = val + 1;
-		} else {
-			top = val;
-		}
-	}
-
-	return nullptr;
-}
-
-int Blorb::sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2) {
-	if (v1->usage < v2->usage)
-		return -1;
-	if (v1->usage > v2->usage)
-		return 1;
-	if (v1->resnum < v2->resnum)
-		return -1;
-	if (v1->resnum > v2->resnum)
-		return 1;
-	return 0;
-}
-
-giblorb_err_t Blorb::load_chunk_by_type(glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count) {
-	uint ix;
-
-	for (ix = 0; ix < _map->numchunks; ix++) {
-		if (_map->chunks[ix].type == chunktype) {
-			if (count == 0)
-				break;
-			count--;
-		}
-	}
-
-	if (ix >= _map->numchunks) {
-		return giblorb_err_NotFound;
-	}
+const Common::ArchiveMemberPtr Blorb::getMember(const Common::String &name) const {
+	if (!hasFile(name))
+		return Common::ArchiveMemberPtr();
 
-	return load_chunk_by_number(method, res, ix);
+	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
 }
 
-giblorb_err_t Blorb::load_chunk_by_number(glui32 method, giblorb_result_t *res, glui32 chunknum) {
-	giblorb_chunkdesc_t *chu;
-
-	if (chunknum >= _map->numchunks)
-		return giblorb_err_NotFound;
-
-	chu = &(_map->chunks[chunknum]);
-
-	switch (method) {
-	case giblorb_method_DontLoad:
-		// do nothing
-		break;
-
-	case giblorb_method_FilePos:
-		res->data.startpos = chu->datpos;
-		break;
-
-	case giblorb_method_Memory:
-		if (!chu->ptr) {
-			glui32 readlen;
-			byte *dat = new byte[chu->len];
-
-			if (!dat)
-				return giblorb_err_Alloc;
-
-			_file.seek(chu->datpos);
+Common::SeekableReadStream *Blorb::createReadStreamForMember(const Common::String &name) const {
+	for (uint idx = 0; idx < _chunks.size(); ++idx) {
+		if (_chunks[idx]._filename.equalsIgnoreCase(name)) {
+			Common::File f;
+			if (!f.open(_filename))
+				error("Reading failed");
 
-			readlen = _file.read(dat, chu->len);
-			if (readlen != chu->len)
-				return giblorb_err_Read;
+			f.seek(_chunks[idx]._offset);
+			Common::SeekableReadStream *result = f.readStream(_chunks[idx]._size);
+			f.close();
 
-			chu->ptr = dat;
+			return result;
 		}
-
-		res->data.ptr = chu->ptr;
-		break;
 	}
 
-	res->chunknum = chunknum;
-	res->length = chu->len;
-	res->chunktype = chu->type;
-
-	return giblorb_err_None;
+	return nullptr;
 }
 
-giblorb_err_t Blorb::unload_chunk(glui32 chunknum) {
-	giblorb_chunkdesc_t *chu;
-
-	if (chunknum >= _map->numchunks)
-		return giblorb_err_NotFound;
-
-	chu = &(_map->chunks[chunknum]);
-
-	if (chu->ptr) {
-		delete chu->ptr;
-		chu->ptr = nullptr;
+Common::ErrorCode Blorb::load() {
+	// First, chew through the file and index the chunks
+	Common::File f;
+	if (!f.open(_filename) || f.size() < 12)
+		return Common::kReadingFailed;
+
+	if (f.readUint32BE() != ID_FORM)
+		return Common::kReadingFailed;
+	f.readUint32BE();
+	if (f.readUint32BE() != ID_IFRS)
+		return Common::kReadingFailed;
+	if (f.readUint32BE() != ID_RIdx)
+		return Common::kReadingFailed;
+
+	f.readUint32BE();
+	uint count = f.readUint32BE();
+
+	// First read in the resource index
+	for (uint idx = 0; idx < count; ++idx) {
+		ChunkEntry ce;
+		ce._type = f.readUint32BE();
+		ce._number = f.readUint32BE();
+		ce._offset = f.readUint32BE();
+
+		_chunks.push_back(ce);
 	}
 
-	return giblorb_err_None;
-}
-
-giblorb_err_t Blorb::load_resource(glui32 method, giblorb_result_t *res, glui32 usage, glui32 resnum) {
-	giblorb_resdesc_t sample;
-	giblorb_resdesc_t *found;
-
-	sample.usage = usage;
-	sample.resnum = resnum;
-
-	found = bsearch(&sample, _map->ressorted, _map->numresources);
-
-	if (!found)
-		return giblorb_err_NotFound;
-
-	return load_chunk_by_number(method, res, found->chunknum);
-}
-
-giblorb_err_t Blorb::count_resources(glui32 usage, glui32 *num, glui32 *min, glui32 *max) {
-	int ix;
-	int count;
-	glui32 val;
-	glui32 minval, maxval;
-
-	count = 0;
-	minval = 0;
-	maxval = 0;
-
-	for (ix = 0; ix<_map->numresources; ix++) {
-		if (_map->resources[ix].usage == usage) {
-			val = _map->resources[ix].resnum;
-			if (count == 0) {
-				count++;
-				minval = val;
-				maxval = val;
-			}
-			else {
-				count++;
-				if (val < minval)
-					minval = val;
-				if (val > maxval)
-					maxval = val;
+	// Further iterate through the resources
+	for (uint idx = 0; idx < _chunks.size(); ++idx) {
+		ChunkEntry &ce = _chunks[idx];
+		f.seek(ce._offset);
+		ce._offset += 8;
+
+		ce._id = f.readUint32BE();
+		ce._size = f.readUint32BE();
+
+		if (ce._type == ID_Pict) {
+			ce._filename = Common::String::format("pic%u", ce._number);
+			if (ce._id == ID_JPEG)
+				ce._filename += ".jpeg";
+			else if (ce._id == ID_PNG)
+				ce._filename += ".png";
+
+		} else if (ce._type == ID_Snd) {
+			ce._filename = Common::String::format("snd%u", ce._number);
+			if (ce._id == ID_MIDI)
+				ce._filename += ".midi";
+			else if (ce._id == ID_MP3)
+				ce._filename += ".mp3";
+			else if (ce._id == ID_WAVE)
+				ce._filename += ".wav";
+			else if (ce._id == ID_AIFF)
+				ce._filename += ".aiff";
+			else if (ce._id == ID_OGG)
+				ce._filename += ".ogg";
+			else if (ce._id == ID_MOD)
+				ce._filename += ".mod";
+
+		} else if (ce._type == ID_Data) {
+			ce._filename = Common::String::format("data%u", ce._number);
+
+		} else if (ce._type == ID_Exec) {
+			char buffer[5];
+			WRITE_BE_UINT32(buffer, ce._id);
+			buffer[4] = '\0';
+			Common::String type(buffer);
+
+			if (
+				(_interpType == INTERPRETER_FROTZ && type == "ZCOD") ||
+				(_interpType == INTERPRETER_TADS && (type == "TAD2" || type == "TAD3")) ||
+				(_interpType == INTERPRETER_HUGO && type == "HUGO")
+			) {
+				// Game executable
+				ce._filename = "game";
+			} else {
+				ce._filename = type;
 			}
 		}
 	}
 
-	if (num)
-		*num = count;
-	if (min)
-		*min = minval;
-	if (max)
-		*max = maxval;
-
-	return giblorb_err_None;
+	return Common::kNoError;
 }
 
 } // End of namespace Glk
diff --git a/engines/glk/blorb.h b/engines/glk/blorb.h
index 97fd520..afd52af 100644
--- a/engines/glk/blorb.h
+++ b/engines/glk/blorb.h
@@ -31,95 +31,30 @@
 namespace Glk {
 
 /**
- * Error type
+ * Describes one chunk of the Blorb file.
  */
-typedef glui32 giblorb_err_t;
-
-/**
- * Error codes
- */
-enum giblorbError {
-	giblorb_err_None        = 0,
-	giblorb_err_CompileTime = 1,
-	giblorb_err_Alloc       = 2,
-	giblorb_err_Read        = 3,
-	giblorb_err_NotAMap     = 4,
-	giblorb_err_Format      = 5,
-	giblorb_err_NotFound    = 6
-};
-
-/**
- * Methods for loading a chunk
- */
-enum giblorbMethod {
-	giblorb_method_DontLoad = 0,
-	giblorb_method_Memory   = 1,
-	giblorb_method_FilePos  = 2
-};
-
-enum {
-	giblorb_ID_Snd       = MKTAG('S', 'n', 'd', ' '),
-	giblorb_ID_Exec      = MKTAG('E', 'x', 'e', 'c'),
-	giblorb_ID_Pict      = MKTAG('P', 'i', 'c', 't'),
-	giblorb_ID_Copyright = MKTAG('(', 'c', ')', ' '),
-	giblorb_ID_AUTH      = MKTAG('A', 'U', 'T', 'H'),
-	giblorb_ID_ANNO      = MKTAG('A', 'N', 'N', 'O')
-};
-
-
-enum {
-	giblorb_ID_MOD  = MKTAG('M', 'O', 'D', ' '),
-	giblorb_ID_FORM = MKTAG('F', 'O', 'R', 'M'),
-	giblorb_ID_IFRS = MKTAG('I', 'F', 'R', 'S'),
-	giblorb_ID_RIdx = MKTAG('R', 'I', 'd', 'x'),
-	giblorb_ID_OGG  = MKTAG('O', 'G', 'G', 'V'),
-
-	// non-standard types
-	giblorb_ID_MIDI = MKTAG('M', 'I', 'D', 'I'),
-	giblorb_ID_MP3  = MKTAG('M', 'P', '3', ' '),
-	giblorb_ID_WAVE = MKTAG('W', 'A', 'V', 'E')
+struct ChunkEntry {
+	uint _type;
+	uint _number;
+	uint _id;
+    size_t _offset;
+	size_t _size;
+	Common::String _filename;
 };
 
 /**
- * Holds the complete description of an open Blorb file.
- * This type is opaque for normal interpreter use.
- */
-typedef struct giblorb_map_struct giblorb_map_t;
-
-/**
- * giblorb_result_t: Result when you try to load a chunk.
- */
-typedef struct giblorb_result_struct {
-	glui32 chunknum; // The chunk number (for use in giblorb_unload_chunk(), etc.)
-	union {
-		void *ptr;			///< A pointer to the data (if you used giblorb_method_Memory)
-		glui32 startpos;	///< The position in the file (if you used giblorb_method_FilePos)
-	} data;
-
-	glui32 length;			///< The length of the data
-	glui32 chunktype;		///< The type of the chunk.
-} giblorb_result_t;
-
-typedef struct giblorb_resdesc_struct giblorb_resdesc_t;
-
-/**
  * Blorb file manager
  */
 class Blorb : public Common::Archive {
 private:
-	Common::File _file;
+	Common::String _filename;
 	InterpreterType _interpType;
-	giblorb_map_t *_map;
+	Common::Array<ChunkEntry> _chunks;	///< list of chunk descriptors
 private:
 	/**
 	 * Parses the Blorb file index to load in a list of the chunks
 	 */
-	giblorb_err_t create_map();
-
-	giblorb_err_t initialize_map();
-	void qsort(giblorb_resdesc_t **list, size_t len);
-	giblorb_resdesc_t *bsearch(giblorb_resdesc_t *sample, giblorb_resdesc_t **list, int len);
-	int sortsplot(giblorb_resdesc_t *v1, giblorb_resdesc_t *v2);
+	Common::ErrorCode load();
 public:
 	/**
 	 * Constructor
@@ -127,11 +62,6 @@ public:
 	Blorb(const Common::String &filename, InterpreterType interpType);
 
 	/**
-	 * Destructor
-	 */
-	~Blorb();
-
-	/**
 	 * Check if a member with the given name is present in the Archive.
 	 * Patterns are not allowed, as this is meant to be a quick File::exists()
 	 * replacement.
@@ -139,14 +69,6 @@ public:
 	virtual bool hasFile(const Common::String &name) const override;
 
 	/**
-	 * Add all members of the Archive matching the specified pattern to list.
-	 * Must only append to list, and not remove elements from it.
-	 *
-	 * @return the number of members added to list
-	 */
-	virtual int listMatchingMembers(Common::ArchiveMemberList &list, const Common::String &pattern) const override;
-
-	/**
 	 * Add all members of the Archive to list.
 	 * Must only append to list, and not remove elements from it.
 	 *
@@ -165,18 +87,6 @@ public:
 	 * @return the newly created input stream
 	 */
 	virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override;
-public:
-	/**
-	 * Get a pointer to the Blorb's resource map
-	 */
-	giblorb_map_t *get_resource_map() const { return _map; }
-
-	giblorb_err_t load_chunk_by_type(glui32 method, giblorb_result_t *res, glui32 chunktype, glui32 count);
-	giblorb_err_t load_chunk_by_number(glui32 method, giblorb_result_t *res, glui32 chunknum);
-	giblorb_err_t unload_chunk(glui32 chunknum);
-
-	giblorb_err_t load_resource(glui32 method, giblorb_result_t *res, glui32 usage, glui32 resnum);
-	giblorb_err_t count_resources(glui32 usage, glui32 *num, glui32 *min, glui32 *max);
 };
 
 } // End of namespace Glk
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index c2a8264..87b736c 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -111,7 +111,7 @@ Common::Error GlkEngine::run() {
 		_blorb = new Blorb(filename, getInterpreterType());
 		SearchMan.add("blorb", _blorb, 99, false);
 
-		if (!f.open("EXEC", *_blorb))
+		if (!f.open("game", *_blorb))
 			return Common::kNoGameDataFoundError;
 	} else {
 		if (!f.open(filename))


Commit: 720ef67a7d124f1d83fdea68879461bca9429a9b
    https://github.com/scummvm/scummvm/commit/720ef67a7d124f1d83fdea68879461bca9429a9b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fixed detection of Infocom .dat gamefiles

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


diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index 29eb5d7..e1c60bd 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -44,7 +44,7 @@ PlainGameDescriptor FrotzMetaEngine::findGame(const char *gameId) {
 }
 
 bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
-	const char *const EXTENSIONS[9] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb" };
+	const char *const EXTENSIONS[10] = { ".dat", ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb" };
 
 	// Loop through the files of the folder
 	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -53,7 +53,7 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 			continue;
 		Common::String filename = file->getName();
 		bool hasExt = false;
-		for (int idx = 0; idx < 9 && !hasExt; ++idx)
+		for (int idx = 0; idx < 10 && !hasExt; ++idx)
 			hasExt = filename.hasSuffixIgnoreCase(EXTENSIONS[idx]);
 		if (!hasExt)
 			continue;
@@ -71,9 +71,12 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 		}
 		gameFile.close();
 
-		// Check for known game
+		// Check for known games. Note that there has been some variation in exact filesizes
+		// for Infocom games due to padding at the end of files. So we match on md5s for the
+		// first 5Kb, and only worry about filesize for more recent Blorb based Zcode games
 		const FrotzGameDescription *p = FROTZ_GAMES;
-		while (p->_gameId && p->_md5 && (md5 != p->_md5 || filesize != p->_filesize))
+		while (p->_gameId && p->_md5 && (md5 != p->_md5 ||
+				(filesize != p->_filesize && filename.hasSuffix(".zblorb"))))
 			++p;
 
 		DetectedGame gd;


Commit: 0b1e695f249d695333279eaf711b779c43b7b13d
    https://github.com/scummvm/scummvm/commit/0b1e695f249d695333279eaf711b779c43b7b13d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Beginnings of support for Infocom picture files

Changed paths:
  A engines/glk/frotz/pics.cpp
  A engines/glk/frotz/pics.h
    engines/glk/blorb.cpp
    engines/glk/conf.cpp
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/glk_interface.h
    engines/glk/glk_api.cpp
    engines/glk/glk_api.h
    engines/glk/module.mk


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index ace0e91..f8a8cbd 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -40,6 +40,7 @@ enum {
 
 	ID_JPEG = MKTAG('J', 'P', 'E', 'G'),
 	ID_PNG  = MKTAG('P', 'N', 'G', ' '),
+	ID_Rect = MKTAG('R', 'e', 'c', 't'),
 
 	ID_MIDI = MKTAG('M', 'I', 'D', 'I'),
 	ID_MP3 = MKTAG('M', 'P', '3', ' '),
@@ -138,9 +139,11 @@ Common::ErrorCode Blorb::load() {
 		if (ce._type == ID_Pict) {
 			ce._filename = Common::String::format("pic%u", ce._number);
 			if (ce._id == ID_JPEG)
-				ce._filename += ".jpeg";
+				ce._filename += ".jpg";
 			else if (ce._id == ID_PNG)
 				ce._filename += ".png";
+			else if (ce._id == ID_Rect)
+				ce._filename += ".rect";
 
 		} else if (ce._type == ID_Snd) {
 			ce._filename = Common::String::format("snd%u", ce._number);
diff --git a/engines/glk/conf.cpp b/engines/glk/conf.cpp
index 89b08fb..4775f26 100644
--- a/engines/glk/conf.cpp
+++ b/engines/glk/conf.cpp
@@ -155,6 +155,10 @@ Conf::Conf(InterpreterType interpType) {
 	get("stylehint", _styleHint, 1);
 	get("safeclicks", _safeClicks);
 
+	// For simplicity's sake, only allow graphics when in non-paletted graphics modes
+	if (g_system->getScreenFormat().bytesPerPixel == 1)
+		_graphics = false;
+
 	Common::copy(T_STYLES, T_STYLES + style_NUMSTYLES, _tStyles);
 	Common::copy(G_STYLES, G_STYLES + style_NUMSTYLES, _gStyles);
 
diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index a8b25a2..2790e30 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -21,13 +21,16 @@
  */
 
 #include "glk/frotz/glk_interface.h"
+#include "glk/frotz/pics.h"
+#include "glk/conf.h"
+#include "glk/screen.h"
 
 namespace Glk {
 namespace Frotz {
 
 GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc) :
 		GlkAPI(syst, gameDesc),
-		oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
+		_pics(nullptr), oldstyle(0), curstyle(0), cury(1), curx(1), fixforced(0),
 		curr_fg(-2), curr_bg(-2), curr_font(1), prev_font(1), temp_font(0),
 		curr_status_ht(0), mach_status_ht(0), gos_status(nullptr), gos_upper(nullptr),
 		gos_lower(nullptr), gos_curwin(nullptr), gos_linepending(0), gos_linebuf(nullptr),
@@ -38,6 +41,10 @@ GlkInterface::GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc) :
 	Common::fill(&statusline[0], &statusline[256], '\0');
 }
 
+GlkInterface::~GlkInterface() {
+	delete _pics;
+}
+
 void GlkInterface::initialize() {
 	uint width, height;
 
@@ -152,8 +159,13 @@ void GlkInterface::initialize() {
 	h_font_height = 1;
 
 	// Must be after screen dimensions are computed
-	if (h_version == V6) {
-		h_flags &= ~GRAPHICS_FLAG;
+	if (g_conf->_graphics) {
+		if (_blorb)
+			// Blorb file containers allow graphics
+			h_flags |= GRAPHICS_FLAG;
+		else if ((h_version == V6 || _storyId == BEYOND_ZORK) && initPictures())
+			// Earlier Infocom game with picture files
+			h_flags |= GRAPHICS_FLAG;
 	}
 
 	// Use the ms-dos interpreter number for v6, because that's the
@@ -170,6 +182,18 @@ void GlkInterface::initialize() {
 	}
 }
 
+bool GlkInterface::initPictures() {
+	if (Pics::exists()) {
+		_pics = new Pics();
+		SearchMan.add("Pics", _pics, 99, false);
+		return true;
+	}
+
+	if (h_version == V6)
+		warning("Could not locate MG1 file");
+	return false;
+}
+
 int GlkInterface::os_char_width(zchar z) {
 	return 1;
 }
@@ -234,6 +258,18 @@ void GlkInterface::os_stop_sample(int a) {
 void GlkInterface::os_beep(int volume) {
 }
 
+bool GlkInterface::os_picture_data(int picture, glui32 *height, glui32 *width) {
+	if (_pics && picture == 0) {
+		*width = _pics->version();
+		*height = _pics->size();
+		return true;
+	} else {
+		return glk_image_get_info(picture, width, height);
+	}
+}
+
+
+
 void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) {
 	// TODO
 }
@@ -405,6 +441,24 @@ void GlkInterface::gos_cancel_pending_line() {
 	gos_linepending = 0;
 }
 
+void GlkInterface::os_restart_game(RestartAction stage) {
+	// Show Beyond Zork's title screen
+	if ((stage == RESTART_END) && (_storyId == BEYOND_ZORK)) {
+/*
+		uint w, h;
+		if (os_picture_data(1, &h, &w)) {
+			_screen->clear();
+			os_draw_picture(1, Common::Point(1, 1));
+			_events->waitForPress();
+		}
+		*/
+	}
+}
+
+void GlkInterface::os_draw_picture(int picture, winid_t win, const Common::Point &pos) {
+	glk_image_draw(win, picture, pos.x - 1, pos.y - 1);
+}
+
 zchar GlkInterface::os_read_key(int timeout, bool show_cursor) {
 	event_t ev;
 	winid_t win = gos_curwin ? gos_curwin : gos_lower;
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index 5e2fa62..0dc8846 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -42,6 +42,7 @@ enum RestartAction {
 	RESTART_END = 2
 };
 
+class Pics;
 
 /**
  * Implements an intermediate interface on top of the GLK layer, providing screen
@@ -49,6 +50,7 @@ enum RestartAction {
  */
 class GlkInterface : public GlkAPI, public virtual UserOptions, public virtual Mem {
 public:
+	Pics *_pics;
 	zchar statusline[256];
 	int oldstyle;
 	int curstyle;
@@ -93,6 +95,11 @@ public:
 
 	bool _soundLocked;
 	bool _soundPlaying;
+private:
+	/**
+	 * Loads the pictures file for Infocom V6 games
+	 */
+	bool initPictures();
 protected:
 	/**
 	 * Return the length of the character in screen units.
@@ -133,10 +140,30 @@ protected:
 	 */
 	void os_start_sample(int number, int volume, int repeats, zword eos);
 
+	/**
+	 * Stop playing a given sound number
+	 */
 	void os_stop_sample(int a);
+
+	/**
+	 * Make a beep sound
+	 */
 	void os_beep(int volume);
 
 	/**
+	 * Return true if the given picture is available. If so, write the
+	 * width and height of the picture into the appropriate variables.
+	 * Only when picture 0 is asked for, write the number of available
+	 * pictures and the release number instead.
+	 */
+	bool os_picture_data(int picture, glui32 *height, glui32 *width);
+
+	/**
+	 * Display a picture at the given coordinates. Top left is (1,1).
+	 */
+	void os_draw_picture(int picture, winid_t win, const Common::Point &pos);
+
+	/**
 	 * Call the IO interface to play a sample.
 	 */
 	void start_sample(int number, int volume, int repeats, zword eos);
@@ -165,7 +192,7 @@ protected:
 	/**
 	 * Called during game restarts
 	 */
-	void os_restart_game(RestartAction) {}
+	void os_restart_game(RestartAction stage);
 
 	/**
 	 * Reads the mouse buttons
@@ -197,7 +224,11 @@ public:
 	 * Constructor
 	 */
 	GlkInterface(OSystem *syst, const GlkGameDescription &gameDesc);
-	virtual ~GlkInterface() {}
+
+	/**
+	 * Destructor
+	 */
+	virtual ~GlkInterface();
 
 	/**
 	 * Initialization
diff --git a/engines/glk/frotz/pics.cpp b/engines/glk/frotz/pics.cpp
new file mode 100644
index 0000000..29d2ec5
--- /dev/null
+++ b/engines/glk/frotz/pics.cpp
@@ -0,0 +1,116 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY{} without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program{} if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/frotz/pics.h"
+#include "glk/glk.h"
+
+namespace Glk {
+namespace Frotz {
+
+enum {
+	PIC_FILE_HEADER_FLAGS      = 1,
+	PIC_FILE_HEADER_NUM_IMAGES = 4,
+	PIC_FILE_HEADER_ENTRY_SIZE = 8,
+	PIC_FILE_HEADER_VERSION    = 14
+};
+
+Pics::Pics() : Common::Archive(), _filename(getFilename()) {
+	Common::File f;
+	if (!f.open(_filename))
+		error("Error reading Pics file");
+
+	byte buffer[16];
+	f.read(buffer, 16);
+	_index.resize(READ_LE_UINT16(&buffer[PIC_FILE_HEADER_NUM_IMAGES]));
+	_entrySize = buffer[PIC_FILE_HEADER_ENTRY_SIZE];
+	_version = buffer[PIC_FILE_HEADER_FLAGS];
+
+	// Iterate through loading the index
+	for (uint idx = 0; idx < _index.size(); ++idx) {
+		Entry &e = _index[idx];
+		e._number = f.readUint16LE();
+		e._offset = f.pos();
+		e._size = _entrySize - 2;
+		f.skip(_entrySize - 2);
+
+		e._filename = Common::String::format("PIC%u", e._number);
+	}
+
+	f.close();
+}
+
+Common::String Pics::getFilename() {
+	Common::String filename = g_vm->getFilename();
+	while (filename.contains('.'))
+		filename.deleteLastChar();
+
+	return filename + ".mg1";
+}
+
+bool Pics::exists() {
+	return Common::File::exists(getFilename());
+}
+
+bool Pics::hasFile(const Common::String &name) const {
+	for (uint idx = 0; idx < _index.size(); ++idx) {
+		if (_index[idx]._filename.equalsIgnoreCase(name))
+			return true;
+	}
+
+	return false;
+}
+
+int Pics::listMembers(Common::ArchiveMemberList &list) const {
+	for (uint idx = 0; idx < _index.size(); ++idx) {
+		list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(_index[idx]._filename, this)));
+	}
+
+	return (int)_index.size();
+}
+
+const Common::ArchiveMemberPtr Pics::getMember(const Common::String &name) const {
+	if (!hasFile(name))
+		return Common::ArchiveMemberPtr();
+
+	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::String &name) const {
+	for (uint idx = 0; idx < _index.size(); ++idx) {
+		if (_index[idx]._filename.equalsIgnoreCase(name)) {
+			Common::File f;
+			if (!f.open(_filename))
+				error("Reading failed");
+
+			f.seek(_index[idx]._offset);
+			Common::SeekableReadStream *result = f.readStream(_index[idx]._size);
+			f.close();
+
+			return result;
+		}
+	}
+
+	return nullptr;
+}
+
+} // End of namespace Frotz
+} // End of namespace Glk
diff --git a/engines/glk/frotz/pics.h b/engines/glk/frotz/pics.h
new file mode 100644
index 0000000..55e3c0d
--- /dev/null
+++ b/engines/glk/frotz/pics.h
@@ -0,0 +1,107 @@
+/* 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_FROTZ_PICS
+#define GLK_FROTZ_PICS
+
+#include "common/archive.h"
+#include "common/array.h"
+
+namespace Glk {
+namespace Frotz {
+
+/**
+ * Infocom graphics file manager
+ */
+class Pics : public Common::Archive {
+	/**
+	 * Describes one chunk of the Blorb file.
+	 */
+	struct Entry {
+		uint _number;
+		size_t _offset;
+		size_t _size;
+		Common::String _filename;
+	};
+private:
+	Common::String _filename;
+	Common::Array<Entry> _index;	///< list of entries
+	uint _entrySize;
+	uint _version;
+private:
+	/**
+	 * Returns the filename for the pictures archive
+	 */
+	static Common::String getFilename();
+public:
+	/**
+	 * Returns true if an mg1 file exists for the game
+	 */
+	static bool exists();
+public:
+	/**
+	 * Constructor
+	 */
+	Pics();
+
+	/**
+	 * Return the number of entries in the file
+	 */
+	size_t size() const { return _index.size(); }
+
+	/**
+	 * Return the version of the file
+	 */
+	uint version() const { return _version; }
+
+	/**
+	 * Check if a member with the given name is present in the Archive.
+	 * Patterns are not allowed, as this is meant to be a quick File::exists()
+	 * replacement.
+	 */
+	virtual bool hasFile(const Common::String &name) const override;
+
+	/**
+	 * Add all members of the Archive to list.
+	 * Must only append to list, and not remove elements from it.
+	 *
+	 * @return the number of names added to list
+	 */
+	virtual int listMembers(Common::ArchiveMemberList &list) const override;
+
+	/**
+	 * Returns a ArchiveMember representation of the given file.
+	 */
+	virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const override;
+
+	/**
+	 * Create a stream bound to a member with the specified name in the
+	 * archive. If no member with this name exists, 0 is returned.
+	 * @return the newly created input stream
+	 */
+	virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override;
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index ec98ab6..981c41e 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -920,7 +920,7 @@ glui32 GlkAPI::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, gls
 	return false;
 }
 
-glui32 GlkAPI::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
+bool GlkAPI::glk_image_get_info(glui32 image, glui32 *width, glui32 *height) {
 	if (!g_conf->_graphics)
 		return false;
 
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
index b625f69..5c19e09 100644
--- a/engines/glk/glk_api.h
+++ b/engines/glk/glk_api.h
@@ -193,7 +193,7 @@ public:
 	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
 	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
 	                             glsi32 val1, glsi32 val2, glui32 width, glui32 height);
-	glui32 glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
+	bool glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
 
 	void glk_window_flow_break(winid_t win);
 
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index eedab57..d1d9100 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -26,6 +26,7 @@ MODULE_OBJS := \
 	frotz/frotz.o \
 	frotz/glk_interface.o \
 	frotz/mem.o \
+	frotz/pics.o \
 	frotz/processor.o \
 	frotz/processor_buffer.o \
 	frotz/processor_input.o \


Commit: 3e8ed4eafcfe3fd8198c65b0838022c1dd32eea8
    https://github.com/scummvm/scummvm/commit/3e8ed4eafcfe3fd8198c65b0838022c1dd32eea8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GRAPHICS: Add convertToInPlace method to ManagedSurface

Changed paths:
    graphics/managed_surface.h


diff --git a/graphics/managed_surface.h b/graphics/managed_surface.h
index 422b461..a7cf70f8 100644
--- a/graphics/managed_surface.h
+++ b/graphics/managed_surface.h
@@ -368,9 +368,22 @@ public:
 		addDirtyRect(area);
 		return _innerSurface.getSubArea(area);
 	}
+
+	/**
+	 * Convert the data to another pixel format.
+	 *
+	 * This works in-place. This means it will not create an additional buffer
+	 * for the conversion process. The value of 'pixels' might change though
+	 * (that means it might realloc the pixel data).
+	 *
+	 * @param dstFormat The desired format
+	 * @param palette   The palette (in RGB888), if the source format has a Bpp of 1
+	 */
+	void convertToInPlace(const PixelFormat &dstFormat, const byte *palette = 0) {
+		_innerSurface.convertToInPlace(dstFormat, palette);
+	}
 };
 
 } // End of namespace Graphics
 
-
 #endif


Commit: 6f508124937df1b0cda5a2732039a82c4ec16b85
    https://github.com/scummvm/scummvm/commit/6f508124937df1b0cda5a2732039a82c4ec16b85
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Added RawDecoder class for new raw picture data format

Changed paths:
  A engines/glk/raw_decoder.cpp
  A engines/glk/raw_decoder.h
    engines/glk/glk_api.cpp
    engines/glk/glk_api.h
    engines/glk/module.mk
    engines/glk/picture.cpp


diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index 981c41e..2a5fd11 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -887,7 +887,7 @@ glui32 GlkAPI::glk_buffer_canon_normalize_uni(glui32 *buf, glui32 len, glui32 nu
 	return 0;
 }
 
-glui32 GlkAPI::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
+bool GlkAPI::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
 	if (!win) {
 		warning("image_draw: invalid ref");
 	} else if (g_conf->_graphics) {
@@ -903,7 +903,7 @@ glui32 GlkAPI::glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val
 	return false;
 }
 
-glui32 GlkAPI::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2,
+bool GlkAPI::glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2,
                                   glui32 width, glui32 height) {
 	if (!win) {
 		warning("image_draw_scaled: invalid ref");
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
index 5c19e09..fe25930 100644
--- a/engines/glk/glk_api.h
+++ b/engines/glk/glk_api.h
@@ -190,8 +190,8 @@ public:
 
 #ifdef GLK_MODULE_IMAGE
 
-	glui32 glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
-	glui32 glk_image_draw_scaled(winid_t win, glui32 image,
+	bool glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2);
+	bool glk_image_draw_scaled(winid_t win, glui32 image,
 	                             glsi32 val1, glsi32 val2, glui32 width, glui32 height);
 	bool glk_image_get_info(glui32 image, glui32 *width, glui32 *height);
 
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index d1d9100..a4344df 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -9,6 +9,7 @@ MODULE_OBJS := \
 	glk.o \
 	glk_api.o \
 	picture.o \
+	raw_decoder.o \
 	screen.o \
 	selection.o \
 	streams.o \
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 122d9c3..7770bb8 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -22,6 +22,7 @@
 
 #include "glk/picture.h"
 #include "glk/glk.h"
+#include "glk/raw_decoder.h"
 #include "glk/screen.h"
 #include "common/file.h"
 #include "image/jpeg.h"
@@ -102,7 +103,9 @@ Picture *Pictures::retrieve(uint id, bool scaled) {
 Picture *Pictures::load(uint32 id) {
 	::Image::PNGDecoder png;
 	::Image::JPEGDecoder jpg;
+	RawDecoder raw;
 	const Graphics::Surface *img;
+	const byte *palette = nullptr;
 	Picture *pic;
 
 	// Check if the picture is already in the store
@@ -114,15 +117,25 @@ Picture *Pictures::load(uint32 id) {
 	if (f.open(Common::String::format("PIC%lu.png", id))) {
 		png.loadStream(f);
 		img = png.getSurface();
+		palette = png.getPalette();
 	} else if (f.open(Common::String::format("PIC%lu.jpg", id))) {
 		jpg.loadStream(f);
 		img = jpg.getSurface();
+	} else if (f.open(Common::String::format("PIC%lu.raw", id))) {
+		raw.loadStream(f);
+		img = raw.getSurface();
+		palette = raw.getPalette();
 	}
 
 	pic = new Picture();
 	pic->_refCount = 1;
     pic->_id = id;
     pic->_scaled = false;
+	pic->create(img->w, img->h, g_system->getScreenFormat());
+	pic->blitFrom(*img);
+
+	if (palette)
+		pic->convertToInPlace(g_system->getScreenFormat(), palette);
 
     store(pic);
     return pic;
diff --git a/engines/glk/raw_decoder.cpp b/engines/glk/raw_decoder.cpp
new file mode 100644
index 0000000..a5cd593
--- /dev/null
+++ b/engines/glk/raw_decoder.cpp
@@ -0,0 +1,61 @@
+/* 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/raw_decoder.h"
+#include "common/stream.h"
+
+namespace Glk {
+
+RawDecoder::RawDecoder() : Image::ImageDecoder(), _palette(nullptr), _paletteColorCount(0) {
+}
+
+RawDecoder::~RawDecoder() {
+	destroy();
+}
+
+void RawDecoder::destroy() {
+	_surface.free();
+	delete[] _palette;
+	_palette = nullptr;
+}
+
+bool RawDecoder::loadStream(Common::SeekableReadStream &stream) {
+	// Reset member variables from previous decodings
+	destroy();
+
+	uint width = stream.readUint16LE();
+	uint height = stream.readUint16LE();
+	_paletteColorCount = stream.readByte();
+	assert(_paletteColorCount > 0);
+
+	// Read in the palette
+	_palette = new byte[_paletteColorCount * 3];
+	stream.read(_palette, _paletteColorCount * 3);
+
+	// Set up the surface and read it in
+	_surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+	stream.read(_surface.getPixels(), width * height);
+
+	return true;
+}
+
+} // End of namespace Glk
diff --git a/engines/glk/raw_decoder.h b/engines/glk/raw_decoder.h
new file mode 100644
index 0000000..96f25c2
--- /dev/null
+++ b/engines/glk/raw_decoder.h
@@ -0,0 +1,61 @@
+/* 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_RAW_DECODER_H
+#define GLK_RAW_DECODER_H
+
+#include "graphics/surface.h"
+#include "image/image_decoder.h"
+
+namespace Glk {
+
+/**
+ * This image decoder class implements loading of a simplified image format.
+ * It's used for sub-engines like Frotz with custom picture formats. They can
+ * expose their picture archives using Common::Archive, and have the individual
+ * picture files stored in the format for this decoder to load
+ * Format:
+ * width		2 bytes
+ * height		2 bytes
+ * pal size		1 byte
+ * palette		3 bytes * pal size
+ * pixels		width * height pixels
+ */
+class RawDecoder : public Image::ImageDecoder {
+private:
+	Graphics::Surface _surface;
+	byte *_palette;
+	uint16 _paletteColorCount;
+public:
+	RawDecoder();
+	~RawDecoder();
+
+	virtual bool loadStream(Common::SeekableReadStream &stream) override;
+	virtual void destroy() override;
+	virtual const Graphics::Surface *getSurface() const override { return &_surface; }
+	virtual const byte *getPalette() const override { return _palette; }
+	virtual uint16 getPaletteColorCount() const override { return _paletteColorCount; }
+};
+
+} // End of namespace Glk
+
+#endif


Commit: a334cd704e21044e0660f918e1c9e3a5040c5837
    https://github.com/scummvm/scummvm/commit/a334cd704e21044e0660f918e1c9e3a5040c5837
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Further loading of Infocom pictures files

Changed paths:
  A engines/glk/frotz/pics_decoder.cpp
  A engines/glk/frotz/pics_decoder.h
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/glk_interface.h
    engines/glk/frotz/pics.cpp
    engines/glk/frotz/pics.h
    engines/glk/module.mk


diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index 2790e30..4e7a503 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -96,11 +96,17 @@ void GlkInterface::initialize() {
 	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Weight, 1);
 	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Oblique, 1);
 
+	/*
+	 * Open game windows
+	 */
+	if (_storyId == BEYOND_ZORK)
+		showBeyondZorkTitle();
+
 	gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
 	if (!gos_lower)
 		gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
 	glk_window_get_size(gos_lower, &width, &height);
-	glk_window_close(gos_lower, NULL);
+	glk_window_close(gos_lower, nullptr);
 
 	gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
 	gos_upper = glk_window_open(gos_lower,
@@ -108,7 +114,7 @@ void GlkInterface::initialize() {
 			0,
 			wintype_TextGrid, 0);
 
-	gos_channel = NULL;
+	gos_channel = nullptr;
 
 	glk_set_window(gos_lower);
 	gos_curwin = gos_lower;
@@ -441,18 +447,17 @@ void GlkInterface::gos_cancel_pending_line() {
 	gos_linepending = 0;
 }
 
-void GlkInterface::os_restart_game(RestartAction stage) {
-	// Show Beyond Zork's title screen
-	if ((stage == RESTART_END) && (_storyId == BEYOND_ZORK)) {
-/*
-		uint w, h;
-		if (os_picture_data(1, &h, &w)) {
-			_screen->clear();
-			os_draw_picture(1, Common::Point(1, 1));
-			_events->waitForPress();
-		}
-		*/
+void GlkInterface::showBeyondZorkTitle() {
+	uint winW, winH, imgW, imgH;
+	winid_t win = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
+	glk_window_get_size(gos_lower, &winW, &winH);
+
+	if (os_picture_data(1, &imgW, &imgH)) {
+		os_draw_picture(1, win, Common::Point(1, 1));
+		_events->waitForPress();
 	}
+
+	glk_window_close(win, nullptr);
 }
 
 void GlkInterface::os_draw_picture(int picture, winid_t win, const Common::Point &pos) {
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index 0dc8846..b279ec8 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -100,6 +100,11 @@ private:
 	 * Loads the pictures file for Infocom V6 games
 	 */
 	bool initPictures();
+
+	/**
+	 * Displays the title screen for the game Beyond Zork
+	 */
+	void showBeyondZorkTitle();
 protected:
 	/**
 	 * Return the length of the character in screen units.
@@ -192,7 +197,7 @@ protected:
 	/**
 	 * Called during game restarts
 	 */
-	void os_restart_game(RestartAction stage);
+	void os_restart_game(RestartAction stage) {}
 
 	/**
 	 * Reads the mouse buttons
diff --git a/engines/glk/frotz/pics.cpp b/engines/glk/frotz/pics.cpp
index 29d2ec5..976c2b2 100644
--- a/engines/glk/frotz/pics.cpp
+++ b/engines/glk/frotz/pics.cpp
@@ -21,7 +21,9 @@
  */
 
 #include "glk/frotz/pics.h"
+#include "glk/frotz/pics_decoder.h"
 #include "glk/glk.h"
+#include "common/algorithm.h"
 
 namespace Glk {
 namespace Frotz {
@@ -38,21 +40,53 @@ Pics::Pics() : Common::Archive(), _filename(getFilename()) {
 	if (!f.open(_filename))
 		error("Error reading Pics file");
 
+	Common::Array<uint> offsets;
 	byte buffer[16];
 	f.read(buffer, 16);
 	_index.resize(READ_LE_UINT16(&buffer[PIC_FILE_HEADER_NUM_IMAGES]));
 	_entrySize = buffer[PIC_FILE_HEADER_ENTRY_SIZE];
 	_version = buffer[PIC_FILE_HEADER_FLAGS];
+	assert(_entrySize >= 6 && _entrySize <= 14);
 
 	// Iterate through loading the index
 	for (uint idx = 0; idx < _index.size(); ++idx) {
 		Entry &e = _index[idx];
-		e._number = f.readUint16LE();
-		e._offset = f.pos();
-		e._size = _entrySize - 2;
-		f.skip(_entrySize - 2);
+		f.read(buffer, _entrySize);
+
+		e._number = READ_LE_UINT16(buffer);
+		e._width = READ_LE_UINT16(buffer + 2);
+		e._height = READ_LE_UINT16(buffer + 4);
+
+		if (_entrySize >= 11) {
+			e._dataOffset = READ_BE_UINT32(buffer + 7) & 0xffffff;
+			if (e._dataOffset)
+				offsets.push_back(e._dataOffset);
+
+			if (_entrySize == 14) {
+				e._paletteOffset = READ_BE_UINT32(buffer + 10) & 0xffffff;
+				e._paletteSize = e._dataOffset - e._paletteOffset;
+				assert((e._paletteSize % 3) == 0);
+			}
+		}
 
-		e._filename = Common::String::format("PIC%u", e._number);
+		e._filename = Common::String::format("pic%u.raw", e._number);
+	}
+
+	// Further processing of index to calculate data sizes
+	Common::sort(offsets.begin(), offsets.end());
+
+	for (uint idx = 0; idx < _index.size(); ++idx) {
+		Entry &e = _index[idx];
+		if (!e._dataOffset)
+			continue;
+
+		// Find the entry in the offsets array
+		uint oidx = 0;
+		while (oidx < offsets.size() && offsets[oidx] != e._dataOffset)
+			++oidx;
+
+		// Set the size
+		e._dataSize = (oidx == (offsets.size() - 1) ? f.size() : offsets[oidx + 1]) - e._dataOffset;
 	}
 
 	f.close();
@@ -96,16 +130,27 @@ const Common::ArchiveMemberPtr Pics::getMember(const Common::String &name) const
 
 Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::String &name) const {
 	for (uint idx = 0; idx < _index.size(); ++idx) {
-		if (_index[idx]._filename.equalsIgnoreCase(name)) {
+		const Entry &e = _index[idx];
+		if (e._filename.equalsIgnoreCase(name)) {
 			Common::File f;
 			if (!f.open(_filename))
 				error("Reading failed");
 
-			f.seek(_index[idx]._offset);
-			Common::SeekableReadStream *result = f.readStream(_index[idx]._size);
-			f.close();
-
-			return result;
+			// Read in the image's palette
+			assert(e._paletteSize);
+			Common::Array<byte> palette;
+			palette.resize(e._paletteSize);
+			f.seek(e._paletteOffset);
+			f.read(&palette[0], e._paletteSize);
+
+			if (e._dataSize) {
+				Common::SeekableReadStream *src = f.readStream(e._dataSize);
+				f.close();
+				return PictureDecoder::decode(*src, &palette[0]);
+
+			} else {
+				error("TODO: Empty rect renderings");
+			}
 		}
 	}
 
diff --git a/engines/glk/frotz/pics.h b/engines/glk/frotz/pics.h
index 55e3c0d..b05acc5 100644
--- a/engines/glk/frotz/pics.h
+++ b/engines/glk/frotz/pics.h
@@ -20,8 +20,8 @@
  *
  */
 
-#ifndef GLK_FROTZ_PICS
-#define GLK_FROTZ_PICS
+#ifndef GLK_FROTZ_PICS_H
+#define GLK_FROTZ_PICS_H
 
 #include "common/archive.h"
 #include "common/array.h"
@@ -38,9 +38,15 @@ class Pics : public Common::Archive {
 	 */
 	struct Entry {
 		uint _number;
-		size_t _offset;
-		size_t _size;
+		size_t _width, _height;
+		size_t _dataOffset;
+		size_t _dataSize;
+		size_t _paletteOffset;
+		size_t _paletteSize;
 		Common::String _filename;
+
+		Entry() : _number(0), _width(0), _height(0), _dataOffset(0), _dataSize(0),
+			_paletteOffset(0), _paletteSize(0) {}
 	};
 private:
 	Common::String _filename;
diff --git a/engines/glk/frotz/pics_decoder.cpp b/engines/glk/frotz/pics_decoder.cpp
new file mode 100644
index 0000000..a67ed12
--- /dev/null
+++ b/engines/glk/frotz/pics_decoder.cpp
@@ -0,0 +1,34 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY{} without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program{} if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/frotz/pics_decoder.h"
+
+namespace Glk {
+namespace Frotz {
+
+Common::MemoryReadStream *PictureDecoder::decode(Common::ReadStream &src, const byte *palette) {
+	// TODO
+	return nullptr;
+}
+
+} // End of namespace Frotz
+} // End of namespace Glk
diff --git a/engines/glk/frotz/pics_decoder.h b/engines/glk/frotz/pics_decoder.h
new file mode 100644
index 0000000..41484a1
--- /dev/null
+++ b/engines/glk/frotz/pics_decoder.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_FROTZ_PICS_DECODER_H
+#define GLK_FROTZ_PICS_DECODER_H
+
+#include "common/memstream.h"
+#include "common/stream.h"
+
+namespace Glk {
+namespace Frotz {
+
+/**
+ * Decodes an Infocom encoded picture into a raw pixel stream that the outer
+ * Glk engine is capable of then loading into a picture object
+ */
+class PictureDecoder {
+public:
+	/**
+	 * Decode method
+	 */
+	static Common::MemoryReadStream *decode(Common::ReadStream &src, const byte *palette);
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index a4344df..0206643 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -28,6 +28,7 @@ MODULE_OBJS := \
 	frotz/glk_interface.o \
 	frotz/mem.o \
 	frotz/pics.o \
+	frotz/pics_decoder.o \
 	frotz/processor.o \
 	frotz/processor_buffer.o \
 	frotz/processor_input.o \


Commit: f92b82664dd099ac4d4439f614188a6544bfff2c
    https://github.com/scummvm/scummvm/commit/f92b82664dd099ac4d4439f614188a6544bfff2c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Fix gcc warnings

Changed paths:
    engines/glk/blorb.cpp
    engines/glk/picture.cpp
    engines/glk/picture.h


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index f8a8cbd..5c1f0bd 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -47,7 +47,7 @@ enum {
 	ID_WAVE = MKTAG('W', 'A', 'V', 'E'),
 	ID_AIFF = MKTAG('A', 'I', 'F', 'F'),
 	ID_OGG = MKTAG('O', 'G', 'G', ' '),
-	ID_MOD = MKTAG('M', 'O', 'D', ' '),
+	ID_MOD = MKTAG('M', 'O', 'D', ' ')
 };
 
 /*--------------------------------------------------------------------------*/
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 7770bb8..6d8fce9 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -109,22 +109,25 @@ Picture *Pictures::load(uint32 id) {
 	Picture *pic;
 
 	// Check if the picture is already in the store
-    pic = retrieve(id, false);
-    if (pic)
-        return pic;
+	pic = retrieve(id, false);
+	if (pic)
+		return pic;
 
 	Common::File f;
-	if (f.open(Common::String::format("PIC%lu.png", id))) {
+	if (f.open(Common::String::format("PIC%u.png", id))) {
 		png.loadStream(f);
 		img = png.getSurface();
 		palette = png.getPalette();
-	} else if (f.open(Common::String::format("PIC%lu.jpg", id))) {
+	} else if (f.open(Common::String::format("PIC%u.jpg", id))) {
 		jpg.loadStream(f);
 		img = jpg.getSurface();
-	} else if (f.open(Common::String::format("PIC%lu.raw", id))) {
+	} else if (f.open(Common::String::format("PIC%u.raw", id))) {
 		raw.loadStream(f);
 		img = raw.getSurface();
 		palette = raw.getPalette();
+	} else {
+		// No such picture
+		return nullptr;
 	}
 
 	pic = new Picture();
diff --git a/engines/glk/picture.h b/engines/glk/picture.h
index f221569..06d598b 100644
--- a/engines/glk/picture.h
+++ b/engines/glk/picture.h
@@ -45,8 +45,8 @@ public:
 	/**
 	 * Constructor
 	 */
-	Picture(int width, int height, const Graphics::PixelFormat &format) :
-		Graphics::ManagedSurface(width, height, format), _refCount(0), _id(0), _scaled(false) {}
+	Picture(int width, int height, const Graphics::PixelFormat &fmt) :
+		Graphics::ManagedSurface(width, height, fmt), _refCount(0), _id(0), _scaled(false) {}
 
 	/**
 	 * Increment reference counter


Commit: 0aff016ad9633ee38b74a91d73743267456977c9
    https://github.com/scummvm/scummvm/commit/0aff016ad9633ee38b74a91d73743267456977c9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix deletion of undo_mem

Changed paths:
    engines/glk/frotz/mem.cpp


diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index bec30b0..656b822 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -231,7 +231,7 @@ void Mem::reset_memory() {
 
 	if (undo_mem) {
 		free_undo(undo_count);
-		delete undo_mem;
+		delete[] undo_mem;
 	}
 
 	undo_mem = nullptr;


Commit: 5b9e1a53ad4c2baca32dbb3fce22230cc7e8bc40
    https://github.com/scummvm/scummvm/commit/5b9e1a53ad4c2baca32dbb3fce22230cc7e8bc40
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix mismatch allocation/free

Changed paths:
    engines/glk/frotz/mem.cpp


diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index 656b822..19d2c3d 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -88,7 +88,7 @@ void Mem::initializeUndo() {
 
 void Mem::loadGameHeader() {
 	// Load header
-	zmp = new byte[64];
+	zmp = (byte *)malloc(64);
 	story_fp->seek(0);
 	story_fp->read(zmp, 64);
 
@@ -236,7 +236,7 @@ void Mem::reset_memory() {
 
 	undo_mem = nullptr;
 	undo_count = 0;
-	delete[] zmp;
+	free(zmp);
 	zmp = nullptr;
 }
 


Commit: c7a632ae2ed6a11500bc77b53540bb7ea23cb0d1
    https://github.com/scummvm/scummvm/commit/c7a632ae2ed6a11500bc77b53540bb7ea23cb0d1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix initialization of undo data

Changed paths:
    engines/glk/frotz/mem.cpp


diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index 19d2c3d..0665eea 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -35,8 +35,8 @@ Mem::Mem() : story_fp(nullptr), story_size(0), first_undo(nullptr), last_undo(nu
 
 void Mem::initialize() {
 	initializeStoryFile();
-	initializeUndo();
 	loadGameHeader();
+	initializeUndo();
 
 	// Allocate memory for story data
 	if ((zmp = (zbyte *)realloc(zmp, story_size)) == nullptr)


Commit: 4a3f517b719d4ee07fab8f704d5884c52c0da4c1
    https://github.com/scummvm/scummvm/commit/4a3f517b719d4ee07fab8f704d5884c52c0da4c1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Prompt for exit when game quits

Changed paths:
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/processor.h


diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 95cdb22..d3b0749 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -44,6 +44,11 @@ void Frotz::runGame(Common::SeekableReadStream *gameFile) {
 
 	// Game loop
 	interpret();
+
+	if (!shouldQuit()) {
+		flush_buffer();
+		glk_exit();
+	}
 }
 
 void Frotz::initialize() {
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index 5f61a2e..77a673d 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -169,11 +169,6 @@ private:
 	 * @{
 	 */
 
-	/**
-	 * Copy the contents of the text buffer to the output streams.
-	 */
-	void flush_buffer();
-
 	 /**
 	  * High level output function.
 	  */
@@ -1545,6 +1540,18 @@ protected:
 	void z_store();
 
 	/**@}*/
+
+	/**
+	 * \defgroup Input support methods
+	 * @{
+	 */
+
+	/**
+	 * Copy the contents of the text buffer to the output streams.
+	 */
+	void flush_buffer();
+
+	/**@}*/
 public:
 	/**
 	 * Constructor


Commit: 1c190e50b45701802bcc8fe9e96d0c782e7d96a2
    https://github.com/scummvm/scummvm/commit/1c190e50b45701802bcc8fe9e96d0c782e7d96a2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Mark text strings as translateable

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


diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index 2a5fd11..19f8aef 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -30,7 +30,7 @@
 #include "glk/window_graphics.h"
 #include "glk/window_text_buffer.h"
 #include "glk/window_pair.h"
-
+#include "common/translation.h"
 
 namespace Glk {
 
@@ -59,7 +59,7 @@ GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc) :
 }
 
 void GlkAPI::glk_exit(void) {
-	glk_put_string("[ press any key to exit ]");
+	glk_put_string(_("[ press any key to exit ]"));
 	_events->waitForPress();
 
 	// Trigger a ScumMVM shutdown of game
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index dc0ae0b..eb8cd40 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -22,6 +22,7 @@
 
 #include "glk/scott/scott.h"
 #include "common/config-manager.h"
+#include "common/translation.h"
 
 namespace Glk {
 namespace Scott {
@@ -94,10 +95,10 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 
 		switch (performActions(vb, no)) {
 		case -1:
-			output("I don't understand your command. ");
+			output(_("I don't understand your command. "));
 			break;
 		case -2:
-			output("I can't do that yet. ");
+			output(_("I can't do that yet. "));
 			break;
 		default:
 			break;
@@ -113,9 +114,9 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 				if (_items[LIGHT_SOURCE]._location == CARRIED ||
 				        _items[LIGHT_SOURCE]._location == MY_LOC) {
 					if (_options & SCOTTLIGHT)
-						output("Light has run out! ");
+						output(_("Light has run out! "));
 					else
-						output("Your light has run out. ");
+						output(_("Your light has run out. "));
 				}
 				if (_options & PREHISTORIC_LAMP)
 					_items[LIGHT_SOURCE]._location = DESTROYED;
@@ -124,12 +125,12 @@ void Scott::runGame(Common::SeekableReadStream *gameFile) {
 				        _items[LIGHT_SOURCE]._location == MY_LOC) {
 
 					if (_options & SCOTTLIGHT) {
-						output("Light runs out in ");
+						output(_("Light runs out in "));
 						outputNumber(_gameHeader._lightTime);
-						output(" turns. ");
+						output(_(" turns. "));
 					} else {
 						if (_gameHeader._lightTime % 5 == 0)
-							output("Your light is growing dim. ");
+							output(_("Your light is growing dim. "));
 					}
 				}
 			}
@@ -388,7 +389,9 @@ void Scott::outputNumber(int a) {
 }
 
 void Scott::look(void) {
-	const char *const ExitNames[6] = { "North", "South", "East", "West", "Up", "Down" };
+	const char *const ExitNames[6] = {
+		_("North"), _("South"), _("East"), _("West"), _("Up"), _("Down")
+	};
 	Room *r;
 	int ct, f;
 	int pos;
@@ -399,9 +402,9 @@ void Scott::look(void) {
 	if ((_bitFlags & (1 << DARKBIT)) && _items[LIGHT_SOURCE]._location != CARRIED
 	        && _items[LIGHT_SOURCE]._location != MY_LOC) {
 		if (_options & YOUARE)
-			display(_topWindow, "You can't see. It is too dark!\n");
+			display(_topWindow, _("You can't see. It is too dark!\n"));
 		else
-			display(_topWindow, "I can't see. It is too dark!\n");
+			display(_topWindow, _("I can't see. It is too dark!\n"));
 		if (_options & TRS80_STYLE)
 			display(_topWindow, TRS80_LINE);
 		return;
@@ -411,14 +414,14 @@ void Scott::look(void) {
 		display(_topWindow, "%s\n", r->_text.c_str() + 1);
 	else {
 		if (_options & YOUARE)
-			display(_topWindow, "You are in a %s\n", r->_text.c_str());
+			display(_topWindow, _("You are in a %s\n"), r->_text.c_str());
 		else
-			display(_topWindow, "I'm in a %s\n", r->_text.c_str());
+			display(_topWindow, _("I'm in a %s\n"), r->_text.c_str());
 	}
 
 	ct = 0;
 	f = 0;
-	display(_topWindow, "\nObvious exits: ");
+	display(_topWindow, _("\nObvious exits: "));
 	while (ct < 6) {
 		if (r->_exits[ct] != 0) {
 			if (f == 0)
@@ -431,7 +434,7 @@ void Scott::look(void) {
 	}
 
 	if (f == 0)
-		display(_topWindow, "none");
+		display(_topWindow, _("none"));
 	display(_topWindow, ".\n");
 	ct = 0;
 	f = 0;
@@ -440,10 +443,10 @@ void Scott::look(void) {
 		if (_items[ct]._location == MY_LOC) {
 			if (f == 0) {
 				if (_options & YOUARE) {
-					display(_topWindow, "\nYou can also see: ");
+					display(_topWindow, _("\nYou can also see: "));
 					pos = 18;
 				} else {
-					display(_topWindow, "\nI can also see: ");
+					display(_topWindow, _("\nI can also see: "));
 					pos = 16;
 				}
 				f++;
@@ -523,7 +526,7 @@ Common::Error Scott::saveGameData(strid_t file) {
 		glk_put_string_stream(file, msg.c_str());
 	}
 
-	output("Saved.\n");
+	output(_("Saved.\n"));
 	return Common::kNoError;
 }
 
@@ -614,7 +617,7 @@ int Scott::getInput(int *vb, int *no) {
 		*vb = vc;
 		*no = nc;
 		if (vc == -1) {
-			output("You use word(s) I don't know! ");
+			output(_("You use word(s) I don't know! "));
 		}
 	} while (vc == -1);
 
@@ -742,9 +745,9 @@ int Scott::performLine(int ct) {
 			case 52:
 				if (countCarried() == _gameHeader._maxCarry) {
 					if (_options & YOUARE)
-						output("You are carrying too much. ");
+						output(_("You are carrying too much. "));
 					else
-						output("I've too much to carry! ");
+						output(_("I've too much to carry! "));
 					break;
 				}
 				_items[param[pptr++]]._location = CARRIED;
@@ -775,9 +778,9 @@ int Scott::performLine(int ct) {
 				break;
 			case 61:
 				if (_options & YOUARE)
-					output("You are dead.\n");
+					output(_("You are dead.\n"));
 				else
-					output("I am dead.\n");
+					output(_("I am dead.\n"));
 				_bitFlags &= ~(1 << DARKBIT);
 				MY_LOC = _gameHeader._numRooms;// It seems to be what the code says!
 				break;
@@ -789,7 +792,7 @@ int Scott::performLine(int ct) {
 			}
 			case 63:
 doneit:
-				output("The game is now over.\n");
+				output(_("The game is now over.\n"));
 				glk_exit();
 				return 0;
 			case 64:
@@ -804,15 +807,15 @@ doneit:
 					i++;
 				}
 				if (_options & YOUARE)
-					output("You have stored ");
+					output(_("You have stored "));
 				else
-					output("I've stored ");
+					output(_("I've stored "));
 				outputNumber(n);
-				output(" treasures.  On a scale of 0 to 100, that rates ");
+				output(_(" treasures.  On a scale of 0 to 100, that rates "));
 				outputNumber((n * 100) / _gameHeader._treasures);
 				output(".\n");
 				if (n == _gameHeader._treasures) {
-					output("Well done.\n");
+					output(_("Well done.\n"));
 					goto doneit;
 				}
 				break;
@@ -821,9 +824,9 @@ doneit:
 				int i = 0;
 				int f = 0;
 				if (_options & YOUARE)
-					output("You are carrying:\n");
+					output(_("You are carrying:\n"));
 				else
-					output("I'm carrying:\n");
+					output(_("I'm carrying:\n"));
 				while (i <= _gameHeader._numItems) {
 					if (_items[i]._location == CARRIED) {
 						if (f == 1) {
@@ -838,7 +841,7 @@ doneit:
 					i++;
 				}
 				if (f == 0)
-					output("Nothing");
+					output(_("Nothing"));
 				output(".\n");
 				break;
 			}
@@ -967,7 +970,7 @@ int Scott::performActions(int vb, int no) {
 	int fl;
 	int doagain = 0;
 	if (vb == 1 && no == -1) {
-		output("Give me a direction too.");
+		output(_("Give me a direction too."));
 		return 0;
 	}
 	if (vb == 1 && no >= 1 && no <= 6) {
@@ -976,7 +979,7 @@ int Scott::performActions(int vb, int no) {
 		        _items[LIGHT_SOURCE]._location == CARRIED)
 			d = 0;
 		if (d)
-			output("Dangerous to move in the dark! ");
+			output(_("Dangerous to move in the dark! "));
 		nl = _rooms[MY_LOC]._exits[no - 1];
 		if (nl != 0) {
 			MY_LOC = nl;
@@ -984,16 +987,16 @@ int Scott::performActions(int vb, int no) {
 		}
 		if (d) {
 			if (_options & YOUARE)
-				output("You fell down and broke your neck. ");
+				output(_("You fell down and broke your neck. "));
 			else
-				output("I fell down and broke my neck. ");
+				output(_("I fell down and broke my neck. "));
 			glk_exit();
 			return 0;
 		}
 		if (_options & YOUARE)
-			output("You can't go in that direction. ");
+			output(_("You can't go in that direction. "));
 		else
-			output("I can't go in that direction. ");
+			output(_("I can't go in that direction. "));
 		return 0;
 	}
 
@@ -1051,7 +1054,7 @@ int Scott::performActions(int vb, int no) {
 					int f = 0;
 
 					if (d) {
-						output("It is dark.\n");
+						output(_("It is dark.\n"));
 						return 0;
 					}
 					while (i <= _gameHeader._numItems) {
@@ -1065,43 +1068,43 @@ int Scott::performActions(int vb, int no) {
 
 							if (countCarried() == _gameHeader._maxCarry) {
 								if (_options & YOUARE)
-									output("You are carrying too much. ");
+									output(_("You are carrying too much. "));
 								else
-									output("I've too much to carry. ");
+									output(_("I've too much to carry. "));
 								return 0;
 							}
 							_items[i]._location = CARRIED;
 							output(_items[i]._text);
-							output(": O.K.\n");
+							output(_(": O.K.\n"));
 							f = 1;
 						}
 						i++;
 					}
 					if (f == 0)
-						output("Nothing taken.");
+						output(_("Nothing taken."));
 					return 0;
 				}
 				if (no == -1) {
-					output("What ? ");
+					output(_("What ? "));
 					return 0;
 				}
 				if (countCarried() == _gameHeader._maxCarry) {
 					if (_options & YOUARE)
-						output("You are carrying too much. ");
+						output(_("You are carrying too much. "));
 					else
-						output("I've too much to carry. ");
+						output(_("I've too much to carry. "));
 					return 0;
 				}
 				item = matchUpItem(_nounText, MY_LOC);
 				if (item == -1) {
 					if (_options & YOUARE)
-						output("It is beyond your power to do that. ");
+						output(_("It is beyond your power to do that. "));
 					else
-						output("It's beyond my power to do that. ");
+						output(_("It's beyond my power to do that. "));
 					return 0;
 				}
 				_items[item]._location = CARRIED;
-				output("O.K. ");
+				output(_("O.K. "));
 				return 0;
 			}
 			if (vb == 18) {
@@ -1120,25 +1123,25 @@ int Scott::performActions(int vb, int no) {
 
 							_items[i]._location = MY_LOC;
 							output(_items[i]._text);
-							output(": O.K.\n");
+							output(_(": O.K.\n"));
 							f = 1;
 						}
 						i++;
 					}
 					if (f == 0)
-						output("Nothing dropped.\n");
+						output(_("Nothing dropped.\n"));
 					return 0;
 				}
 				if (no == -1) {
-					output("What ? ");
+					output(_("What ? "));
 					return 0;
 				}
 				item = matchUpItem(_nounText, CARRIED);
 				if (item == -1) {
 					if (_options & YOUARE)
-						output("It's beyond your power to do that.\n");
+						output(_("It's beyond your power to do that.\n"));
 					else
-						output("It's beyond my power to do that.\n");
+						output(_("It's beyond my power to do that.\n"));
 					return 0;
 				}
 				_items[item]._location = MY_LOC;


Commit: 3700919881a9636ae02478691d8c7c73a6d5084b
    https://github.com/scummvm/scummvm/commit/3700919881a9636ae02478691d8c7c73a6d5084b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add POTFILES

Changed paths:
  A engines/glk/POTFILES


diff --git a/engines/glk/POTFILES b/engines/glk/POTFILES
new file mode 100644
index 0000000..9caee0e
--- /dev/null
+++ b/engines/glk/POTFILES
@@ -0,0 +1,2 @@
+engines/glk/streams.cpp
+engines/glk/scott/scott.cpp


Commit: 62af5ea8919a5148154413eaec80a9e686d64114
    https://github.com/scummvm/scummvm/commit/62af5ea8919a5148154413eaec80a9e686d64114
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: More work on displaying Beyond Zork title screen

Changed paths:
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/pics.cpp
    engines/glk/frotz/pics.h
    engines/glk/frotz/pics_decoder.cpp
    engines/glk/frotz/pics_decoder.h
    engines/glk/picture.cpp
    engines/glk/raw_decoder.cpp


diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index 4e7a503..c046bb4 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -97,10 +97,8 @@ void GlkInterface::initialize() {
 	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Oblique, 1);
 
 	/*
-	 * Open game windows
+	 * Get the screen size
 	 */
-	if (_storyId == BEYOND_ZORK)
-		showBeyondZorkTitle();
 
 	gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
 	if (!gos_lower)
@@ -108,20 +106,8 @@ void GlkInterface::initialize() {
 	glk_window_get_size(gos_lower, &width, &height);
 	glk_window_close(gos_lower, nullptr);
 
-	gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
-	gos_upper = glk_window_open(gos_lower,
-			winmethod_Above | winmethod_Fixed,
-			0,
-			wintype_TextGrid, 0);
-
 	gos_channel = nullptr;
 
-	glk_set_window(gos_lower);
-	gos_curwin = gos_lower;
-
-	// Set the screen colors
-	garglk_set_zcolors(_defaultForeground, _defaultBackground);
-
 	/*
 	 * Icky magic bit setting
 	 */
@@ -186,6 +172,24 @@ void GlkInterface::initialize() {
 		if (h_flags & COLOUR_FLAG)
 			h_flags &= ~COLOUR_FLAG;
 	}
+
+	/*
+	 * Open the windows
+	 */
+	if (_storyId == BEYOND_ZORK)
+		showBeyondZorkTitle();
+
+	gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+	gos_upper = glk_window_open(gos_lower,
+		winmethod_Above | winmethod_Fixed,
+		0,
+		wintype_TextGrid, 0);
+
+	glk_set_window(gos_lower);
+	gos_curwin = gos_lower;
+
+	// Set the screen colors
+	garglk_set_zcolors(_defaultForeground, _defaultBackground);
 }
 
 bool GlkInterface::initPictures() {
@@ -274,8 +278,6 @@ bool GlkInterface::os_picture_data(int picture, glui32 *height, glui32 *width) {
 	}
 }
 
-
-
 void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) {
 	// TODO
 }
diff --git a/engines/glk/frotz/pics.cpp b/engines/glk/frotz/pics.cpp
index 976c2b2..4953f04 100644
--- a/engines/glk/frotz/pics.cpp
+++ b/engines/glk/frotz/pics.cpp
@@ -46,7 +46,7 @@ Pics::Pics() : Common::Archive(), _filename(getFilename()) {
 	_index.resize(READ_LE_UINT16(&buffer[PIC_FILE_HEADER_NUM_IMAGES]));
 	_entrySize = buffer[PIC_FILE_HEADER_ENTRY_SIZE];
 	_version = buffer[PIC_FILE_HEADER_FLAGS];
-	assert(_entrySize >= 6 && _entrySize <= 14);
+	assert(_entrySize >= 8 && _entrySize <= 14);
 
 	// Iterate through loading the index
 	for (uint idx = 0; idx < _index.size(); ++idx) {
@@ -56,6 +56,7 @@ Pics::Pics() : Common::Archive(), _filename(getFilename()) {
 		e._number = READ_LE_UINT16(buffer);
 		e._width = READ_LE_UINT16(buffer + 2);
 		e._height = READ_LE_UINT16(buffer + 4);
+		e._flags = READ_LE_UINT16(buffer + 6);
 
 		if (_entrySize >= 11) {
 			e._dataOffset = READ_BE_UINT32(buffer + 7) & 0xffffff;
@@ -64,8 +65,6 @@ Pics::Pics() : Common::Archive(), _filename(getFilename()) {
 
 			if (_entrySize == 14) {
 				e._paletteOffset = READ_BE_UINT32(buffer + 10) & 0xffffff;
-				e._paletteSize = e._dataOffset - e._paletteOffset;
-				assert((e._paletteSize % 3) == 0);
 			}
 		}
 
@@ -132,25 +131,29 @@ Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::String
 	for (uint idx = 0; idx < _index.size(); ++idx) {
 		const Entry &e = _index[idx];
 		if (e._filename.equalsIgnoreCase(name)) {
+			Common::Array<byte> palette;
 			Common::File f;
+			Common::SeekableReadStream *dest;
 			if (!f.open(_filename))
 				error("Reading failed");
 
 			// Read in the image's palette
-			assert(e._paletteSize);
-			Common::Array<byte> palette;
-			palette.resize(e._paletteSize);
+			assert(e._paletteOffset);
 			f.seek(e._paletteOffset);
-			f.read(&palette[0], e._paletteSize);
+			palette.resize(f.readByte() * 3);
+			f.read(&palette[0], palette.size());
 
+			PictureDecoder decoder;
 			if (e._dataSize) {
 				Common::SeekableReadStream *src = f.readStream(e._dataSize);
-				f.close();
-				return PictureDecoder::decode(*src, &palette[0]);
-
+				dest = decoder.decode(*src, e._flags, palette, MCGA, e._width, e._height);
+				delete src;
 			} else {
 				error("TODO: Empty rect renderings");
 			}
+
+			f.close();
+			return dest;
 		}
 	}
 
diff --git a/engines/glk/frotz/pics.h b/engines/glk/frotz/pics.h
index b05acc5..2c6d17a 100644
--- a/engines/glk/frotz/pics.h
+++ b/engines/glk/frotz/pics.h
@@ -29,24 +29,36 @@
 namespace Glk {
 namespace Frotz {
 
+enum PicturesMode {
+	MONO  = 0,
+	TEXT  = 1,
+	CGA   = 2,
+	MCGA  = 3,
+	EGA   = 4,
+	AMIGA = 5
+};
+
 /**
  * Infocom graphics file manager
  */
 class Pics : public Common::Archive {
 	/**
-	 * Describes one chunk of the Blorb file.
+	 * Describes a single index entry
 	 */
 	struct Entry {
 		uint _number;
 		size_t _width, _height;
+		uint _flags;
 		size_t _dataOffset;
 		size_t _dataSize;
 		size_t _paletteOffset;
-		size_t _paletteSize;
 		Common::String _filename;
 
-		Entry() : _number(0), _width(0), _height(0), _dataOffset(0), _dataSize(0),
-			_paletteOffset(0), _paletteSize(0) {}
+		/**
+		 * Constructor
+		 */
+		Entry() : _number(0), _width(0), _height(0), _flags(0), _dataOffset(0), _dataSize(0),
+			_paletteOffset(0) {}
 	};
 private:
 	Common::String _filename;
diff --git a/engines/glk/frotz/pics_decoder.cpp b/engines/glk/frotz/pics_decoder.cpp
index a67ed12..dbe8aed 100644
--- a/engines/glk/frotz/pics_decoder.cpp
+++ b/engines/glk/frotz/pics_decoder.cpp
@@ -21,13 +21,226 @@
  */
 
 #include "glk/frotz/pics_decoder.h"
+#include "glk/frotz/pics.h"
+#include "common/memstream.h"
 
 namespace Glk {
 namespace Frotz {
 
-Common::MemoryReadStream *PictureDecoder::decode(Common::ReadStream &src, const byte *palette) {
-	// TODO
-	return nullptr;
+PictureDecoder::PictureDecoder() {
+	_tableVal = new byte[3 * 3840];
+	_tableRef = (uint16 *)(_tableVal + 3840);
+}
+
+PictureDecoder::~PictureDecoder() {
+	delete[] _tableVal;
+}
+
+Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint flags,
+		const Common::Array<byte> &palette, uint display, size_t width, size_t height) {
+    static const int raise_bits[4] = { 0x0100, 0x0300, 0x0700, 0x0000 };
+	Common::MemoryWriteStreamDynamic out(DisposeAfterUse::NO);
+	byte buf[512];
+    byte transparent;
+//    int colour_shift;
+//    int first_colour;
+    int code, prev_code = 0;
+    int next_entry;
+    int bits_per_code;
+    int bits_shift;
+    int bits;
+    int bufpos = 0;
+    int i;
+
+	/*
+	 * Write out dimensions of image
+	 */
+	out.writeUint16LE(width);
+	out.writeUint16LE(height);
+
+    /* Set up the color mapping. This is only used for MCGA pictures; the colour
+	 * map affects every picture on the screen. The first colour to be defined is
+     * colour 2. Every map defines up to 14 colours (colour 2 to 15). These colours
+	 * are not related to the standard Z-machine colour scheme which remains unchanged.
+	 * (This is based on the Amiga interpreter which had to work with 16 colours.
+	 * Colours 0 and 1 were used for text; changing the text colours actually changed
+     * palette entries 0 and 1. This interface uses the same trick in Amiga mode.)
+	 */
+/*
+	switch (display) {
+	case CGA:
+		colour_shift = -2;
+		break;
+	case EGA:
+		colour_shift = 0;
+		break;
+	case MCGA:
+		colour_shift = 32;
+		first_colour = 34;
+		break;
+	case AMIGA:
+		colour_shift = -1;
+		first_colour = 65;
+		break;
+	default:
+		break;
+	}
+	*/
+	out.writeUint16LE(palette.size() / 3);
+	if (!palette.empty())
+		out.write(&palette[0], palette.size());
+
+    /* Bit 0 of "flags" indicates that the picture uses a transparent colour,
+	 * the top four bits tell us which colour it is. For CGA and MCGA pictures
+	 * this is always 0; for EGA pictures it can be any colour between 0 and 15.
+	 */
+    transparent = 0xff;
+    if (flags & 1)
+		transparent = flags >> 12;
+	out.writeByte(transparent);
+
+    /* The uncompressed picture is a long sequence of bytes. Every byte holds
+	 * the colour of a pixel, starting at the top left, stopping at the bottom right.
+	 * We keep track of our position in the current line. (There is a special case:
+	 * CGA pictures with no transparent colour are stored as bit patterns, i.e.
+	 * every byte holds the pattern for eight pixels. A pixel must be white if the
+	 * corresponding bit is set, otherwise it must be black.)
+	 */
+//    current_x = 1 + width;
+//    current_y = 1 - 1;
+
+    /* The compressed picture is a stream of bits. We read the file byte-wise,
+	 * storing the current byte in the variable "bits". Several bits make one code;
+	 * the variable "bits_shift" helps us to build the next code.
+	 */
+    bits_shift = 0;
+    bits = 0;
+
+reset_table:
+    /* Clear the table. We use a table of 3840 entries. Each entry consists of both
+	 * a value and a reference to another table entry. Following these references
+	 * we get a sequence of values. At the start of decompression all table entries
+	 * are undefined. Later we see how entries are set and used.
+	 */
+    next_entry = 1;
+
+    /* At the start of decompression 9 bits make one code; during the process this can
+	 * rise to 12 bits per code. 9 bits are sufficient to address both 256 literal values
+	 * and 256 table entries; 12 bits are sufficient to address both 256 literal values
+	 * and all 3840 table entries. The number of bits per code rises with the number of
+	 * table entries. When the table is cleared, the number of bits per code drops back to 9.
+	 */
+    bits_per_code = 9;
+
+next_code:
+
+    /* Read the next code from the graphics file. This requires some confusing bit operations.
+	 * Note that low bits always come first. Usually there are a few bits left over from
+	 * the previous code; these bits must be used before further bits are read from the
+	 * graphics file.
+	 */
+    code = bits >> (8 - bits_shift);
+
+    do {
+		bits = src.readByte();
+		code |= bits << bits_shift;
+
+		bits_shift += 8;
+	} while (bits_shift < bits_per_code);
+
+	bits_shift -= bits_per_code;
+
+	code &= 0xfff >> (12 - bits_per_code);
+
+	/* There are two codes with a special meaning. The first one is 256 which clears
+	 * the table and sets the number of bits per code to 9. (This is necessary when
+	 * the table is full.) The second one is 257 which marks the end of the picture.
+	 * For the sake of efficiency, we drecement the code by 256.
+	 */
+	code -= 256;
+
+	if (code == 0)
+		goto reset_table;
+	if (code == 1) {
+		return new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES);
+	}
+
+	/* Codes from 0 to 255 are literals, i.e. they represent a plain byte value.
+	 * Codes from 258 onwards are references to table entries, i.e. they represent
+	 * a sequence of byte values (see the remarks on the table above). This means
+	 * that for each code one or several byte values are added to the decompressed
+	 * picture. But there is yet more work to do: Every time we read a code one
+	 * table entry is set. As we said above, a table entry consist of both a value
+	 * and a reference to another table entry. If the current code is a literal,
+	 * then the value has to be set to this literal; but if the code refers to a
+	 * sequence of byte values, then the value has to be set to the last byte of
+	 * this sequence. In any case, the reference is set to the previous code.
+	 * Finally, one should be aware that a code may legally refer to the table entry
+	 * which is currently being set. This requires some extra care.
+	 */
+	_tableRef[next_entry] = prev_code;
+
+	prev_code = code;
+
+	while (code >= 0) {
+		buf[bufpos++] = _tableVal[code];
+		code = (short) _tableRef[code];
+	}
+
+    if (next_entry == prev_code)
+		buf[0] = code;
+
+    _tableVal[next_entry] = code;
+
+    /* The number of bits per code is incremented when the current number of bits
+	 * no longer suffices to address all defined table entries; but in any case
+	 * the number of bits may never be greater than 12.
+	 */
+    next_entry++;
+
+    if (next_entry == raise_bits[bits_per_code - 9])
+		bits_per_code++;
+
+reverse_buffer:
+    /* Output the sequence of byte values (pixels). The order of the sequence
+	 * must be reversed. (This is why we have stored the sequence in a buffer;
+	 * experiments show that a buffer of 512 bytes suffices.)
+	 *
+	 * Either add a single pixel or a pattern of eight bits (b/w CGA pictures without
+	 * a transparent colour) to the current line. Increment our position by 1 or 8
+	 * respectively. The pixel may have to be painted several times if the scaling
+	 * factor is greater than one.
+	 */
+    if (display == CGA && transparent == 0xff) {
+		// TODO
+    } else {
+		byte v = code;
+
+		if (v != transparent) {
+			//v += colour_shift;
+
+			if (display != MCGA) {
+				// TODO
+			} else {
+				// position shift
+			}
+
+			out.writeByte(v);
+
+			if (display == AMIGA) {
+				// TODO
+			}
+		}
+    }
+
+    /* If there are no more values in the buffer then read the next code from the file. 
+	 * Otherwise fetch the next byte value from the buffer and continue outputing the picture.
+	 */
+    if (bufpos == 0)
+		goto next_code;
+
+	code = (code & ~0xff) | buf[--bufpos];
+    goto reverse_buffer;
 }
 
 } // End of namespace Frotz
diff --git a/engines/glk/frotz/pics_decoder.h b/engines/glk/frotz/pics_decoder.h
index 41484a1..3d2165f 100644
--- a/engines/glk/frotz/pics_decoder.h
+++ b/engines/glk/frotz/pics_decoder.h
@@ -23,8 +23,8 @@
 #ifndef GLK_FROTZ_PICS_DECODER_H
 #define GLK_FROTZ_PICS_DECODER_H
 
-#include "common/memstream.h"
 #include "common/stream.h"
+#include "common/array.h"
 
 namespace Glk {
 namespace Frotz {
@@ -34,11 +34,25 @@ namespace Frotz {
  * Glk engine is capable of then loading into a picture object
  */
 class PictureDecoder {
+private:
+	byte *_tableVal;
+	uint16 *_tableRef;
 public:
 	/**
+	 * Constructor
+	 */
+	PictureDecoder();
+
+	/**
+	 * Destructor
+	 */
+	~PictureDecoder();
+
+	/**
 	 * Decode method
 	 */
-	static Common::MemoryReadStream *decode(Common::ReadStream &src, const byte *palette);
+	Common::SeekableReadStream *decode(Common::ReadStream &src, uint flags,
+		const Common::Array<byte> &palette, uint display, size_t width, size_t height);
 };
 
 } // End of namespace Frotz
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 6d8fce9..c8dcbc5 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -130,11 +130,10 @@ Picture *Pictures::load(uint32 id) {
 		return nullptr;
 	}
 
-	pic = new Picture();
+	pic = new Picture(img->w, img->h, img->format);
 	pic->_refCount = 1;
     pic->_id = id;
     pic->_scaled = false;
-	pic->create(img->w, img->h, g_system->getScreenFormat());
 	pic->blitFrom(*img);
 
 	if (palette)
diff --git a/engines/glk/raw_decoder.cpp b/engines/glk/raw_decoder.cpp
index a5cd593..e0899e0 100644
--- a/engines/glk/raw_decoder.cpp
+++ b/engines/glk/raw_decoder.cpp
@@ -22,6 +22,7 @@
 
 #include "glk/raw_decoder.h"
 #include "common/stream.h"
+#include "common/textconsole.h"
 
 namespace Glk {
 
@@ -44,7 +45,7 @@ bool RawDecoder::loadStream(Common::SeekableReadStream &stream) {
 
 	uint width = stream.readUint16LE();
 	uint height = stream.readUint16LE();
-	_paletteColorCount = stream.readByte();
+	_paletteColorCount = stream.readUint16LE();
 	assert(_paletteColorCount > 0);
 
 	// Read in the palette
@@ -52,8 +53,18 @@ bool RawDecoder::loadStream(Common::SeekableReadStream &stream) {
 	stream.read(_palette, _paletteColorCount * 3);
 
 	// Set up the surface and read it in
+	stream.readByte();
 	_surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
-	stream.read(_surface.getPixels(), width * height);
+
+	assert((stream.size() - stream.pos()) == (int)(width * height));
+	byte *pixels = (byte *)_surface.getPixels();
+	stream.read(pixels, width * height);
+
+	for (uint idx = 0; idx < width * height; ++idx, ++pixels) {
+		assert(*pixels != 0xff);
+		if (*pixels >= _paletteColorCount)
+			*pixels = _paletteColorCount - 1;
+	}
 
 	return true;
 }


Commit: 77468312de507fcd7e01c95f1135afc33a6f4b24
    https://github.com/scummvm/scummvm/commit/77468312de507fcd7e01c95f1135afc33a6f4b24
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Beyond Zork title screen now showing

Changed paths:
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/glk_interface.h
    engines/glk/frotz/pics_decoder.cpp
    engines/glk/picture.cpp
    engines/glk/window_graphics.cpp


diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index c046bb4..c2d6ca2 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -165,13 +165,11 @@ void GlkInterface::initialize() {
 	h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_AMIGA;
 	h_interpreter_version = 'F';
 
-	{
-		// Set these per spec 8.3.2.
-		h_default_foreground = WHITE_COLOUR;
-		h_default_background = BLACK_COLOUR;
-		if (h_flags & COLOUR_FLAG)
-			h_flags &= ~COLOUR_FLAG;
-	}
+	// Set these per spec 8.3.2.
+	h_default_foreground = WHITE_COLOUR;
+	h_default_background = BLACK_COLOUR;
+	if (h_flags & COLOUR_FLAG)
+		h_flags &= ~COLOUR_FLAG;
 
 	/*
 	 * Open the windows
@@ -451,11 +449,11 @@ void GlkInterface::gos_cancel_pending_line() {
 
 void GlkInterface::showBeyondZorkTitle() {
 	uint winW, winH, imgW, imgH;
-	winid_t win = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
-	glk_window_get_size(gos_lower, &winW, &winH);
+	winid_t win = glk_window_open(0, 0, 0, wintype_Graphics, 0);
+	glk_window_get_size(win, &winW, &winH);
 
 	if (os_picture_data(1, &imgW, &imgH)) {
-		os_draw_picture(1, win, Common::Point(1, 1));
+		os_draw_picture(1, win, Common::Rect(0, 0, winW, winH));
 		_events->waitForPress();
 	}
 
@@ -466,6 +464,10 @@ void GlkInterface::os_draw_picture(int picture, winid_t win, const Common::Point
 	glk_image_draw(win, picture, pos.x - 1, pos.y - 1);
 }
 
+void GlkInterface::os_draw_picture(int picture, winid_t win, const Common::Rect &r) {
+	glk_image_draw_scaled(win, picture, r.left, r.top, r.width(), r.height());
+}
+
 zchar GlkInterface::os_read_key(int timeout, bool show_cursor) {
 	event_t ev;
 	winid_t win = gos_curwin ? gos_curwin : gos_lower;
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index b279ec8..4e531ee 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -169,6 +169,11 @@ protected:
 	void os_draw_picture(int picture, winid_t win, const Common::Point &pos);
 
 	/**
+	 * Display a picture using the specified bounds
+	 */
+	void os_draw_picture(int picture, winid_t win, const Common::Rect &r);
+
+	/**
 	 * Call the IO interface to play a sample.
 	 */
 	void start_sample(int number, int volume, int repeats, zword eos);
diff --git a/engines/glk/frotz/pics_decoder.cpp b/engines/glk/frotz/pics_decoder.cpp
index dbe8aed..d601874 100644
--- a/engines/glk/frotz/pics_decoder.cpp
+++ b/engines/glk/frotz/pics_decoder.cpp
@@ -42,8 +42,8 @@ Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint
 	Common::MemoryWriteStreamDynamic out(DisposeAfterUse::NO);
 	byte buf[512];
     byte transparent;
-//    int colour_shift;
-//    int first_colour;
+    int colour_shift;
+	int first_colour;
     int code, prev_code = 0;
     int next_entry;
     int bits_per_code;
@@ -66,7 +66,7 @@ Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint
 	 * Colours 0 and 1 were used for text; changing the text colours actually changed
      * palette entries 0 and 1. This interface uses the same trick in Amiga mode.)
 	 */
-/*
+
 	switch (display) {
 	case CGA:
 		colour_shift = -2;
@@ -85,7 +85,11 @@ Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint
 	default:
 		break;
 	}
-	*/
+	
+	// Note: we don't actually use paletted indexes, so adjust colour_shift
+	// relative to first_colour
+	colour_shift -= first_colour;
+
 	out.writeUint16LE(palette.size() / 3);
 	if (!palette.empty())
 		out.write(&palette[0], palette.size());
@@ -162,6 +166,12 @@ next_code:
 	if (code == 0)
 		goto reset_table;
 	if (code == 1) {
+		bool t[256];
+		// *******DEBUG*******
+		Common::fill(&t[0], &t[256], false);
+		for (uint idx = 0; idx < out.size(); ++idx)
+			t[*((byte *)out.getData() + idx)] = true;
+
 		return new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES);
 	}
 
@@ -217,7 +227,7 @@ reverse_buffer:
 		byte v = code;
 
 		if (v != transparent) {
-			//v += colour_shift;
+			v += colour_shift;
 
 			if (display != MCGA) {
 				// TODO
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index c8dcbc5..3404315 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -106,6 +106,7 @@ Picture *Pictures::load(uint32 id) {
 	RawDecoder raw;
 	const Graphics::Surface *img;
 	const byte *palette = nullptr;
+	int palCount = 0;
 	Picture *pic;
 
 	// Check if the picture is already in the store
@@ -114,30 +115,46 @@ Picture *Pictures::load(uint32 id) {
 		return pic;
 
 	Common::File f;
-	if (f.open(Common::String::format("PIC%u.png", id))) {
+	if (f.open(Common::String::format("pic%u.png", id))) {
 		png.loadStream(f);
 		img = png.getSurface();
 		palette = png.getPalette();
-	} else if (f.open(Common::String::format("PIC%u.jpg", id))) {
+		palCount = png.getPaletteColorCount();
+	} else if (f.open(Common::String::format("pic%u.jpg", id))) {
 		jpg.loadStream(f);
 		img = jpg.getSurface();
-	} else if (f.open(Common::String::format("PIC%u.raw", id))) {
+	} else if (f.open(Common::String::format("pic%u.raw", id))) {
 		raw.loadStream(f);
 		img = raw.getSurface();
 		palette = raw.getPalette();
+		palCount = raw.getPaletteColorCount();
 	} else {
 		// No such picture
 		return nullptr;
 	}
 
-	pic = new Picture(img->w, img->h, img->format);
+	pic = new Picture(img->w, img->h, g_system->getScreenFormat());
 	pic->_refCount = 1;
     pic->_id = id;
     pic->_scaled = false;
-	pic->blitFrom(*img);
 
-	if (palette)
-		pic->convertToInPlace(g_system->getScreenFormat(), palette);
+	if (!palette) {
+		pic->blitFrom(*img);
+	} else {
+		uint pal[256];
+		for (uint idx = 0; idx < palCount; ++idx)
+			pal[idx] = pic->format.RGBToColor(palette[idx * 3],
+				palette[idx * 3 + 1], palette[idx * 3 + 2]);
+		
+		byte *srcP = (byte *)img->getPixels(), *destP = (byte *)pic->getPixels();
+		for (int idx = 0; idx < img->w * img->h; ++idx, srcP++, destP += pic->format.bytesPerPixel) {
+			uint val = (*srcP >= palCount) ? 0 : pal[*srcP];
+			if (pic->format.bytesPerPixel == 2)
+				WRITE_LE_UINT16(destP, val);
+			else
+				WRITE_LE_UINT32(destP, val);
+		}
+	}
 
     store(pic);
     return pic;
diff --git a/engines/glk/window_graphics.cpp b/engines/glk/window_graphics.cpp
index 92224b6..7343d51 100644
--- a/engines/glk/window_graphics.cpp
+++ b/engines/glk/window_graphics.cpp
@@ -62,8 +62,7 @@ void GraphicsWindow::rearrange(const Rect &box) {
 	if (newhgt < bothhgt)
 		bothhgt = newhgt;
 
-	newSurface = new Graphics::ManagedSurface(newwid, newhgt,
-	        Graphics::PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0));
+	newSurface = new Graphics::ManagedSurface(newwid, newhgt, g_system->getScreenFormat());
 
 	// If the new surface is equal or bigger than the old one, copy it over
 	if (_surface && bothwid && bothhgt)
@@ -231,7 +230,7 @@ void GraphicsWindow::drawPicture(Picture *src,  int x0, int y0, int width, int h
 	w = sx1 - sx0;
 	h = sy1 - sy0;
 
-	_surface->blitFrom(*g_vm->_screen, Rect(sx0, sy0, sx0 + w, sy0 + h), Point(0, 0));
+	_surface->blitFrom(*src, Rect(sx0, sy0, sx0 + w, sy0 + h), Point(0, 0));
 }
 
 void GraphicsWindow::getSize(glui32 *width, glui32 *height) const {


Commit: c2625264fb4e0a0abde96257ea86c83dde60fad2
    https://github.com/scummvm/scummvm/commit/c2625264fb4e0a0abde96257ea86c83dde60fad2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Change int 0 to false

Changed paths:
    engines/glk/windows.cpp


diff --git a/engines/glk/windows.cpp b/engines/glk/windows.cpp
index 1e53f25..58a1648 100644
--- a/engines/glk/windows.cpp
+++ b/engines/glk/windows.cpp
@@ -411,7 +411,7 @@ void Windows::redraw() {
 	if (_moreFocus)
 		refocus(_focusWin);
 
-	_forceRedraw = 0;
+	_forceRedraw = false;
 }
 
 void Windows::redrawRect(const Rect &r) {


Commit: 5cc3d40c2831ef9d0760178b091e668f5b46195f
    https://github.com/scummvm/scummvm/commit/5cc3d40c2831ef9d0760178b091e668f5b46195f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Adding bitmap font class for Infocom character graphics

Changed paths:
  A engines/glk/frotz/bitmap_font.cpp
  A engines/glk/frotz/bitmap_font.h
    engines/glk/fonts.cpp
    engines/glk/frotz/frotz_types.h
    engines/glk/frotz/processor_screen.cpp
    engines/glk/module.mk
    engines/glk/picture.cpp


diff --git a/engines/glk/fonts.cpp b/engines/glk/fonts.cpp
index bc699af..4164966 100644
--- a/engines/glk/fonts.cpp
+++ b/engines/glk/fonts.cpp
@@ -24,10 +24,12 @@
 #include "glk/glk_types.h"
 #include "glk/conf.h"
 #include "glk/glk.h"
+#include "glk/frotz/bitmap_font.h"
 #include "common/memstream.h"
 #include "common/unzip.h"
 #include "graphics/fonts/ttf.h"
 #include "graphics/fontman.h"
+#include "image/bmp.h"
 
 namespace Glk {
 
@@ -100,16 +102,27 @@ bool Fonts::loadFonts() {
 
 const Graphics::Font *Fonts::loadFont(FACES face, Common::Archive *archive, double size, double aspect, int
  style) {
+	Common::File f;
 	const char *const FILENAMES[8] = {
 		"GoMono-Regular.ttf", "GoMono-Bold.ttf", "GoMono-Italic.ttf", "GoMono-Bold-Italic.ttf",
 		"NotoSerif-Regular.ttf", "NotoSerif-Bold.ttf", "NotoSerif-Italic.ttf", "NotoSerif-Bold-Italic.ttf"
 	};
 	
-	Common::File f;
-	if (!f.open(FILENAMES[face], *archive))
-		error("Could not load font");
+	// TODO: Properly create a derived Fonts manager for the Frotz sub-engine
+	if (face == MONOZ && g_vm->getInterpreterType() == INTERPRETER_FROTZ) {
+		if (!f.open("infocom_graphics.bmp", *archive))
+			error("Could not load font");
+
+		Image::BitmapDecoder decoder;
+		decoder.loadStream(f);
+		return new Frotz::BitmapFont(*decoder.getSurface());
 
-	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
+	} else {
+		if (!f.open(FILENAMES[face], *archive))
+			error("Could not load font");
+
+		return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
+	}
 }
 
 FACES Fonts::getId(const Common::String &name) {
diff --git a/engines/glk/frotz/bitmap_font.cpp b/engines/glk/frotz/bitmap_font.cpp
new file mode 100644
index 0000000..91f44a9
--- /dev/null
+++ b/engines/glk/frotz/bitmap_font.cpp
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/frotz/bitmap_font.h"
+
+namespace Glk {
+namespace Frotz {
+
+BitmapFont::BitmapFont(const Graphics::Surface &src, uint charWidth,
+		uint charHeight, unsigned char startingChar) : _startingChar(startingChar) {
+	assert(src.format.bytesPerPixel == 1);
+	assert((src.w % charWidth) == 0);
+	assert((src.h % charHeight) == 0);
+	_surface.copyFrom(src);
+
+	Common::Rect r(charWidth, charHeight);
+	for (uint y = 0; y < src.h; y += charHeight) {
+		r.moveTo(0, y);
+		for (uint x = 0; x < src.w; x += charWidth, r.translate(charWidth, 0))
+			_chars.push_back(r);
+	}
+}
+
+BitmapFont::~BitmapFont() {
+	_surface.free();
+}
+
+void BitmapFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+	const Common::Rect &r = _chars[chr - _startingChar];
+	for (int yCtr = 0; yCtr < r.height(); ++yCtr) {
+		const byte *srcP = (const byte *)_surface.getBasePtr(r.left, r.top + yCtr);
+
+		for (int xCtr = 0; xCtr < r.width(); ++xCtr, ++srcP) {
+			if (*srcP)
+				dst->hLine(x + xCtr, y + yCtr, x + xCtr, color);
+		}
+	}
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/frotz/bitmap_font.h b/engines/glk/frotz/bitmap_font.h
new file mode 100644
index 0000000..dac716c
--- /dev/null
+++ b/engines/glk/frotz/bitmap_font.h
@@ -0,0 +1,81 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_FROTZ_FONTS
+#define GLK_FROTZ_FONTS
+
+#include "graphics/font.h"
+#include "graphics/surface.h"
+#include "common/archive.h"
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Glk {
+namespace Frotz {
+
+/**
+ * Implements a fixed width font stored as a grid on a passed surface
+ */
+class BitmapFont : public Graphics::Font {
+private:
+	Graphics::Surface _surface;
+	Common::Array<Common::Rect> _chars;
+	size_t _startingChar;
+public:
+	/**
+	 * Constructor
+	 */
+	BitmapFont(const Graphics::Surface &src, uint charWidth = 8, uint charHeight = 8,
+		unsigned char startingChar = ' ');
+	
+	/**
+	 * Destructor
+	 */
+	~BitmapFont();
+
+	/**
+	 * Get the font height
+	 */
+	virtual int getFontHeight() const override { return _chars[0].height(); }
+
+	/**
+	 * Get the maximum character width
+	 */
+	virtual int getMaxCharWidth() const override { return _chars[0].width(); }
+
+	/**
+	 * Get the width of the given character
+	 */
+	virtual int getCharWidth(uint32 chr) const override {
+		return _chars[chr - _startingChar].width();
+	}
+
+	/**
+	 * Draw a character
+	 */
+	virtual void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/frotz/frotz_types.h b/engines/glk/frotz/frotz_types.h
index bf3067f..555a204 100644
--- a/engines/glk/frotz/frotz_types.h
+++ b/engines/glk/frotz/frotz_types.h
@@ -183,6 +183,7 @@ enum Style {
 };
 
 enum FontStyle {
+	PREVIOUS_FONT    = 0,
 	TEXT_FONT        = 1,
 	PICTURE_FONT     = 2,
 	GRAPHICS_FONT    = 3,
diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index 6ee7f1d..696ce78 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -302,39 +302,30 @@ void Processor::z_set_font() {
 	zword font = zargs[0];
 
 	switch (font) {
-		case 0:
-			// previous font
-			temp_font = curr_font;
-			curr_font = prev_font;
-			prev_font = temp_font;
-			zargs[0] = 0xf000;	// tickle tickle!
-			z_set_text_style();
-			store (curr_font);
-			break;
-
-		case 1:
-			// normal font
-			prev_font = curr_font;
-			curr_font = 1;
-			zargs[0] = 0xf000;	// tickle tickle!
-			z_set_text_style();
-			store (prev_font);
-			break; 
-
-		case 4:
-			// fixed-pitch font
-			prev_font = curr_font;
-			curr_font = 4;
-			zargs[0] = 0xf000;	// tickle tickle!
-			z_set_text_style();
-			store (prev_font);
-			break;
-
-		case 2: // picture font, undefined per 1.1
-		case 3: // character graphics font
-		default: // unavailable
-			store (0);
-			break;
+	case PREVIOUS_FONT:
+		// previous font
+		temp_font = curr_font;
+		curr_font = prev_font;
+		prev_font = temp_font;
+		zargs[0] = 0xf000;	// tickle tickle!
+		z_set_text_style();
+		store(curr_font);
+		break;
+
+	case TEXT_FONT:
+	case GRAPHICS_FONT:
+	case FIXED_WIDTH_FONT:
+		prev_font = curr_font;
+		curr_font = font;
+		zargs[0] = 0xf000;	// tickle tickle!
+		z_set_text_style();
+		store(prev_font);
+		break;
+
+	case PICTURE_FONT: // picture font, undefined per 1.1
+	default:           // unavailable
+		store(0);
+		break;
 	}
 }
 
@@ -361,7 +352,7 @@ void Processor::z_set_text_style() {
 		// not tickle time
 		curstyle |= zargs[0];
 
-	if (h_flags & FIXED_FONT_FLAG || curr_font == 4)
+	if (h_flags & FIXED_FONT_FLAG || curr_font == FIXED_WIDTH_FONT)
 		style = curstyle | FIXED_WIDTH_STYLE;
 	else
 		style = curstyle;
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 0206643..7c7a85b 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS := \
 	window_pair.o \
 	window_text_buffer.o \
 	window_text_grid.o \
+	frotz/bitmap_font.o \
 	frotz/config.o \
 	frotz/detection.o \
 	frotz/frotz.o \
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 3404315..4422e46 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -106,7 +106,7 @@ Picture *Pictures::load(uint32 id) {
 	RawDecoder raw;
 	const Graphics::Surface *img;
 	const byte *palette = nullptr;
-	int palCount = 0;
+	uint palCount = 0;
 	Picture *pic;
 
 	// Check if the picture is already in the store


Commit: fb7dbffd59b797ed00785a2d3762355f9dbb9f7d
    https://github.com/scummvm/scummvm/commit/fb7dbffd59b797ed00785a2d3762355f9dbb9f7d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Merge Fonts class into Screen class

Changed paths:
  A engines/glk/frotz/screen.cpp
  A engines/glk/frotz/screen.h
  R engines/glk/fonts.cpp
  R engines/glk/fonts.h
  R engines/glk/frotz/bitmap_font.cpp
  R engines/glk/frotz/bitmap_font.h
    engines/glk/conf.cpp
    engines/glk/conf.h
    engines/glk/module.mk
    engines/glk/screen.cpp
    engines/glk/screen.h
    engines/glk/windows.h


diff --git a/engines/glk/conf.cpp b/engines/glk/conf.cpp
index 4775f26..ee2fd9b 100644
--- a/engines/glk/conf.cpp
+++ b/engines/glk/conf.cpp
@@ -21,7 +21,6 @@
  */
 
 #include "glk/conf.h"
-#include "glk/fonts.h"
 #include "glk/utils.h"
 #include "glk/windows.h"
 #include "common/config-manager.h"
@@ -197,9 +196,9 @@ Conf::Conf(InterpreterType interpType) {
 			char *font = strtok(buffer, "\r\n\t ");
 
 			if (tg == 0)
-				_tStyles[style].font = Fonts::getId(font);
+				_tStyles[style].font = Screen::getFontId(font);
 			else
-				_gStyles[style].font = Fonts::getId(font);
+				_gStyles[style].font = Screen::getFontId(font);
 		}
 	}
 
@@ -231,7 +230,7 @@ void Conf::get(const Common::String &key, bool &field, bool defaultVal) {
 }
 
 void Conf::get(const Common::String &key, FACES &field, FACES defaultFont) {
-	field = ConfMan.hasKey(key) ? Fonts::getId(ConfMan.get(key)) : defaultFont;
+	field = ConfMan.hasKey(key) ? Screen::getFontId(ConfMan.get(key)) : defaultFont;
 }
 
 void Conf::get(const Common::String &key, double &field, double defaultVal) {
diff --git a/engines/glk/conf.h b/engines/glk/conf.h
index bf68e34..ca9de2e 100644
--- a/engines/glk/conf.h
+++ b/engines/glk/conf.h
@@ -24,13 +24,10 @@
 #define GLK_CONF_H
 
 #include "glk/glk_types.h"
-#include "glk/fonts.h"
 #include "glk/windows.h"
 
 namespace Glk {
 
-
-
 /**
  * Engine configuration
  */
diff --git a/engines/glk/fonts.cpp b/engines/glk/fonts.cpp
deleted file mode 100644
index 4164966..0000000
--- a/engines/glk/fonts.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software{} you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation{} either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY{} without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program{} if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "glk/fonts.h"
-#include "glk/glk_types.h"
-#include "glk/conf.h"
-#include "glk/glk.h"
-#include "glk/frotz/bitmap_font.h"
-#include "common/memstream.h"
-#include "common/unzip.h"
-#include "graphics/fonts/ttf.h"
-#include "graphics/fontman.h"
-#include "image/bmp.h"
-
-namespace Glk {
-
-#define FONTS_VERSION 1.0
-#define FONTS_FILENAME "fonts.dat"
-
-Fonts::Fonts(Graphics::ManagedSurface *surface) : _surface(surface), _fontsMissing(false) {
-	if (!loadFonts())
-		error("Could not load data file");
-
-	// TODO: See if there's any better way for getting the leading and baseline
-	Common::Rect r1 = _fontTable[7]->getBoundingBox('o');
-	Common::Rect r2 = _fontTable[7]->getBoundingBox('y');
-	double baseLine = (double)r1.bottom;
-	double leading = (double)r2.bottom + 2;
-
-	g_conf->_leading = MAX((double)g_conf->_leading, leading);
-	g_conf->_baseLine = MAX((double)g_conf->_baseLine, baseLine);
-	g_conf->_cellW = _fontTable[0]->getStringWidth("0");
-	g_conf->_cellH = g_conf->_leading;
-}
-
-Fonts::~Fonts() {
-	for (int idx = 0; idx < FONTS_TOTAL; ++idx)
-		delete _fontTable[idx];
-}
-
-bool Fonts::loadFonts() {
-	Common::Archive *archive = nullptr;
-
-	if (!Common::File::exists(FONTS_FILENAME) || (archive = Common::makeZipArchive(FONTS_FILENAME)) == nullptr)
-		return false;
-
-	// Open the version.txt file within it to validate the version
-	Common::File f;
-	if (!f.open("version.txt", *archive)) {
-		delete archive;
-		return false;
-	}
-
-	// Validate the version
-	char buffer[4];
-	f.read(buffer, 3);
-	buffer[3] = '\0';
-
-	if (Common::String(buffer) != "1.0") {
-		delete archive;
-		return false;
-	}
-
-	// R ead in the fonts
-	double monoAspect = g_conf->_monoAspect;
-	double propAspect = g_conf->_propAspect;
-	double monoSize = g_conf->_monoSize;
-	double propSize = g_conf->_propSize;
-
-	_fontTable[0] = loadFont(MONOR, archive, monoSize, monoAspect, FONTR);
-	_fontTable[1] = loadFont(MONOB, archive, monoSize, monoAspect, FONTB);
-	_fontTable[2] = loadFont(MONOI, archive, monoSize, monoAspect, FONTI);
-	_fontTable[3] = loadFont(MONOZ, archive, monoSize, monoAspect, FONTZ);
-
-	_fontTable[4] = loadFont(PROPR, archive, propSize, propAspect, FONTR);
-	_fontTable[5] = loadFont(PROPB, archive, propSize, propAspect, FONTB);
-	_fontTable[6] = loadFont(PROPI, archive, propSize, propAspect, FONTI);
-	_fontTable[7] = loadFont(PROPZ, archive, propSize, propAspect, FONTZ);
-
-	delete archive;
-	return true;
-}
-
-const Graphics::Font *Fonts::loadFont(FACES face, Common::Archive *archive, double size, double aspect, int
- style) {
-	Common::File f;
-	const char *const FILENAMES[8] = {
-		"GoMono-Regular.ttf", "GoMono-Bold.ttf", "GoMono-Italic.ttf", "GoMono-Bold-Italic.ttf",
-		"NotoSerif-Regular.ttf", "NotoSerif-Bold.ttf", "NotoSerif-Italic.ttf", "NotoSerif-Bold-Italic.ttf"
-	};
-	
-	// TODO: Properly create a derived Fonts manager for the Frotz sub-engine
-	if (face == MONOZ && g_vm->getInterpreterType() == INTERPRETER_FROTZ) {
-		if (!f.open("infocom_graphics.bmp", *archive))
-			error("Could not load font");
-
-		Image::BitmapDecoder decoder;
-		decoder.loadStream(f);
-		return new Frotz::BitmapFont(*decoder.getSurface());
-
-	} else {
-		if (!f.open(FILENAMES[face], *archive))
-			error("Could not load font");
-
-		return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
-	}
-}
-
-FACES Fonts::getId(const Common::String &name) {
-	if (name == "monor") return MONOR;
-	if (name == "monob") return MONOB;
-	if (name == "monoi") return MONOI;
-	if (name == "monoz") return MONOZ;
-	if (name == "propr") return PROPR;
-	if (name == "propb") return PROPB;
-	if (name == "propi") return PROPI;
-	if (name == "propz") return PROPZ;
-	return MONOR;
-}
-
-int Fonts::drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
-	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
-	const Graphics::Font *font = _fontTable[fontIdx];
-	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
-	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
-
-	pt.x += font->getStringWidth(text);
-	return MIN((int)pt.x, (int)_surface->w) * GLI_SUBPIX;
-}
-
-int Fonts::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
-	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
-	const Graphics::Font *font = _fontTable[fontIdx];
-	const uint32 color = _surface->format.RGBToColor(rgb[0], rgb[1], rgb[2]);
-	font->drawString(_surface, text, pt.x, pt.y, _surface->w - pt.x, color);
-
-	pt.x += font->getStringWidth(text);
-	return MIN((int)pt.x, (int)_surface->w) * GLI_SUBPIX;
-}
-
-size_t Fonts::stringWidth(int fontIdx, const Common::String &text, int spw) {
-	const Graphics::Font *font = _fontTable[fontIdx];
-	return font->getStringWidth(text) * GLI_SUBPIX;
-}
-
-size_t Fonts::stringWidthUni(int fontIdx, const Common::U32String &text, int spw) {
-	const Graphics::Font *font = _fontTable[fontIdx];
-	return font->getStringWidth(text) * GLI_SUBPIX;
-}
-
-} // End of namespace Glk
diff --git a/engines/glk/fonts.h b/engines/glk/fonts.h
deleted file mode 100644
index 364b023..0000000
--- a/engines/glk/fonts.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GLK_FONTS_H
-#define GLK_FONTS_H
-
-#include "glk/glk_types.h"
-#include "glk/utils.h"
-#include "common/archive.h"
-#include "common/array.h"
-#include "common/file.h"
-#include "common/str.h"
-#include "common/ustr.h"
-#include "graphics/font.h"
-
-namespace Glk {
-
-#define FONTS_TOTAL 8
-
-enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
-enum TYPES { MONOF, PROPF };
-enum STYLES { FONTR, FONTB, FONTI, FONTZ };
-
-/**
- * Fonts manager
- */
-class Fonts {
-private:
-	Graphics::ManagedSurface *_surface;
-	const Graphics::Font *_fontTable[FONTS_TOTAL];
-	bool _fontsMissing;
-private:
-	/**
-	 * Load all the fonts
-	 */
-	bool loadFonts();
-
-	/**
-	 * Load a single font
-	 */
-	const Graphics::Font *loadFont(FACES face, Common::Archive *archive, double size, double aspect, int style);
-public:
-	/**
-	 * Get the index/id of a font by name
-	 */
-	static FACES getId(const Common::String &name);
-public:
-	/**
-	 * Constructor
-	 */
-	Fonts(Graphics::ManagedSurface *surface);
-
-	/**
-	 * Destructor
-	 */
-	virtual ~Fonts();
-
-	/**
-	 * Draws a string using the specified font at the given co-ordinates
-	 * @param pos       Position for the bottom-left corner the text will be drawn with
-	 * @param fontIdx   Which font to use
-	 * @param rgb       RGB tuplet specifying the text color
-	 * @param text      The text to draw
-	 * @param spw       ??
-	 */
-	int drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
-
-	/**
-	 * Draws a unicode string using the specified font at the given co-ordinates
-	 * @param pos       Position for the bottom-left corner the text will be drawn with
-	 * @param fontIdx   Which font to use
-	 * @param rgb       RGB tuplet specifying the text color
-	 * @param text      The text to draw
-	 * @param spw       ??
-	 */
-	int drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
-
-	/**
-	 * Get the width in pixels of a string
-	 * @param fontIdx   Which font to use
-	 * @param text      Text to get the width of
-	 * @param spw       Delta X
-	 * @returns         Width of string multiplied by GLI_SUBPIX
-	 */
-	size_t stringWidth(int fontIdx, const Common::String &text, int spw = 0);
-
-	/**
-	 * Get the width in pixels of a unicode string
-	 * @param fontIdx   Which font to use
-	 * @param text      Text to get the width of
-	 * @param spw       Delta X
-	 * @returns         Width of string multiplied by GLI_SUBPIX
-	 */
-	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = 0);
-};
-
-} // End of namespace Glk
-
-#endif
diff --git a/engines/glk/frotz/bitmap_font.cpp b/engines/glk/frotz/bitmap_font.cpp
deleted file mode 100644
index 91f44a9..0000000
--- a/engines/glk/frotz/bitmap_font.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "glk/frotz/bitmap_font.h"
-
-namespace Glk {
-namespace Frotz {
-
-BitmapFont::BitmapFont(const Graphics::Surface &src, uint charWidth,
-		uint charHeight, unsigned char startingChar) : _startingChar(startingChar) {
-	assert(src.format.bytesPerPixel == 1);
-	assert((src.w % charWidth) == 0);
-	assert((src.h % charHeight) == 0);
-	_surface.copyFrom(src);
-
-	Common::Rect r(charWidth, charHeight);
-	for (uint y = 0; y < src.h; y += charHeight) {
-		r.moveTo(0, y);
-		for (uint x = 0; x < src.w; x += charWidth, r.translate(charWidth, 0))
-			_chars.push_back(r);
-	}
-}
-
-BitmapFont::~BitmapFont() {
-	_surface.free();
-}
-
-void BitmapFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
-	const Common::Rect &r = _chars[chr - _startingChar];
-	for (int yCtr = 0; yCtr < r.height(); ++yCtr) {
-		const byte *srcP = (const byte *)_surface.getBasePtr(r.left, r.top + yCtr);
-
-		for (int xCtr = 0; xCtr < r.width(); ++xCtr, ++srcP) {
-			if (*srcP)
-				dst->hLine(x + xCtr, y + yCtr, x + xCtr, color);
-		}
-	}
-}
-
-} // End of namespace Scott
-} // End of namespace Glk
diff --git a/engines/glk/frotz/bitmap_font.h b/engines/glk/frotz/bitmap_font.h
deleted file mode 100644
index dac716c..0000000
--- a/engines/glk/frotz/bitmap_font.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef GLK_FROTZ_FONTS
-#define GLK_FROTZ_FONTS
-
-#include "graphics/font.h"
-#include "graphics/surface.h"
-#include "common/archive.h"
-#include "common/array.h"
-#include "common/rect.h"
-
-namespace Glk {
-namespace Frotz {
-
-/**
- * Implements a fixed width font stored as a grid on a passed surface
- */
-class BitmapFont : public Graphics::Font {
-private:
-	Graphics::Surface _surface;
-	Common::Array<Common::Rect> _chars;
-	size_t _startingChar;
-public:
-	/**
-	 * Constructor
-	 */
-	BitmapFont(const Graphics::Surface &src, uint charWidth = 8, uint charHeight = 8,
-		unsigned char startingChar = ' ');
-	
-	/**
-	 * Destructor
-	 */
-	~BitmapFont();
-
-	/**
-	 * Get the font height
-	 */
-	virtual int getFontHeight() const override { return _chars[0].height(); }
-
-	/**
-	 * Get the maximum character width
-	 */
-	virtual int getMaxCharWidth() const override { return _chars[0].width(); }
-
-	/**
-	 * Get the width of the given character
-	 */
-	virtual int getCharWidth(uint32 chr) const override {
-		return _chars[chr - _startingChar].width();
-	}
-
-	/**
-	 * Draw a character
-	 */
-	virtual void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
-};
-
-} // End of namespace Frotz
-} // End of namespace Glk
-
-#endif
diff --git a/engines/glk/frotz/screen.cpp b/engines/glk/frotz/screen.cpp
new file mode 100644
index 0000000..eef6d61
--- /dev/null
+++ b/engines/glk/frotz/screen.cpp
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/frotz/screen.h"
+
+namespace Glk {
+namespace Frotz {
+
+BitmapFont::BitmapFont(const Graphics::Surface &src, uint charWidth,
+		uint charHeight, unsigned char startingChar) : _startingChar(startingChar) {
+	assert(src.format.bytesPerPixel == 1);
+	assert((src.w % charWidth) == 0);
+	assert((src.h % charHeight) == 0);
+	_surface.copyFrom(src);
+
+	Common::Rect r(charWidth, charHeight);
+	for (uint y = 0; y < src.h; y += charHeight) {
+		r.moveTo(0, y);
+		for (uint x = 0; x < src.w; x += charWidth, r.translate(charWidth, 0))
+			_chars.push_back(r);
+	}
+}
+
+BitmapFont::~BitmapFont() {
+	_surface.free();
+}
+
+void BitmapFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
+	const Common::Rect &r = _chars[chr - _startingChar];
+	for (int yCtr = 0; yCtr < r.height(); ++yCtr) {
+		const byte *srcP = (const byte *)_surface.getBasePtr(r.left, r.top + yCtr);
+
+		for (int xCtr = 0; xCtr < r.width(); ++xCtr, ++srcP) {
+			if (*srcP)
+				dst->hLine(x + xCtr, y + yCtr, x + xCtr, color);
+		}
+	}
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/frotz/screen.h b/engines/glk/frotz/screen.h
new file mode 100644
index 0000000..dac716c
--- /dev/null
+++ b/engines/glk/frotz/screen.h
@@ -0,0 +1,81 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_FROTZ_FONTS
+#define GLK_FROTZ_FONTS
+
+#include "graphics/font.h"
+#include "graphics/surface.h"
+#include "common/archive.h"
+#include "common/array.h"
+#include "common/rect.h"
+
+namespace Glk {
+namespace Frotz {
+
+/**
+ * Implements a fixed width font stored as a grid on a passed surface
+ */
+class BitmapFont : public Graphics::Font {
+private:
+	Graphics::Surface _surface;
+	Common::Array<Common::Rect> _chars;
+	size_t _startingChar;
+public:
+	/**
+	 * Constructor
+	 */
+	BitmapFont(const Graphics::Surface &src, uint charWidth = 8, uint charHeight = 8,
+		unsigned char startingChar = ' ');
+	
+	/**
+	 * Destructor
+	 */
+	~BitmapFont();
+
+	/**
+	 * Get the font height
+	 */
+	virtual int getFontHeight() const override { return _chars[0].height(); }
+
+	/**
+	 * Get the maximum character width
+	 */
+	virtual int getMaxCharWidth() const override { return _chars[0].width(); }
+
+	/**
+	 * Get the width of the given character
+	 */
+	virtual int getCharWidth(uint32 chr) const override {
+		return _chars[chr - _startingChar].width();
+	}
+
+	/**
+	 * Draw a character
+	 */
+	virtual void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 7c7a85b..4112a9f 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -5,7 +5,6 @@ MODULE_OBJS := \
 	conf.o \
 	detection.o \
 	events.o \
-	fonts.o \
 	glk.o \
 	glk_api.o \
 	picture.o \
@@ -22,7 +21,6 @@ MODULE_OBJS := \
 	window_pair.o \
 	window_text_buffer.o \
 	window_text_grid.o \
-	frotz/bitmap_font.o \
 	frotz/config.o \
 	frotz/detection.o \
 	frotz/frotz.o \
@@ -42,6 +40,7 @@ MODULE_OBJS := \
 	frotz/processor_text.o \
 	frotz/processor_variables.o \
 	frotz/quetzal.o \
+	frotz/screen.o \
 	scott/detection.o \
 	scott/scott.o
 
diff --git a/engines/glk/screen.cpp b/engines/glk/screen.cpp
index 1a76ac8..cfe8cde 100644
--- a/engines/glk/screen.cpp
+++ b/engines/glk/screen.cpp
@@ -22,9 +22,38 @@
 
 #include "glk/screen.h"
 #include "glk/conf.h"
+#include "common/unzip.h"
+#include "image/bmp.h"
+#include "graphics/fonts/ttf.h"
+#include "graphics/fontman.h"
 
 namespace Glk {
 
+
+#define FONTS_VERSION 1.0
+#define FONTS_FILENAME "fonts.dat"
+
+Screen::Screen() {
+	if (!loadFonts())
+		error("Could not load data file");
+
+	// TODO: See if there's any better way for getting the leading and baseline
+	Common::Rect r1 = _fonts[7]->getBoundingBox('o');
+	Common::Rect r2 = _fonts[7]->getBoundingBox('y');
+	double baseLine = (double)r1.bottom;
+	double leading = (double)r2.bottom + 2;
+
+	g_conf->_leading = MAX((double)g_conf->_leading, leading);
+	g_conf->_baseLine = MAX((double)g_conf->_baseLine, baseLine);
+	g_conf->_cellW = _fonts[0]->getStringWidth("0");
+	g_conf->_cellH = g_conf->_leading;
+}
+
+Screen::~Screen() {
+	for (int idx = 0; idx < FONTS_TOTAL; ++idx)
+		delete _fonts[idx];
+}
+
 void Screen::fill(const byte *rgb) {
 	uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	clear(color);
@@ -69,4 +98,104 @@ void Screen::drawCaret(const Point &pos) {
 	}
 }
 
+bool Screen::loadFonts() {
+	Common::Archive *archive = nullptr;
+	_fonts.resize(FONTS_TOTAL);
+
+	if (!Common::File::exists(FONTS_FILENAME) || (archive = Common::makeZipArchive(FONTS_FILENAME)) == nullptr)
+		return false;
+
+	// Open the version.txt file within it to validate the version
+	Common::File f;
+	if (!f.open("version.txt", *archive)) {
+		delete archive;
+		return false;
+	}
+
+	// Validate the version
+	char buffer[4];
+	f.read(buffer, 3);
+	buffer[3] = '\0';
+
+	if (Common::String(buffer) != "1.0") {
+		delete archive;
+		return false;
+	}
+
+	// R ead in the fonts
+	double monoAspect = g_conf->_monoAspect;
+	double propAspect = g_conf->_propAspect;
+	double monoSize = g_conf->_monoSize;
+	double propSize = g_conf->_propSize;
+
+	_fonts[0] = loadFont(MONOR, archive, monoSize, monoAspect, FONTR);
+	_fonts[1] = loadFont(MONOB, archive, monoSize, monoAspect, FONTB);
+	_fonts[2] = loadFont(MONOI, archive, monoSize, monoAspect, FONTI);
+	_fonts[3] = loadFont(MONOZ, archive, monoSize, monoAspect, FONTZ);
+
+	_fonts[4] = loadFont(PROPR, archive, propSize, propAspect, FONTR);
+	_fonts[5] = loadFont(PROPB, archive, propSize, propAspect, FONTB);
+	_fonts[6] = loadFont(PROPI, archive, propSize, propAspect, FONTI);
+	_fonts[7] = loadFont(PROPZ, archive, propSize, propAspect, FONTZ);
+
+	delete archive;
+	return true;
+}
+
+const Graphics::Font *Screen::loadFont(FACES face, Common::Archive *archive, double size, double aspect, int
+ style) {
+	Common::File f;
+	const char *const FILENAMES[8] = {
+		"GoMono-Regular.ttf", "GoMono-Bold.ttf", "GoMono-Italic.ttf", "GoMono-Bold-Italic.ttf",
+		"NotoSerif-Regular.ttf", "NotoSerif-Bold.ttf", "NotoSerif-Italic.ttf", "NotoSerif-Bold-Italic.ttf"
+	};
+
+	if (!f.open(FILENAMES[face], *archive))
+		error("Could not load font");
+
+	return Graphics::loadTTFFont(f, size, Graphics::kTTFSizeModeCharacter);
+}
+
+FACES Screen::getFontId(const Common::String &name) {
+	if (name == "monor") return MONOR;
+	if (name == "monob") return MONOB;
+	if (name == "monoi") return MONOI;
+	if (name == "monoz") return MONOZ;
+	if (name == "propr") return PROPR;
+	if (name == "propb") return PROPB;
+	if (name == "propi") return PROPI;
+	if (name == "propz") return PROPZ;
+	return MONOR;
+}
+
+int Screen::drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw) {
+	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
+	const Graphics::Font *font = _fonts[fontIdx];
+	const uint32 color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+	font->drawString(this, text, pt.x, pt.y, w - pt.x, color);
+
+	pt.x += font->getStringWidth(text);
+	return MIN((int)pt.x, (int)w) * GLI_SUBPIX;
+}
+
+int Screen::drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw) {
+	Point pt(pos.x / GLI_SUBPIX, pos.y - g_conf->_baseLine);
+	const Graphics::Font *font = _fonts[fontIdx];
+	const uint32 color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
+	font->drawString(this, text, pt.x, pt.y, w - pt.x, color);
+
+	pt.x += font->getStringWidth(text);
+	return MIN((int)pt.x, (int)w) * GLI_SUBPIX;
+}
+
+size_t Screen::stringWidth(int fontIdx, const Common::String &text, int spw) {
+	const Graphics::Font *font = _fonts[fontIdx];
+	return font->getStringWidth(text) * GLI_SUBPIX;
+}
+
+size_t Screen::stringWidthUni(int fontIdx, const Common::U32String &text, int spw) {
+	const Graphics::Font *font = _fonts[fontIdx];
+	return font->getStringWidth(text) * GLI_SUBPIX;
+}
+
 } // End of namespace Glk
diff --git a/engines/glk/screen.h b/engines/glk/screen.h
index 0020d86..541e9e1 100644
--- a/engines/glk/screen.h
+++ b/engines/glk/screen.h
@@ -23,24 +23,56 @@
 #ifndef GLK_DRAW_H
 #define GLK_DRAW_H
 
+#include "common/archive.h"
+#include "common/array.h"
 #include "graphics/screen.h"
-#include "glk/fonts.h"
+#include "graphics/font.h"
+#include "glk/utils.h"
 
 namespace Glk {
 
+#define FONTS_TOTAL 8
+
 enum CaretShape {
 	SMALL_DOT = 0, FAT_DOT = 1, THIN_LINE = 2, FAT_LINE = 3, BLOCK = 4
 };
 
+enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
+enum TYPES { MONOF, PROPF };
+enum STYLES { FONTR, FONTB, FONTI, FONTZ };
+
 /**
  * Screen surface class
  */
-class Screen : public Graphics::Screen, public Fonts {
+class Screen : public Graphics::Screen {
+private:
+	Common::Array<const Graphics::Font *> _fonts;
+private:
+	/**
+	 * Load all the fonts
+	 */
+	bool loadFonts();
+
+	/**
+	 * Load a single font
+	 */
+	const Graphics::Font *loadFont(FACES face, Common::Archive *archive,
+		double size, double aspect, int style);
+public:
+	/**
+	 * Return the font Id for a given name
+	 */
+	static FACES getFontId(const Common::String &name);
 public:
 	/**
 	 * Constructor
 	 */
-	Screen() : Graphics::Screen(), Fonts(this) {}
+	Screen();
+
+	/**
+	 * Destructor
+	 */
+	virtual ~Screen();
 
 	/**
 	 * Fills the screen with a given rgb color
@@ -58,6 +90,44 @@ public:
 	 *      and the X position is in multiples of GLI_SUBPIX
 	 */
 	void drawCaret(const Point &pos);
+
+	/**
+	 * Draws a string using the specified font at the given co-ordinates
+	 * @param pos       Position for the bottom-left corner the text will be drawn with
+	 * @param fontIdx   Which font to use
+	 * @param rgb       RGB tuplet specifying the text color
+	 * @param text      The text to draw
+	 * @param spw       ??
+	 */
+	int drawString(const Point &pos, int fontIdx, const byte *rgb, const Common::String &text, int spw = 0);
+
+	/**
+	 * Draws a unicode string using the specified font at the given co-ordinates
+	 * @param pos       Position for the bottom-left corner the text will be drawn with
+	 * @param fontIdx   Which font to use
+	 * @param rgb       RGB tuplet specifying the text color
+	 * @param text      The text to draw
+	 * @param spw       ??
+	 */
+	int drawStringUni(const Point &pos, int fontIdx, const byte *rgb, const Common::U32String &text, int spw = 0);
+
+	/**
+	 * Get the width in pixels of a string
+	 * @param fontIdx   Which font to use
+	 * @param text      Text to get the width of
+	 * @param spw       Delta X
+	 * @returns         Width of string multiplied by GLI_SUBPIX
+	 */
+	size_t stringWidth(int fontIdx, const Common::String &text, int spw = 0);
+
+	/**
+	 * Get the width in pixels of a unicode string
+	 * @param fontIdx   Which font to use
+	 * @param text      Text to get the width of
+	 * @param spw       Delta X
+	 * @returns         Width of string multiplied by GLI_SUBPIX
+	 */
+	size_t stringWidthUni(int fontIdx, const Common::U32String &text, int spw = 0);
 };
 
 } // End of namespace Glk
diff --git a/engines/glk/windows.h b/engines/glk/windows.h
index 7abb9e5..27380fa 100644
--- a/engines/glk/windows.h
+++ b/engines/glk/windows.h
@@ -29,7 +29,7 @@
 #include "graphics/screen.h"
 #include "glk/events.h"
 #include "glk/glk_types.h"
-#include "glk/fonts.h"
+#include "glk/screen.h"
 #include "glk/selection.h"
 #include "glk/streams.h"
 


Commit: 3a543a1e6d35a5bcab61a8fb4257b7d567d9e278
    https://github.com/scummvm/scummvm/commit/3a543a1e6d35a5bcab61a8fb4257b7d567d9e278
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Add derived Screen class to add Infocom character graphics font

Changed paths:
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/frotz.h
    engines/glk/frotz/pics_decoder.cpp
    engines/glk/frotz/screen.cpp
    engines/glk/frotz/screen.h
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/screen.cpp
    engines/glk/screen.h


diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index d3b0749..421b778 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -22,6 +22,7 @@
 
 #include "glk/frotz/frotz.h"
 #include "glk/frotz/frotz_types.h"
+#include "glk/frotz/screen.h"
 #include "common/config-manager.h"
 
 namespace Glk {
@@ -38,6 +39,10 @@ Frotz::~Frotz() {
 	reset_memory();
 }
 
+Screen *Frotz::createScreen() {
+	return new FrotzScreen();
+}
+
 void Frotz::runGame(Common::SeekableReadStream *gameFile) {
 	story_fp = gameFile;
 	initialize();
diff --git a/engines/glk/frotz/frotz.h b/engines/glk/frotz/frotz.h
index 0dd8b69..22604e5 100644
--- a/engines/glk/frotz/frotz.h
+++ b/engines/glk/frotz/frotz.h
@@ -32,6 +32,11 @@ namespace Frotz {
  * Frotz interpreter for Z-code games
  */
 class Frotz : public Processor {
+protected:
+	/**
+	 * Create the screen class
+	 */
+	virtual Screen *createScreen() override;
 public:
 	/**
 	 * Constructor
diff --git a/engines/glk/frotz/pics_decoder.cpp b/engines/glk/frotz/pics_decoder.cpp
index d601874..6dde910 100644
--- a/engines/glk/frotz/pics_decoder.cpp
+++ b/engines/glk/frotz/pics_decoder.cpp
@@ -42,15 +42,14 @@ Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint
 	Common::MemoryWriteStreamDynamic out(DisposeAfterUse::NO);
 	byte buf[512];
     byte transparent;
-    int colour_shift;
-	int first_colour;
+    int colour_shift = 0;
+	int first_colour = 0;
     int code, prev_code = 0;
     int next_entry;
     int bits_per_code;
     int bits_shift;
     int bits;
     int bufpos = 0;
-    int i;
 
 	/*
 	 * Write out dimensions of image
@@ -83,6 +82,7 @@ Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint
 		first_colour = 65;
 		break;
 	default:
+		error("Unsupported mode");
 		break;
 	}
 	
diff --git a/engines/glk/frotz/screen.cpp b/engines/glk/frotz/screen.cpp
index eef6d61..a71217c 100644
--- a/engines/glk/frotz/screen.cpp
+++ b/engines/glk/frotz/screen.cpp
@@ -21,10 +21,26 @@
  */
 
 #include "glk/frotz/screen.h"
+#include "common/file.h"
+#include "image/bmp.h"
 
 namespace Glk {
 namespace Frotz {
 
+void FrotzScreen::loadFonts(Common::Archive *archive) {
+	Screen::loadFonts(archive);
+
+	Image::BitmapDecoder decoder;
+	Common::File f;
+	if (!f.open("infocom_graphics.bmp", *archive))
+		error("Could not load font");
+
+	decoder.loadStream(f);
+	_fonts.push_back(new Frotz::BitmapFont(*decoder.getSurface()));
+}
+
+/*--------------------------------------------------------------------------*/
+
 BitmapFont::BitmapFont(const Graphics::Surface &src, uint charWidth,
 		uint charHeight, unsigned char startingChar) : _startingChar(startingChar) {
 	assert(src.format.bytesPerPixel == 1);
diff --git a/engines/glk/frotz/screen.h b/engines/glk/frotz/screen.h
index dac716c..d8b2b89 100644
--- a/engines/glk/frotz/screen.h
+++ b/engines/glk/frotz/screen.h
@@ -28,11 +28,23 @@
 #include "common/archive.h"
 #include "common/array.h"
 #include "common/rect.h"
+#include "glk/screen.h"
 
 namespace Glk {
 namespace Frotz {
 
 /**
+ * Derived screen class that adds in the Infocom character graphics font
+ */
+class FrotzScreen : public Glk::Screen {
+protected:
+	/**
+	 * Load the fonts
+	 */
+	virtual void loadFonts(Common::Archive *archive) override;
+};
+
+/**
  * Implements a fixed width font stored as a grid on a passed surface
  */
 class BitmapFont : public Graphics::Font {
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 87b736c..98ce410 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -71,9 +71,10 @@ void GlkEngine::initialize() {
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
 
 	initGraphicsMode();
-	_conf = new Conf(getInterpreterType());
-	_screen = new Screen();
 
+	_conf = new Conf(getInterpreterType());
+	_screen = createScreen();
+	_screen->initialize();
 	_clipboard = new Clipboard();
 	_events = new Events();
 	_pictures = new Pictures();
@@ -82,6 +83,10 @@ void GlkEngine::initialize() {
 	_windows = new Windows(_screen);
 }
 
+Screen *GlkEngine::createScreen() {
+	return new Screen();
+}
+
 void GlkEngine::initGraphicsMode() {
 	uint width = ConfMan.hasKey("width") ? ConfMan.getInt("width") : 640;
 	uint height = ConfMan.hasKey("height") ? ConfMan.getInt("height") : 480;
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index af9a42b..526755b 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -90,6 +90,11 @@ protected:
 	virtual bool hasFeature(EngineFeature f) const;
 
 	/**
+	 * Create the screen
+	 */
+	virtual Screen *createScreen();
+
+	/**
 	 * Main game loop for the individual interpreters
 	 */
 	virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
diff --git a/engines/glk/screen.cpp b/engines/glk/screen.cpp
index cfe8cde..6768360 100644
--- a/engines/glk/screen.cpp
+++ b/engines/glk/screen.cpp
@@ -33,7 +33,12 @@ namespace Glk {
 #define FONTS_VERSION 1.0
 #define FONTS_FILENAME "fonts.dat"
 
-Screen::Screen() {
+Screen::~Screen() {
+	for (int idx = 0; idx < FONTS_TOTAL; ++idx)
+		delete _fonts[idx];
+}
+
+void Screen::initialize() {
 	if (!loadFonts())
 		error("Could not load data file");
 
@@ -49,11 +54,6 @@ Screen::Screen() {
 	g_conf->_cellH = g_conf->_leading;
 }
 
-Screen::~Screen() {
-	for (int idx = 0; idx < FONTS_TOTAL; ++idx)
-		delete _fonts[idx];
-}
-
 void Screen::fill(const byte *rgb) {
 	uint color = format.RGBToColor(rgb[0], rgb[1], rgb[2]);
 	clear(color);
@@ -100,7 +100,6 @@ void Screen::drawCaret(const Point &pos) {
 
 bool Screen::loadFonts() {
 	Common::Archive *archive = nullptr;
-	_fonts.resize(FONTS_TOTAL);
 
 	if (!Common::File::exists(FONTS_FILENAME) || (archive = Common::makeZipArchive(FONTS_FILENAME)) == nullptr)
 		return false;
@@ -122,12 +121,20 @@ bool Screen::loadFonts() {
 		return false;
 	}
 
+	loadFonts(archive);
+
+	delete archive;
+	return true;
+}
+
+void Screen::loadFonts(Common::Archive *archive) {
 	// R ead in the fonts
 	double monoAspect = g_conf->_monoAspect;
 	double propAspect = g_conf->_propAspect;
 	double monoSize = g_conf->_monoSize;
 	double propSize = g_conf->_propSize;
 
+	_fonts.resize(FONTS_TOTAL);
 	_fonts[0] = loadFont(MONOR, archive, monoSize, monoAspect, FONTR);
 	_fonts[1] = loadFont(MONOB, archive, monoSize, monoAspect, FONTB);
 	_fonts[2] = loadFont(MONOI, archive, monoSize, monoAspect, FONTI);
@@ -137,9 +144,6 @@ bool Screen::loadFonts() {
 	_fonts[5] = loadFont(PROPB, archive, propSize, propAspect, FONTB);
 	_fonts[6] = loadFont(PROPI, archive, propSize, propAspect, FONTI);
 	_fonts[7] = loadFont(PROPZ, archive, propSize, propAspect, FONTZ);
-
-	delete archive;
-	return true;
 }
 
 const Graphics::Font *Screen::loadFont(FACES face, Common::Archive *archive, double size, double aspect, int
diff --git a/engines/glk/screen.h b/engines/glk/screen.h
index 541e9e1..bb5d7f6 100644
--- a/engines/glk/screen.h
+++ b/engines/glk/screen.h
@@ -46,10 +46,8 @@ enum STYLES { FONTR, FONTB, FONTI, FONTZ };
  */
 class Screen : public Graphics::Screen {
 private:
-	Common::Array<const Graphics::Font *> _fonts;
-private:
 	/**
-	 * Load all the fonts
+	 * Open the fonts archive and load all the fonts
 	 */
 	bool loadFonts();
 
@@ -58,6 +56,13 @@ private:
 	 */
 	const Graphics::Font *loadFont(FACES face, Common::Archive *archive,
 		double size, double aspect, int style);
+protected:
+	Common::Array<const Graphics::Font *> _fonts;
+protected:
+	/**
+	 * Load the fonts
+	 */
+	virtual void loadFonts(Common::Archive *archive);
 public:
 	/**
 	 * Return the font Id for a given name
@@ -67,7 +72,7 @@ public:
 	/**
 	 * Constructor
 	 */
-	Screen();
+	Screen() : Graphics::Screen() {}
 
 	/**
 	 * Destructor
@@ -75,6 +80,11 @@ public:
 	virtual ~Screen();
 
 	/**
+	 * Initialize the screen
+	 */
+	void initialize();
+
+	/**
 	 * Fills the screen with a given rgb color
 	 */
 	void fill(const byte *rgb);


Commit: 9fa7e9be8180614b5f7c94440a8243f775e56aef
    https://github.com/scummvm/scummvm/commit/9fa7e9be8180614b5f7c94440a8243f775e56aef
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix selection of the character graphics font

Changed paths:
    engines/glk/frotz/processor_screen.cpp
    engines/glk/frotz/screen.cpp
    engines/glk/frotz/screen.h
    engines/glk/screen.h


diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index 696ce78..6a518cf 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -352,7 +352,7 @@ void Processor::z_set_text_style() {
 		// not tickle time
 		curstyle |= zargs[0];
 
-	if (h_flags & FIXED_FONT_FLAG || curr_font == FIXED_WIDTH_FONT)
+	if (h_flags & FIXED_FONT_FLAG || curr_font == FIXED_WIDTH_FONT || curr_font == GRAPHICS_FONT)
 		style = curstyle | FIXED_WIDTH_STYLE;
 	else
 		style = curstyle;
@@ -367,7 +367,9 @@ void Processor::z_set_text_style() {
 	}
 
 	if (style & FIXED_WIDTH_STYLE) {
-		if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
+		if (curr_font == GRAPHICS_FONT)
+			glk_set_style(style_User1);			// character graphics
+		else if (style & BOLDFACE_STYLE && style & EMPHASIS_STYLE)
 			glk_set_style(style_BlockQuote);	// monoz
 		else if (style & EMPHASIS_STYLE)
 			glk_set_style(style_Alert);			// monoi
diff --git a/engines/glk/frotz/screen.cpp b/engines/glk/frotz/screen.cpp
index a71217c..edd62eb 100644
--- a/engines/glk/frotz/screen.cpp
+++ b/engines/glk/frotz/screen.cpp
@@ -21,12 +21,17 @@
  */
 
 #include "glk/frotz/screen.h"
+#include "glk/conf.h"
 #include "common/file.h"
 #include "image/bmp.h"
 
 namespace Glk {
 namespace Frotz {
 
+FrotzScreen::FrotzScreen() : Glk::Screen() {
+	g_conf->_tStyles[style_User1].font = CUSTOM;
+}
+
 void FrotzScreen::loadFonts(Common::Archive *archive) {
 	Screen::loadFonts(archive);
 
@@ -66,7 +71,7 @@ void BitmapFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint
 		const byte *srcP = (const byte *)_surface.getBasePtr(r.left, r.top + yCtr);
 
 		for (int xCtr = 0; xCtr < r.width(); ++xCtr, ++srcP) {
-			if (*srcP)
+			if (!*srcP)
 				dst->hLine(x + xCtr, y + yCtr, x + xCtr, color);
 		}
 	}
diff --git a/engines/glk/frotz/screen.h b/engines/glk/frotz/screen.h
index d8b2b89..568c3c7 100644
--- a/engines/glk/frotz/screen.h
+++ b/engines/glk/frotz/screen.h
@@ -42,6 +42,11 @@ protected:
 	 * Load the fonts
 	 */
 	virtual void loadFonts(Common::Archive *archive) override;
+public:
+	/**
+	 * Constructor
+	 */
+	FrotzScreen();
 };
 
 /**
diff --git a/engines/glk/screen.h b/engines/glk/screen.h
index bb5d7f6..10f8b14 100644
--- a/engines/glk/screen.h
+++ b/engines/glk/screen.h
@@ -37,7 +37,7 @@ enum CaretShape {
 	SMALL_DOT = 0, FAT_DOT = 1, THIN_LINE = 2, FAT_LINE = 3, BLOCK = 4
 };
 
-enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ };
+enum FACES { MONOR, MONOB, MONOI, MONOZ, PROPR, PROPB, PROPI, PROPZ, CUSTOM };
 enum TYPES { MONOF, PROPF };
 enum STYLES { FONTR, FONTB, FONTI, FONTZ };
 


Commit: 6080a1d09105c8c743d9e739df91109c809fcef1
    https://github.com/scummvm/scummvm/commit/6080a1d09105c8c743d9e739df91109c809fcef1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Draw character graphic chars at the same size as the fixed width font

Changed paths:
    engines/glk/frotz/screen.cpp
    engines/glk/frotz/screen.h


diff --git a/engines/glk/frotz/screen.cpp b/engines/glk/frotz/screen.cpp
index edd62eb..08bf8c9 100644
--- a/engines/glk/frotz/screen.cpp
+++ b/engines/glk/frotz/screen.cpp
@@ -40,37 +40,40 @@ void FrotzScreen::loadFonts(Common::Archive *archive) {
 	if (!f.open("infocom_graphics.bmp", *archive))
 		error("Could not load font");
 
+	Common::Point fontSize(_fonts[0]->getMaxCharWidth(), _fonts[0]->getFontHeight());
 	decoder.loadStream(f);
-	_fonts.push_back(new Frotz::BitmapFont(*decoder.getSurface()));
+	_fonts.push_back(new Frotz::BitmapFont(*decoder.getSurface(), fontSize));
 }
 
 /*--------------------------------------------------------------------------*/
 
-BitmapFont::BitmapFont(const Graphics::Surface &src, uint charWidth,
-		uint charHeight, unsigned char startingChar) : _startingChar(startingChar) {
+BitmapFont::BitmapFont(const Graphics::Surface &src, const Common::Point &size,
+		uint srcWidth, uint srcHeight, unsigned char startingChar) :
+		_startingChar(startingChar), _size(size) {
 	assert(src.format.bytesPerPixel == 1);
-	assert((src.w % charWidth) == 0);
-	assert((src.h % charHeight) == 0);
-	_surface.copyFrom(src);
+	assert((src.w % srcWidth) == 0);
+	assert((src.h % srcHeight) == 0);
 
-	Common::Rect r(charWidth, charHeight);
-	for (uint y = 0; y < src.h; y += charHeight) {
-		r.moveTo(0, y);
-		for (uint x = 0; x < src.w; x += charWidth, r.translate(charWidth, 0))
-			_chars.push_back(r);
-	}
-}
+	// Set up a characters array
+	_chars.resize((src.w / srcWidth) * (src.h / srcHeight));
 
-BitmapFont::~BitmapFont() {
-	_surface.free();
+	// Iterate through loading characters
+	Common::Rect r(srcWidth, srcHeight);
+	int charsPerRow = src.w / srcWidth;
+	for (uint idx = 0; idx < _chars.size(); ++idx) {
+		r.moveTo((idx % charsPerRow) * srcWidth, (idx / charsPerRow) * srcHeight);
+
+		_chars[idx].create(size.x, size.y, src.format);
+		_chars[idx].transBlitFrom(src, r, Common::Rect(0, 0, size.x, size.y));
+	}
 }
 
 void BitmapFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
-	const Common::Rect &r = _chars[chr - _startingChar];
-	for (int yCtr = 0; yCtr < r.height(); ++yCtr) {
-		const byte *srcP = (const byte *)_surface.getBasePtr(r.left, r.top + yCtr);
+	const Graphics::ManagedSurface &c = _chars[chr - _startingChar];
+	for (int yCtr = 0; yCtr < c.h; ++yCtr) {
+		const byte *srcP = (const byte *)c.getBasePtr(0, yCtr);
 
-		for (int xCtr = 0; xCtr < r.width(); ++xCtr, ++srcP) {
+		for (int xCtr = 0; xCtr < c.w; ++xCtr, ++srcP) {
 			if (!*srcP)
 				dst->hLine(x + xCtr, y + yCtr, x + xCtr, color);
 		}
diff --git a/engines/glk/frotz/screen.h b/engines/glk/frotz/screen.h
index 568c3c7..223ef3b 100644
--- a/engines/glk/frotz/screen.h
+++ b/engines/glk/frotz/screen.h
@@ -54,37 +54,30 @@ public:
  */
 class BitmapFont : public Graphics::Font {
 private:
-	Graphics::Surface _surface;
-	Common::Array<Common::Rect> _chars;
+	Common::Array<Graphics::ManagedSurface> _chars;
 	size_t _startingChar;
+	Common::Point _size;
 public:
 	/**
 	 * Constructor
 	 */
-	BitmapFont(const Graphics::Surface &src, uint charWidth = 8, uint charHeight = 8,
-		unsigned char startingChar = ' ');
-	
-	/**
-	 * Destructor
-	 */
-	~BitmapFont();
+	BitmapFont(const Graphics::Surface &src, const Common::Point &size,
+		uint srcWidth = 8, uint srcHeight = 8, unsigned char startingChar = ' ');
 
 	/**
 	 * Get the font height
 	 */
-	virtual int getFontHeight() const override { return _chars[0].height(); }
+	virtual int getFontHeight() const override { return _size.y; }
 
 	/**
 	 * Get the maximum character width
 	 */
-	virtual int getMaxCharWidth() const override { return _chars[0].width(); }
+	virtual int getMaxCharWidth() const override { return _size.x; }
 
 	/**
 	 * Get the width of the given character
 	 */
-	virtual int getCharWidth(uint32 chr) const override {
-		return _chars[chr - _startingChar].width();
-	}
+	virtual int getCharWidth(uint32 chr) const override { return _size.x; }
 
 	/**
 	 * Draw a character


Commit: 6e6b285e83d44e0b35b1c8df3fd5fe325310810e
    https://github.com/scummvm/scummvm/commit/6e6b285e83d44e0b35b1c8df3fd5fe325310810e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Savegame listing now reads Quetzal savegames

Changed paths:
    engines/glk/POTFILES
    engines/glk/detection.cpp
    engines/glk/frotz/detection.cpp
    engines/glk/frotz/detection.h


diff --git a/engines/glk/POTFILES b/engines/glk/POTFILES
index 9caee0e..d672609 100644
--- a/engines/glk/POTFILES
+++ b/engines/glk/POTFILES
@@ -1,2 +1,3 @@
 engines/glk/streams.cpp
 engines/glk/scott/scott.cpp
+engines/glk/frotz/detection.cpp
diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index a91c603..4e9dee3 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -210,6 +210,8 @@ SaveStateList GlkMetaEngine::listSaves(const char *target) const {
 			if (in) {
 				if (Glk::FileStream::readSavegameHeader(in, header))
 					saveList.push_back(SaveStateDescriptor(slot, header._saveName));
+				else if (Glk::Frotz::FrotzMetaEngine::readSavegameHeader(in, header))
+					saveList.push_back(SaveStateDescriptor(slot, header._saveName));
 
 				delete in;
 			}
diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index e1c60bd..b1d751a 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -25,6 +25,7 @@
 #include "common/debug.h"
 #include "common/file.h"
 #include "common/md5.h"
+#include "common/translation.h"
 
 namespace Glk {
 namespace Frotz {
@@ -113,5 +114,37 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 	return !gameList.empty();
 }
 
+bool FrotzMetaEngine::readSavegameHeader(Common::SeekableReadStream *stream, Glk::SavegameHeader &header) {
+	stream->seek(0);
+	if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M'))
+		return false;
+	stream->readUint32BE();
+	if (stream->readUint32BE() != MKTAG('I', 'F', 'Z', 'S'))
+		return false;
+
+	header._saveName = _("Unnamed savegame");
+
+	while (stream->pos() < stream->size()) {
+		uint type = stream->readUint32BE();
+		size_t len = stream->readUint32BE();
+
+		if (type == MKTAG('A', 'N', 'N', 'O')) {
+			// Read savegame name from the annotation chunk
+			char *buffer = new char[len + 1];
+			stream->read(buffer, len);
+			buffer[len] = '\0';
+			header._saveName = Common::String(buffer);
+			break;
+		} else {
+			if (len & 1)
+				// Length must be even
+				++len;
+			stream->skip(len);
+		}
+	}
+
+	return true;
+}
+
 } // End of namespace Frotz
 } // End of namespace Glk
diff --git a/engines/glk/frotz/detection.h b/engines/glk/frotz/detection.h
index 6c70f98..4fa555b 100644
--- a/engines/glk/frotz/detection.h
+++ b/engines/glk/frotz/detection.h
@@ -25,6 +25,7 @@
 
 #include "common/fs.h"
 #include "engines/game.h"
+#include "glk/streams.h"
 
 namespace Glk {
 namespace Frotz {
@@ -46,6 +47,11 @@ public:
 	 * Detect supported games
 	 */
 	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+
+	/**
+	 * Check a passed stream for a Quetzal save, and if so, get header information
+	 */
+	static bool readSavegameHeader(Common::SeekableReadStream *stream, Glk::SavegameHeader &header);
 };
 
 } // End of namespace Frotz


Commit: 33a23bca1c0363340d14ed4c84e558e82a9f245e
    https://github.com/scummvm/scummvm/commit/33a23bca1c0363340d14ed4c84e558e82a9f245e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Further reading/writing setup for savegames using Quetzal

Changed paths:
    engines/glk/frotz/detection.cpp
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/frotz.h
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/scott/scott.cpp
    engines/glk/scott/scott.h
    engines/glk/streams.cpp


diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index b1d751a..6e1d23c 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -122,6 +122,7 @@ bool FrotzMetaEngine::readSavegameHeader(Common::SeekableReadStream *stream, Glk
 	if (stream->readUint32BE() != MKTAG('I', 'F', 'Z', 'S'))
 		return false;
 
+	header._interpType = INTERPRETER_FROTZ;
 	header._saveName = _("Unnamed savegame");
 
 	while (stream->pos() < stream->size()) {
@@ -143,6 +144,7 @@ bool FrotzMetaEngine::readSavegameHeader(Common::SeekableReadStream *stream, Glk
 		}
 	}
 
+	stream->seek(0);
 	return true;
 }
 
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 421b778..993c65e 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -64,7 +64,7 @@ void Frotz::initialize() {
 	z_restart();
 }
 
-Common::Error Frotz::saveGameData(strid_t file) {
+Common::Error Frotz::saveGameData(strid_t file, const Common::String &desc) {
 #ifdef TODO
 	long pc;
 	zword addr;
diff --git a/engines/glk/frotz/frotz.h b/engines/glk/frotz/frotz.h
index 22604e5..28902c5 100644
--- a/engines/glk/frotz/frotz.h
+++ b/engines/glk/frotz/frotz.h
@@ -71,7 +71,7 @@ public:
 	/**
 	 * Save the game to the passed stream
 	 */
-	virtual Common::Error saveGameData(strid_t file) override;
+	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
 };
 
 extern Frotz *g_vm;
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 98ce410..a22f9e9 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -186,7 +186,7 @@ Common::Error GlkEngine::saveGameState(int slot, const Common::String &desc) {
 	if (file == nullptr)
 		return Common::kWritingFailed;
 
-	Common::Error result = saveGameData(file);
+	Common::Error result = saveGameData(file, desc);
 
 	file->close();
 	return result;
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 526755b..923e295 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -209,7 +209,7 @@ public:
 	/**
 	 * Save the game to the passed file
 	 */
-	virtual Common::Error saveGameData(strid_t file) = 0;
+	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) = 0;
 };
 
 extern GlkEngine *g_vm;
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index eb8cd40..1efc683 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -508,7 +508,7 @@ void Scott::lineInput(char *buf, size_t n) {
 	buf[ev.val1] = 0;
 }
 
-Common::Error Scott::saveGameData(strid_t file) {
+Common::Error Scott::saveGameData(strid_t file, const Common::String &desc) {
 	Common::String msg;
 
 	for (int ct = 0; ct < 16; ct++) {
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index ad1313d..b490fd6 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -184,7 +184,7 @@ public:
 	/**
 	 * Save the game to the passed stream
 	 */
-	virtual Common::Error saveGameData(strid_t file) override;
+	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
 };
 
 } // End of namespace Scott
diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
index 911f6d9..80f03dd 100644
--- a/engines/glk/streams.cpp
+++ b/engines/glk/streams.cpp
@@ -25,6 +25,7 @@
 #include "glk/events.h"
 #include "glk/glk.h"
 #include "glk/windows.h"
+#include "glk/frotz/detection.h"
 #include "gui/saveload.h"
 #include "common/file.h"
 #include "common/savefile.h"
@@ -771,7 +772,9 @@ FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 roc
 		if (!_outFile)
 			error("Could open file for writing - %s", fname.c_str());
 
-		if (fref->_slotNumber != -1)
+		// For creating savegames, write out the header. Frotz is a special case,
+		// since the Quetzal format is used for compatibility
+		if (fref->_slotNumber != -1 && g_vm->getInterpreterType() != INTERPRETER_FROTZ)
 			writeSavegameHeader(_outFile, fref->_description);
 	} else if (fmode == filemode_Read) {
 		if (_file.open(fname)) {
@@ -787,8 +790,11 @@ FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 roc
 		if (_inFile) {
 			// It's a save file, so skip over the header
 			SavegameHeader header;
-			if (!readSavegameHeader(_inStream, header))
+			if (!(g_vm->getInterpreterType() == INTERPRETER_FROTZ ?
+					Frotz::FrotzMetaEngine::readSavegameHeader(_inStream, header) :
+					readSavegameHeader(_inStream, header)))
 				error("Invalid savegame");
+
 			if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage()
 			        || header._md5 != g_vm->getGameMD5())
 				error("Savegame is for a different game");


Commit: 2331a0e9fb3dfe335c15c470d3e7da49cc7ac5f7
    https://github.com/scummvm/scummvm/commit/2331a0e9fb3dfe335c15c470d3e7da49cc7ac5f7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Quetzal saving and loading now works

Changed paths:
    engines/glk/frotz/config.cpp
    engines/glk/frotz/detection.cpp
    engines/glk/frotz/frotz.cpp
    engines/glk/frotz/processor.h
    engines/glk/frotz/quetzal.cpp
    engines/glk/frotz/quetzal.h
    engines/glk/streams.cpp
    engines/glk/streams.h


diff --git a/engines/glk/frotz/config.cpp b/engines/glk/frotz/config.cpp
index 21cfb03..e53cd8c 100644
--- a/engines/glk/frotz/config.cpp
+++ b/engines/glk/frotz/config.cpp
@@ -145,11 +145,10 @@ void Header::loadHeader(Common::SeekableReadStream &f) {
 
 /*--------------------------------------------------------------------------*/
 
-UserOptions::UserOptions() : _undo_slots(MAX_UNDO_SLOTS), _sound(true) {
+UserOptions::UserOptions() : _undo_slots(MAX_UNDO_SLOTS), _sound(true), _quetzal(true) {
 	_err_report_mode = getConfigInt("err_report_mode", ERR_REPORT_ONCE, ERR_REPORT_FATAL);
 	_ignore_errors = getConfigBool("ignore_errors");
 	_expand_abbreviations = getConfigBool("expand_abbreviations");
-	_quetzal = getConfigBool("quetzal", true);
 	_tandyBit = getConfigBool("tandy_bit");
 	_piracy = getConfigBool("piracy");
 	_script_cols = getConfigInt("wrap_script_lines", 80, 999);
diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index 6e1d23c..895dd4e 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -22,6 +22,7 @@
 
 #include "glk/frotz/detection.h"
 #include "glk/frotz/detection_tables.h"
+#include "glk/frotz/quetzal.h"
 #include "common/debug.h"
 #include "common/file.h"
 #include "common/md5.h"
@@ -116,10 +117,10 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 
 bool FrotzMetaEngine::readSavegameHeader(Common::SeekableReadStream *stream, Glk::SavegameHeader &header) {
 	stream->seek(0);
-	if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M'))
+	if (stream->readUint32BE() != ID_FORM)
 		return false;
 	stream->readUint32BE();
-	if (stream->readUint32BE() != MKTAG('I', 'F', 'Z', 'S'))
+	if (stream->readUint32BE() != ID_IFZS)
 		return false;
 
 	header._interpType = INTERPRETER_FROTZ;
@@ -129,13 +130,14 @@ bool FrotzMetaEngine::readSavegameHeader(Common::SeekableReadStream *stream, Glk
 		uint type = stream->readUint32BE();
 		size_t len = stream->readUint32BE();
 
-		if (type == MKTAG('A', 'N', 'N', 'O')) {
+		if (type == ID_ANNO) {
 			// Read savegame name from the annotation chunk
 			char *buffer = new char[len + 1];
 			stream->read(buffer, len);
 			buffer[len] = '\0';
 			header._saveName = Common::String(buffer);
 			break;
+
 		} else {
 			if (len & 1)
 				// Length must be even
diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 993c65e..2ec7585 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -23,6 +23,7 @@
 #include "glk/frotz/frotz.h"
 #include "glk/frotz/frotz_types.h"
 #include "glk/frotz/screen.h"
+#include "glk/frotz/quetzal.h"
 #include "common/config-manager.h"
 
 namespace Glk {
@@ -65,167 +66,44 @@ void Frotz::initialize() {
 }
 
 Common::Error Frotz::saveGameData(strid_t file, const Common::String &desc) {
-#ifdef TODO
-	long pc;
-	zword addr;
-	zword nsp, nfp;
-	int skip;
-	int i;
+	Quetzal q(story_fp);
+	bool success = q.save(*file, this, desc);
 
-	// Open game file
-
-	if ((gfp = frotzopenprompt(FILE_SAVE)) == nullptr)
-		goto finished;
-
-	if (_quetzal) {
-		success = save_quetzal(gfp, story_fp, blorb_ofs);
-	}
-	else {
-		// Write game file
-
-		fputc((int)hi(h_release), gfp);
-		fputc((int)lo(h_release), gfp);
-		fputc((int)hi(h_checksum), gfp);
-		fputc((int)lo(h_checksum), gfp);
-
-		GET_PC(pc)
-
-			fputc((int)(pc >> 16) & 0xff, gfp);
-		fputc((int)(pc >> 8) & 0xff, gfp);
-		fputc((int)(pc)& 0xff, gfp);
-
-		nsp = (int)(_sp - _stack);
-		nfp = (int)(_fp - _stack);
-
-		fputc((int)hi(nsp), gfp);
-		fputc((int)lo(nsp), gfp);
-		fputc((int)hi(nfp), gfp);
-		fputc((int)lo(nfp), gfp);
-
-		for (i = nsp; i < STACK_SIZE; i++) {
-			fputc((int)hi(_stack[i]), gfp);
-			fputc((int)lo(_stack[i]), gfp);
-		}
-
-		fseek(story_fp, blorb_ofs, SEEK_SET);
-
-		for (addr = 0, skip = 0; addr < h_dynamic_size; addr++)
-			if (zmp[addr] != fgetc(story_fp) || skip == 255 || addr + 1 == h_dynamic_size) {
-				fputc(skip, gfp);
-				fputc(zmp[addr], gfp);
-				skip = 0;
-			}
-			else skip++;
-	}
-
-	// Close game file and check for errors
-
-	if (fclose(gfp) == EOF || ferror(story_fp)) {
+	if (!success)
 		print_string("Error writing save file\n");
-		goto finished;
-	}
 
-	// Success
-	success = 1;
-#endif
 	return Common::kNoError;
 }
 
 Common::Error Frotz::loadGameData(strid_t file) {
-#ifdef TODO
-	long pc;
-	zword release;
-	zword addr;
-	int i;
-
-	// Open game file
-	if ((gfp = frotzopenprompt(FILE_RESTORE)) == nullptr)
-		goto finished;
-
-	if (_quetzal) {
-		success = restore_quetzal (gfp, story_fp, blorb_ofs);
-
+	Quetzal q(story_fp);
+	bool success = q.restore(*file, this) == 2;
+
+	if (success) {
+		zbyte old_screen_rows;
+		zbyte old_screen_cols;
+
+		// In V3, reset the upper window.
+		if (h_version == V3)
+			split_window(0);
+
+		LOW_BYTE(H_SCREEN_ROWS, old_screen_rows);
+		LOW_BYTE(H_SCREEN_COLS, old_screen_cols);
+
+		// Reload cached header fields
+		restart_header ();
+
+		/* Since QUETZAL files may be saved on many different machines,
+		 * the screen sizes may vary a lot. Erasing the status window
+		 * seems to cover up most of the resulting badness.
+		 */
+		if (h_version > V3 && h_version != V6 && (h_screen_rows != old_screen_rows
+					|| h_screen_cols != old_screen_cols))
+			erase_window (1);
 	} else {
-		// Load game file
-
-		release = (unsigned) fgetc (gfp) << 8;
-		release |= fgetc (gfp);
-
-		() fgetc (gfp);
-		() fgetc (gfp);
-
-		// Check the release number
-		if (release == h_release) {
-
-			pc = (long) fgetc (gfp) << 16;
-			pc |= (unsigned) fgetc (gfp) << 8;
-			pc |= fgetc (gfp);
-
-			SET_PC (pc);
-
-			_sp = _stack + (fgetc (gfp) << 8);
-			_sp += fgetc (gfp);
-			_fp = _stack + (fgetc (gfp) << 8);
-			_fp += fgetc (gfp);
-
-			for (i = (int) (_sp - _stack); i < STACK_SIZE; i++) {
-				_stack[i] = (unsigned) fgetc (gfp) << 8;
-				_stack[i] |= fgetc (gfp);
-			}
-
-			fseek (story_fp, blorb_ofs, SEEK_SET);
-
-			for (addr = 0; addr < h_dynamic_size; addr++) {
-				int skip = fgetc (gfp);
-				for (i = 0; i < skip; i++)
-					zmp[addr++] = fgetc (story_fp);
-				zmp[addr] = fgetc (gfp);
-				() fgetc (story_fp);
-			}
-
-			// Check for errors
-			if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size)
-				success = -1;
-			else
-
-				// Success
-				success = 2;
-
-		} else print_string ("Invalid save file\n");
+		error("Error reading save file");
 	}
 
-	if ((short) success >= 0) {
-
-		// Close game file
-		fclose (gfp);
-
-		if ((short) success > 0) {
-			zbyte old_screen_rows;
-			zbyte old_screen_cols;
-
-			// In V3, reset the upper window.
-			if (h_version == V3)
-				split_window (0);
-
-			LOW_BYTE (H_SCREEN_ROWS, old_screen_rows);
-			LOW_BYTE (H_SCREEN_COLS, old_screen_cols);
-
-			/* Reload cached header fields. */
-			restart_header ();
-
-			/*
-				* Since QUETZAL files may be saved on many different machines,
-				* the screen sizes may vary a lot. Erasing the status window
-				* seems to cover up most of the resulting badness.
-				*/
-			if (h_version > V3 && h_version != V6
-					&& (h_screen_rows != old_screen_rows
-						|| h_screen_cols != old_screen_cols))
-				erase_window (1);
-		}
-	} else
-		os_fatal ("Error reading save file");
-#endif
 	return Common::kNoError;
 }
 
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index 77a673d..fb9c12f 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -95,7 +95,7 @@ private:
 	bool istream_replay;
 	bool message;
 	Common::FixedStack<Redirect, MAX_NESTING> _redirect;
-private:
+protected:
 	/**
 	 * \defgroup General support methods
 	 * @{
@@ -190,6 +190,11 @@ private:
 	void new_line();
 
 	/**
+	 * Copy the contents of the text buffer to the output streams.
+	 */
+	void flush_buffer();
+
+	/**
 	 * Returns true if the buffer is empty
 	 */
 	bool bufferEmpty() const { return !_bufPos; }
@@ -1540,18 +1545,6 @@ protected:
 	void z_store();
 
 	/**@}*/
-
-	/**
-	 * \defgroup Input support methods
-	 * @{
-	 */
-
-	/**
-	 * Copy the contents of the text buffer to the output streams.
-	 */
-	void flush_buffer();
-
-	/**@}*/
 public:
 	/**
 	 * Constructor
diff --git a/engines/glk/frotz/quetzal.cpp b/engines/glk/frotz/quetzal.cpp
index 9ff33ab..d727515 100644
--- a/engines/glk/frotz/quetzal.cpp
+++ b/engines/glk/frotz/quetzal.cpp
@@ -50,9 +50,9 @@ bool Quetzal::read_long(Common::ReadStream *f, uint *result) {
 	return true;
 }
 
-bool Quetzal::save(Common::WriteStream *svf, Processor *proc) {
+bool Quetzal::save(Common::WriteStream *svf, Processor *proc, const Common::String &desc) {
 	Processor &p = *proc;
-	uint ifzslen = 0, cmemlen = 0, stkslen = 0;
+	uint ifzslen = 0, cmemlen = 0, stkslen = 0, descLen = 0;
 	uint pc;
 	zword i, j, n;
 	zword nvars, nargs, nstk;
@@ -79,10 +79,20 @@ bool Quetzal::save(Common::WriteStream *svf, Processor *proc) {
 	write_word(p.h_checksum);
 	write_long(pc << 8);		// Includes pad
 
+	// Write 'ANNO' chunk
+	descLen = desc.size() + 1;
+	write_chnk(ID_ANNO, descLen);
+	saveData.write(desc.c_str(), desc.size());
+	write_byte(0);
+	if ((desc.size() % 2) == 0) {
+		write_byte(0);
+		++descLen;
+	}
+
 	// Write `CMem' chunk.
-	cmempos = svf->pos();
+	cmempos = saveData.pos();
 	write_chnk(ID_CMem, 0);
-	_storyFile->seek(_blorbOffset);
+	_storyFile->seek(0);
 
 	// j holds current run length.
 	for (i = 0, j = 0, cmemlen = 0; i < p.h_dynamic_size; ++i) {
@@ -116,7 +126,7 @@ bool Quetzal::save(Common::WriteStream *svf, Processor *proc) {
 		write_byte(0);
 
 	// Write `Stks' chunk. You are not expected to understand this. ;)
-	stkspos = _storyFile->pos();
+	stkspos = saveData.pos();
 	write_chnk(ID_Stks, 0);
 
 	// We construct a list of frame indices, most recent first, in `frames'.
@@ -182,7 +192,7 @@ bool Quetzal::save(Common::WriteStream *svf, Processor *proc) {
 	}
 
 	// Fill in variable chunk lengths
-	ifzslen = 3 * 8 + 4 + 14 + cmemlen + stkslen;
+	ifzslen = 4 * 8 + 4 + 14 + cmemlen + stkslen + descLen;
 	if (cmemlen & 1)
 		++ifzslen;
 
@@ -281,7 +291,7 @@ int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
 			fatal = -1;		// Setting PC means errors must be fatal
 			p.setPC(pc);
 
-			svf->skip(13);	// Skip rest of chunk
+			svf->skip(currlen - 13);	// Skip rest of chunk
 			break;
 
 		// `Stks' stacks chunk; restoring this is quite complex. ;)
@@ -394,8 +404,7 @@ int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
 		// `CMem' compressed memory chunk; uncompress it
 		case ID_CMem:
 			if (!(progress & GOT_MEMORY)) {
-				// Don't complain if two
-				_storyFile->seek(_blorbOffset);
+				_storyFile->seek(0);
 				
 				i = 0;	// Bytes written to data area
 				for (; currlen > 0; --currlen) {
@@ -416,10 +425,10 @@ int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
 						--currlen;
 						x = svf->readByte();
 						for (; x >= 0 && i < p.h_dynamic_size; --x, ++i)
-							p[i] = svf->readByte();
+							p[i] = _storyFile->readByte();
 					} else {
 						// Not a run
-						y = svf->readByte();
+						y = _storyFile->readByte();
 						p[i] = (zbyte)(x ^ y);
 						++i;
 					}
@@ -434,14 +443,14 @@ int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
 
 				// If chunk is short, assume a run
 				for (; i < p.h_dynamic_size; ++i)
-					p[i] = svf->readByte();
+					p[i] = _storyFile->readByte();
 
 				if (currlen == 0)
 					progress |= GOT_MEMORY;		// Only if succeeded
 				break;
 			}
 
-			// Intentional fall-through
+			// Intentional fall-through on error
 
 		case ID_UMem:
 			if (!(progress & GOT_MEMORY)) {
@@ -458,7 +467,7 @@ int Quetzal::restore(Common::SeekableReadStream *svf, Processor *proc) {
 				// Fall into default action (skip chunk) on errors
 			}
 
-			// Intentional fall-through
+			// Intentional fall-through on error
 
 		default:
 			svf->seek(currlen, SEEK_CUR);		// Skip chunk
diff --git a/engines/glk/frotz/quetzal.h b/engines/glk/frotz/quetzal.h
index 9d382ab..98765b8 100644
--- a/engines/glk/frotz/quetzal.h
+++ b/engines/glk/frotz/quetzal.h
@@ -36,7 +36,8 @@ enum QueztalTag {
 	ID_UMem = MKTAG('U', 'M', 'e', 'm'),
 	ID_CMem = MKTAG('C', 'M', 'e', 'm'),
 	ID_Stks = MKTAG('S', 't', 'k', 's'),
-	ID_ANNO = MKTAG('A', 'N', 'N', 'O')
+	ID_ANNO = MKTAG('A', 'N', 'N', 'O'),
+	ID_SCVM = MKTAG('S', 'C', 'V', 'M')
 };
 
 class Processor;
@@ -45,8 +46,6 @@ class Quetzal {
 private:
 	Common::SeekableReadStream *_storyFile;
 	Common::WriteStream *_out;
-	size_t _blorbOffset;
-	int _slot;
 	zword frames[STACK_SIZE / 4 + 1];
 private:
 	/**
@@ -63,7 +62,7 @@ private:
 	void write_bytx(zword b) { _out->writeByte(b & 0xFF); }
 	void write_word(zword w) { _out->writeUint16BE(w); }
 	void write_long(uint l) { _out->writeUint32BE(l); }
-	void write_run(zword run) { _out->writeUint16LE(run); }
+	void write_run(zword run) { write_byte(0); write_byte(run); }
 	void write_chnk(QueztalTag id, zword len) {
 		_out->writeUint32BE(id);
 		_out->writeUint32BE(len);
@@ -72,19 +71,21 @@ public:
 	/**
 	 * Constructor
 	 */
-	Quetzal(Common::SeekableReadStream *storyFile, size_t blorbOffset, int slot) :
-		_storyFile(storyFile), _blorbOffset(blorbOffset), _slot(slot) {}
+	Quetzal(Common::SeekableReadStream *storyFile) : _storyFile(storyFile) {}
 
 	/*
 	 * Save a game using Quetzal format.
 	 * @param svf	Savegame file
+	 * @param proc	Pointer to the Frotz processor
+	 * @param desc	Savegame description
 	 * @returns		Returns true if OK, false if failed
 	 */
-	bool save(Common::WriteStream *svf, Processor *proc);
+	bool save(Common::WriteStream *svf, Processor *proc, const Common::String &desc);
 
 	/**
 	 * Restore a saved game using Quetzal format
 	 * @param svf	Savegame file
+	 * @param proc	Pointer to the Frotz processor
 	 * @returns		Return 2 if OK, 0 if an error occurred before any damage was done,
 	 *				-1 on a fatal error
 	 */
diff --git a/engines/glk/streams.cpp b/engines/glk/streams.cpp
index 80f03dd..d6c71ca 100644
--- a/engines/glk/streams.cpp
+++ b/engines/glk/streams.cpp
@@ -768,7 +768,7 @@ FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 roc
 	Common::String fname = fref->_slotNumber == -1 ? fref->_filename : fref->getSaveName();
 
 	if (fmode == filemode_Write || fmode == filemode_ReadWrite || fmode == filemode_WriteAppend) {
-		_outFile = g_system->getSavefileManager()->openForSaving(fname, fref->_slotNumber != -1);
+		_outFile = g_system->getSavefileManager()->openForSaving(fname, fref->_slotNumber != -1 && g_vm->getInterpreterType() != INTERPRETER_FROTZ);
 		if (!_outFile)
 			error("Could open file for writing - %s", fname.c_str());
 
@@ -795,11 +795,13 @@ FileStream::FileStream(Streams *streams, frefid_t fref, glui32 fmode, glui32 roc
 					readSavegameHeader(_inStream, header)))
 				error("Invalid savegame");
 
-			if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage()
-			        || header._md5 != g_vm->getGameMD5())
-				error("Savegame is for a different game");
+			if (g_vm->getInterpreterType() != INTERPRETER_FROTZ) {
+				if (header._interpType != g_vm->getInterpreterType() || header._language != g_vm->getLanguage()
+						|| header._md5 != g_vm->getGameMD5())
+					error("Savegame is for a different game");
 
-			g_vm->_events->setTotalPlayTicks(header._totalFrames);
+				g_vm->_events->setTotalPlayTicks(header._totalFrames);
+			}
 		}
 	}
 }
diff --git a/engines/glk/streams.h b/engines/glk/streams.h
index 3150ce9..0b82b36 100644
--- a/engines/glk/streams.h
+++ b/engines/glk/streams.h
@@ -283,6 +283,16 @@ public:
 	 * Set the reverse video style
 	 */
 	virtual void setReverseVideo(bool reverse);
+
+	/**
+	 * Cast a stream to a ScummVM write stream
+	 */
+	virtual operator Common::WriteStream *() const { return nullptr; }
+
+	/**
+	 * Cast a stream to a ScummVM read stream
+	 */
+	virtual operator Common::SeekableReadStream *() const { return nullptr; }
 };
 typedef Stream *strid_t;
 
@@ -528,6 +538,16 @@ public:
 	 * Get a unicode line
 	 */
 	virtual glui32 getLineUni(glui32 *ubuf, glui32 len) override;
+
+	/**
+	 * Cast a stream to a ScummVM write stream
+	 */
+	virtual operator Common::WriteStream *() const override { return _outFile; }
+
+	/**
+	 * Cast a stream to a ScummVM read stream
+	 */
+	virtual operator Common::SeekableReadStream *() const override { return _inStream; }
 };
 
 /**


Commit: bfbc0ba0edc7a84e49660781d840035d4de7f076
    https://github.com/scummvm/scummvm/commit/bfbc0ba0edc7a84e49660781d840035d4de7f076
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Allow loading savegames from launcher

Changed paths:
    engines/glk/frotz/frotz.cpp


diff --git a/engines/glk/frotz/frotz.cpp b/engines/glk/frotz/frotz.cpp
index 2ec7585..69ca0a3 100644
--- a/engines/glk/frotz/frotz.cpp
+++ b/engines/glk/frotz/frotz.cpp
@@ -48,6 +48,17 @@ void Frotz::runGame(Common::SeekableReadStream *gameFile) {
 	story_fp = gameFile;
 	initialize();
 
+	// If save was selected from the launcher, handle loading it
+	int saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
+	if (saveSlot != -1) {
+		bool success = loadGameState(saveSlot).getCode() == Common::kNoError;
+
+		if (h_version <= V3)
+			branch(success);
+		else
+			store(success);
+	}
+
 	// Game loop
 	interpret();
 


Commit: 855911d0c0d8536edd9330540380345fd3b5eb3f
    https://github.com/scummvm/scummvm/commit/855911d0c0d8536edd9330540380345fd3b5eb3f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix display of character graphics in text grid windows

Changed paths:
    engines/glk/frotz/screen.cpp


diff --git a/engines/glk/frotz/screen.cpp b/engines/glk/frotz/screen.cpp
index 08bf8c9..971ea2f 100644
--- a/engines/glk/frotz/screen.cpp
+++ b/engines/glk/frotz/screen.cpp
@@ -30,6 +30,7 @@ namespace Frotz {
 
 FrotzScreen::FrotzScreen() : Glk::Screen() {
 	g_conf->_tStyles[style_User1].font = CUSTOM;
+	g_conf->_gStyles[style_User1].font = CUSTOM;
 }
 
 void FrotzScreen::loadFonts(Common::Archive *archive) {


Commit: 0b7a29c7973025d32d8952e97cdc4d13a2794c36
    https://github.com/scummvm/scummvm/commit/0b7a29c7973025d32d8952e97cdc4d13a2794c36
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Skeleton engine

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


diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 4e9dee3..d5b0ce8 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -25,6 +25,8 @@
 #include "glk/frotz/frotz.h"
 #include "glk/scott/detection.h"
 #include "glk/scott/scott.h"
+#include "glk/tads/detection.h"
+#include "glk/tads/tads.h"
 
 #include "base/plugins.h"
 #include "common/md5.h"
@@ -133,6 +135,8 @@ Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
 		*engine = new Glk::Frotz::Frotz(syst, gameDesc);
 	} else if (Glk::Scott::ScottMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
 		*engine = new Glk::Scott::Scott(syst, gameDesc);
+	} else if (Glk::TADS::TADSMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
+		*engine = new Glk::TADS::TADS(syst, gameDesc);
 	} else {
 		return Common::kNoGameDataFoundError;
 	}
@@ -166,6 +170,7 @@ PlainGameList GlkMetaEngine::getSupportedGames() const {
 	PlainGameList list;
 	Glk::Frotz::FrotzMetaEngine::getSupportedGames(list);
 	Glk::Scott::ScottMetaEngine::getSupportedGames(list);
+	Glk::TADS::TADSMetaEngine::getSupportedGames(list);
 
 	return list;
 }
@@ -179,6 +184,9 @@ PlainGameDescriptor GlkMetaEngine::findGame(const char *gameId) const {
 	gd = Glk::Scott::ScottMetaEngine::findGame(gameId);
 	if (gd.description) return gd;
 
+	gd = Glk::TADS::TADSMetaEngine::findGame(gameId);
+	if (gd.description) return gd;
+
 	return PlainGameDescriptor();
 }
 
@@ -186,6 +194,7 @@ DetectedGames GlkMetaEngine::detectGames(const Common::FSList &fslist) const {
 	DetectedGames detectedGames;
 	Glk::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
 	Glk::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
+	Glk::TADS::TADSMetaEngine::detectGames(fslist, detectedGames);
 
 	return detectedGames;
 }
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 4112a9f..b75772d 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -42,7 +42,9 @@ MODULE_OBJS := \
 	frotz/quetzal.o \
 	frotz/screen.o \
 	scott/detection.o \
-	scott/scott.o
+	scott/scott.o \
+	tads/detection.o \
+	tads/tads.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_GLK), DYNAMIC_PLUGIN)
diff --git a/engines/glk/tads/detection.cpp b/engines/glk/tads/detection.cpp
new file mode 100644
index 0000000..ef2caa2
--- /dev/null
+++ b/engines/glk/tads/detection.cpp
@@ -0,0 +1,81 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/tads/detection.h"
+#include "glk/tads/detection_tables.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "engines/game.h"
+
+namespace Glk {
+namespace TADS {
+
+void TADSMetaEngine::getSupportedGames(PlainGameList &games) {
+	for (const PlainGameDescriptor *pd = TADS_GAME_LIST; pd->gameId; ++pd)
+		games.push_back(*pd);
+}
+
+PlainGameDescriptor TADSMetaEngine::findGame(const char *gameId) {
+	for (const PlainGameDescriptor *pd = TADS_GAME_LIST; pd->gameId; ++pd) {
+		if (!strcmp(gameId, pd->gameId))
+			return *pd;
+	}
+
+	return PlainGameDescriptor();;
+}
+
+bool TADSMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+	Common::File gameFile;
+	Common::String md5;
+
+	// Loop through the files of the folder
+	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+		if (file->isDirectory() || !(file->getName().hasSuffixIgnoreCase(".saga")
+				|| file->getName().hasSuffixIgnoreCase(".dat")))
+			continue;
+
+		if (gameFile.open(*file)) {
+			md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+
+			// Scan through the TADS game list for a match
+			const TADSGame *p = TADS_GAMES;
+			while (p->_md5 && p->_filesize != gameFile.size() && md5 != p->_md5)
+				++p;
+
+			if (p->_filesize) {
+				// Found a match
+				PlainGameDescriptor gameDesc = findGame(p->_gameId);
+				DetectedGame gd(p->_gameId, gameDesc.description, Common::EN_ANY, Common::kPlatformUnknown);
+				gd.addExtraEntry("filename", file->getName());
+
+				gameList.push_back(gd);
+			}
+
+			gameFile.close();
+		}
+	}
+
+	return !gameList.empty();
+}
+
+} // End of namespace TADS
+} // End of namespace Glk
diff --git a/engines/glk/tads/detection.h b/engines/glk/tads/detection.h
new file mode 100644
index 0000000..59a6bc7
--- /dev/null
+++ b/engines/glk/tads/detection.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.
+ *
+ */
+
+#ifndef GLK_TADS_DETECTION
+#define GLK_TADS_DETECTION
+
+#include "common/fs.h"
+#include "engines/game.h"
+
+namespace Glk {
+namespace TADS {
+
+/**
+ * Meta engine for TADS interpreter
+ */
+class TADSMetaEngine {
+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 PlainGameDescriptor findGame(const char *gameId);
+
+	/**
+	 * Detect supported games
+	 */
+	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+};
+
+} // End of namespace TADS
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/tads/detection_tables.h b/engines/glk/tads/detection_tables.h
new file mode 100644
index 0000000..eb9441f
--- /dev/null
+++ b/engines/glk/tads/detection_tables.h
@@ -0,0 +1,49 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/game.h"
+#include "common/gui_options.h"
+#include "common/language.h"
+
+namespace Glk {
+namespace TADS {
+
+/**
+ * Game descriptor
+ */
+struct TADSGame {
+	const char *_md5;
+	const char *_gameId;
+	int32 _filesize;
+};
+
+const PlainGameDescriptor TADS_GAME_LIST[] = {
+	{ "tads",     "TADS" },
+	{ nullptr, nullptr }
+};
+
+const TADSGame TADS_GAMES[] = {
+	{ nullptr, nullptr, 0 }
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
diff --git a/engines/glk/tads/tads.cpp b/engines/glk/tads/tads.cpp
new file mode 100644
index 0000000..8a40dbd
--- /dev/null
+++ b/engines/glk/tads/tads.cpp
@@ -0,0 +1,48 @@
+/* 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/tads/tads.h"
+#include "common/config-manager.h"
+#include "common/translation.h"
+
+namespace Glk {
+namespace TADS {
+
+TADS::TADS(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+}
+
+void TADS::runGame(Common::SeekableReadStream *gameFile) {
+	// TODO
+}
+
+Common::Error TADS::loadGameData(strid_t file) {
+	// TODO
+	return Common::kNoError;
+}
+
+Common::Error TADS::saveGameData(strid_t file, const Common::String &desc) {
+	// TODO
+	return Common::kNoError;
+}
+
+} // End of namespace TADS
+} // End of namespace Glk
diff --git a/engines/glk/tads/tads.h b/engines/glk/tads/tads.h
new file mode 100644
index 0000000..23caef7
--- /dev/null
+++ b/engines/glk/tads/tads.h
@@ -0,0 +1,66 @@
+/* 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_TADS
+#define GLK_TADS
+
+#include "common/scummsys.h"
+#include "glk/glk_api.h"
+
+namespace Glk {
+namespace TADS {
+
+/**
+ * tads game interpreter
+ */
+class TADS : public GlkAPI {
+public:
+	/**
+	 * Constructor
+	 */
+	TADS(OSystem *syst, const GlkGameDescription &gameDesc);
+
+	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_SCOTT; }
+
+	/**
+	 * Execute the game
+	 */
+	virtual void runGame(Common::SeekableReadStream *gameFile) override;
+
+	/**
+	 * 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 TADS
+} // End of namespace Glk
+
+#endif


Commit: 09b682cb63fed029f43133f4a2d9daa3438ac8e2
    https://github.com/scummvm/scummvm/commit/09b682cb63fed029f43133f4a2d9daa3438ac8e2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Skeletons for TADS 2 & 3 subengines, extra detection logic

Changed paths:
  A engines/glk/tads/tads2/tads2.cpp
  A engines/glk/tads/tads2/tads2.h
  A engines/glk/tads/tads3/tads3.cpp
  A engines/glk/tads/tads3/tads3.h
    engines/glk/blorb.cpp
    engines/glk/detection.cpp
    engines/glk/glk_types.h
    engines/glk/module.mk
    engines/glk/tads/detection.cpp
    engines/glk/tads/detection.h
    engines/glk/tads/detection_tables.h
    engines/glk/tads/tads.cpp
    engines/glk/tads/tads.h


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index 5c1f0bd..038a79e 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -171,7 +171,8 @@ Common::ErrorCode Blorb::load() {
 
 			if (
 				(_interpType == INTERPRETER_FROTZ && type == "ZCOD") ||
-				(_interpType == INTERPRETER_TADS && (type == "TAD2" || type == "TAD3")) ||
+				(_interpType == INTERPRETER_TADS2 && type == "TAD2") ||
+				(_interpType == INTERPRETER_TADS3 && type == "TAD3") ||
 				(_interpType == INTERPRETER_HUGO && type == "HUGO")
 			) {
 				// Game executable
diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index d5b0ce8..491c6c8 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -26,7 +26,8 @@
 #include "glk/scott/detection.h"
 #include "glk/scott/scott.h"
 #include "glk/tads/detection.h"
-#include "glk/tads/tads.h"
+#include "glk/tads/tads2/tads2.h"
+#include "glk/tads/tads3/tads3.h"
 
 #include "base/plugins.h"
 #include "common/md5.h"
@@ -100,6 +101,7 @@ bool Glk::GlkEngine::hasFeature(EngineFeature f) const {
 }
 
 Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
+	Glk::TADS::TADSDescriptor td;
 	assert(engine);
 
 	// Populate the game description
@@ -135,8 +137,11 @@ Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
 		*engine = new Glk::Frotz::Frotz(syst, gameDesc);
 	} else if (Glk::Scott::ScottMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
 		*engine = new Glk::Scott::Scott(syst, gameDesc);
-	} else if (Glk::TADS::TADSMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
-		*engine = new Glk::TADS::TADS(syst, gameDesc);
+	} else if ((td = Glk::TADS::TADSMetaEngine::findGame(gameDesc._gameId.c_str())).description) {
+		if (td.isTADS3)
+			*engine = new Glk::TADS::TADS3::TADS3(syst, gameDesc);
+		else
+			*engine = new Glk::TADS::TADS2::TADS2(syst, gameDesc);
 	} else {
 		return Common::kNoGameDataFoundError;
 	}
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index 4ac9566..81a36d8 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -50,7 +50,8 @@ enum InterpreterType {
 	INTERPRETER_NITFOL = 11,
 	INTERPRETER_SCARE = 12,
 	INTERPRETER_SCOTT = 13,
-	INTERPRETER_TADS = 14
+	INTERPRETER_TADS2 = 14,
+	INTERPRETER_TADS3 = 15
 };
 
 /**
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index b75772d..bb55c98 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -44,7 +44,9 @@ MODULE_OBJS := \
 	scott/detection.o \
 	scott/scott.o \
 	tads/detection.o \
-	tads/tads.o
+	tads/tads.o \
+	tads/tads2/tads2.o \
+	tads/tads3/tads3.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_GLK), DYNAMIC_PLUGIN)
diff --git a/engines/glk/tads/detection.cpp b/engines/glk/tads/detection.cpp
index ef2caa2..6627b8f 100644
--- a/engines/glk/tads/detection.cpp
+++ b/engines/glk/tads/detection.cpp
@@ -30,17 +30,18 @@ namespace Glk {
 namespace TADS {
 
 void TADSMetaEngine::getSupportedGames(PlainGameList &games) {
-	for (const PlainGameDescriptor *pd = TADS_GAME_LIST; pd->gameId; ++pd)
+	for (const TADSDescriptor *pd = TADS_GAME_LIST; pd->gameId; ++pd) {
 		games.push_back(*pd);
+	}
 }
 
-PlainGameDescriptor TADSMetaEngine::findGame(const char *gameId) {
-	for (const PlainGameDescriptor *pd = TADS_GAME_LIST; pd->gameId; ++pd) {
+TADSDescriptor TADSMetaEngine::findGame(const char *gameId) {
+	for (const TADSDescriptor *pd = TADS_GAME_LIST; pd->gameId; ++pd) {
 		if (!strcmp(gameId, pd->gameId))
 			return *pd;
 	}
 
-	return PlainGameDescriptor();;
+	return TADSDescriptor();;
 }
 
 bool TADSMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
@@ -49,8 +50,8 @@ bool TADSMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &ga
 
 	// Loop through the files of the folder
 	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
-		if (file->isDirectory() || !(file->getName().hasSuffixIgnoreCase(".saga")
-				|| file->getName().hasSuffixIgnoreCase(".dat")))
+		if (file->isDirectory() || !(file->getName().hasSuffixIgnoreCase(".gam")
+				|| file->getName().hasSuffixIgnoreCase(".t3")))
 			continue;
 
 		if (gameFile.open(*file)) {
@@ -63,7 +64,7 @@ bool TADSMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &ga
 
 			if (p->_filesize) {
 				// Found a match
-				PlainGameDescriptor gameDesc = findGame(p->_gameId);
+				TADSDescriptor gameDesc = findGame(p->_gameId);
 				DetectedGame gd(p->_gameId, gameDesc.description, Common::EN_ANY, Common::kPlatformUnknown);
 				gd.addExtraEntry("filename", file->getName());
 
diff --git a/engines/glk/tads/detection.h b/engines/glk/tads/detection.h
index 59a6bc7..3e4d3e3 100644
--- a/engines/glk/tads/detection.h
+++ b/engines/glk/tads/detection.h
@@ -30,6 +30,22 @@ namespace Glk {
 namespace TADS {
 
 /**
+ * TADS game descriptior
+ */
+struct TADSDescriptor {
+	const char *gameId;
+	const char *description;
+	bool isTADS3;
+
+	operator PlainGameDescriptor() const {
+		PlainGameDescriptor pd;
+		pd.gameId = gameId;
+		pd.description = description;
+		return pd;
+	}
+};
+
+/**
  * Meta engine for TADS interpreter
  */
 class TADSMetaEngine {
@@ -42,7 +58,7 @@ public:
 	/**
 	 * Returns a game description for the given game Id, if it's supported
 	 */
-	static PlainGameDescriptor findGame(const char *gameId);
+	static TADSDescriptor findGame(const char *gameId);
 
 	/**
 	 * Detect supported games
diff --git a/engines/glk/tads/detection_tables.h b/engines/glk/tads/detection_tables.h
index eb9441f..c8fe318 100644
--- a/engines/glk/tads/detection_tables.h
+++ b/engines/glk/tads/detection_tables.h
@@ -36,9 +36,10 @@ struct TADSGame {
 	int32 _filesize;
 };
 
-const PlainGameDescriptor TADS_GAME_LIST[] = {
-	{ "tads",     "TADS" },
-	{ nullptr, nullptr }
+const TADSDescriptor TADS_GAME_LIST[] = {
+	{ "tads2", "TADS 2 Game", false },
+	{ "tads3", "TADS 3 Game", true },
+	{ nullptr, nullptr, false }
 };
 
 const TADSGame TADS_GAMES[] = {
diff --git a/engines/glk/tads/tads.cpp b/engines/glk/tads/tads.cpp
index 8a40dbd..8301813 100644
--- a/engines/glk/tads/tads.cpp
+++ b/engines/glk/tads/tads.cpp
@@ -30,10 +30,6 @@ namespace TADS {
 TADS::TADS(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
 }
 
-void TADS::runGame(Common::SeekableReadStream *gameFile) {
-	// TODO
-}
-
 Common::Error TADS::loadGameData(strid_t file) {
 	// TODO
 	return Common::kNoError;
diff --git a/engines/glk/tads/tads.h b/engines/glk/tads/tads.h
index 23caef7..8582897 100644
--- a/engines/glk/tads/tads.h
+++ b/engines/glk/tads/tads.h
@@ -45,11 +45,6 @@ public:
 	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_SCOTT; }
 
 	/**
-	 * Execute the game
-	 */
-	virtual void runGame(Common::SeekableReadStream *gameFile) override;
-
-	/**
 	 * Load a savegame from the passed stream
 	 */
 	virtual Common::Error loadGameData(strid_t file) override;
diff --git a/engines/glk/tads/tads2/tads2.cpp b/engines/glk/tads/tads2/tads2.cpp
new file mode 100644
index 0000000..81f7272
--- /dev/null
+++ b/engines/glk/tads/tads2/tads2.cpp
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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/tads/tads2/tads2.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+TADS2::TADS2(OSystem *syst, const GlkGameDescription &gameDesc) : TADS(syst, gameDesc) {
+}
+
+void TADS2::runGame(Common::SeekableReadStream *gameFile) {
+	// TODO
+}
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
diff --git a/engines/glk/tads/tads2/tads2.h b/engines/glk/tads/tads2/tads2.h
new file mode 100644
index 0000000..e07b5c8
--- /dev/null
+++ b/engines/glk/tads/tads2/tads2.h
@@ -0,0 +1,57 @@
+/* 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_TADS_TADS2
+#define GLK_TADS_TADS2
+
+#include "glk/tads/tads.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+/**
+ * TADS 2 game interpreter
+ */
+class TADS2 : public TADS {
+public:
+	/**
+	 * Constructor
+	 */
+	TADS2(OSystem *syst, const GlkGameDescription &gameDesc);
+
+	/**
+	 * Execute the game
+	 */
+	virtual void runGame(Common::SeekableReadStream *gameFile) override;
+
+	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_TADS2; }
+};
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/tads/tads3/tads3.cpp b/engines/glk/tads/tads3/tads3.cpp
new file mode 100644
index 0000000..911e87f
--- /dev/null
+++ b/engines/glk/tads/tads3/tads3.cpp
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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/tads/tads3/tads3.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS3 {
+
+TADS3::TADS3(OSystem *syst, const GlkGameDescription &gameDesc) : TADS(syst, gameDesc) {
+}
+
+void TADS3::runGame(Common::SeekableReadStream *gameFile) {
+	// TODO
+}
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
diff --git a/engines/glk/tads/tads3/tads3.h b/engines/glk/tads/tads3/tads3.h
new file mode 100644
index 0000000..2fc4961
--- /dev/null
+++ b/engines/glk/tads/tads3/tads3.h
@@ -0,0 +1,57 @@
+/* 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_TADS_TADS3
+#define GLK_TADS_TADS3
+
+#include "glk/tads/tads.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS3 {
+
+/**
+ * TADS 3 game interpreter
+ */
+class TADS3 : public TADS {
+public:
+	/**
+	 * Constructor
+	 */
+	TADS3(OSystem *syst, const GlkGameDescription &gameDesc);
+
+	/**
+	 * Execute the game
+	 */
+	virtual void runGame(Common::SeekableReadStream *gameFile) override;
+
+	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_TADS3; }
+};
+
+} // End of namespace TADS3
+} // End of namespace TADS
+} // End of namespace Glk
+
+#endif


Commit: 75f4d34070a874437f6f339bac590f99d4b28f65
    https://github.com/scummvm/scummvm/commit/75f4d34070a874437f6f339bac590f99d4b28f65
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Add first detection entry, further fallback detection

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


diff --git a/engines/glk/tads/detection.cpp b/engines/glk/tads/detection.cpp
index 6627b8f..5f0bfea 100644
--- a/engines/glk/tads/detection.cpp
+++ b/engines/glk/tads/detection.cpp
@@ -22,6 +22,7 @@
 
 #include "glk/tads/detection.h"
 #include "glk/tads/detection_tables.h"
+#include "common/debug.h"
 #include "common/file.h"
 #include "common/md5.h"
 #include "engines/game.h"
@@ -45,34 +46,52 @@ TADSDescriptor TADSMetaEngine::findGame(const char *gameId) {
 }
 
 bool TADSMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
-	Common::File gameFile;
-	Common::String md5;
-
 	// Loop through the files of the folder
 	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
-		if (file->isDirectory() || !(file->getName().hasSuffixIgnoreCase(".gam")
-				|| file->getName().hasSuffixIgnoreCase(".t3")))
+		// Check for a recognised filename
+		Common::String filename = file->getName();
+		if (file->isDirectory() || !(filename.hasSuffixIgnoreCase(".gam")
+				|| filename.hasSuffixIgnoreCase(".blorb")))
 			continue;
 
-		if (gameFile.open(*file)) {
-			md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+		// Open up the file and calculate the md5
+		Common::File gameFile;
+		if (!gameFile.open(*file))
+			continue;
+		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+		size_t filesize = gameFile.size();
+		gameFile.close();
 
-			// Scan through the TADS game list for a match
-			const TADSGame *p = TADS_GAMES;
-			while (p->_md5 && p->_filesize != gameFile.size() && md5 != p->_md5)
-				++p;
+		// Check for known games
+		const TADSGameDescription *p = TADS_GAMES;
+		while (p->_gameId && p->_md5 && (md5 != p->_md5 || filesize != p->_filesize))
+			++p;
 
-			if (p->_filesize) {
-				// Found a match
-				TADSDescriptor gameDesc = findGame(p->_gameId);
-				DetectedGame gd(p->_gameId, gameDesc.description, Common::EN_ANY, Common::kPlatformUnknown);
-				gd.addExtraEntry("filename", file->getName());
+		DetectedGame gd;
+		if (!p->_gameId) {
+			if (!filename.hasSuffixIgnoreCase(".gam"))
+				continue;
 
-				gameList.push_back(gd);
-			}
+			if (gDebugLevel > 0) {
+				// Print an entry suitable for putting into the detection_tables.h, using the
+				Common::String fname = filename;
+				const char *dot = strchr(fname.c_str(), '.');
+				if (dot)
+					fname = Common::String(fname.c_str(), dot);
 
-			gameFile.close();
+				debug("ENTRY0(\"%s\", \"%s\", %lu),",
+					fname.c_str(), md5.c_str(), filesize);
+			}
+			const TADSDescriptor &desc = TADS_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.addExtraEntry("filename", filename);
+		gameList.push_back(gd);
 	}
 
 	return !gameList.empty();
diff --git a/engines/glk/tads/detection_tables.h b/engines/glk/tads/detection_tables.h
index c8fe318..d4d5709 100644
--- a/engines/glk/tads/detection_tables.h
+++ b/engines/glk/tads/detection_tables.h
@@ -28,22 +28,32 @@ namespace Glk {
 namespace TADS {
 
 /**
- * Game descriptor
+ * Game description
  */
-struct TADSGame {
-	const char *_md5;
-	const char *_gameId;
-	int32 _filesize;
+struct TADSGameDescription {
+	const char *const _gameId;
+	const char *const _extra;
+	const char *const _md5;
+	size_t _filesize;
+	Common::Language _language;
 };
 
 const TADSDescriptor TADS_GAME_LIST[] = {
+	// TADS 2 Games
 	{ "tads2", "TADS 2 Game", false },
+	{ "oncefuture", "Once and Future", false },
+
+	// TADS 3 Games
 	{ "tads3", "TADS 3 Game", true },
 	{ nullptr, nullptr, false }
 };
 
-const TADSGame TADS_GAMES[] = {
-	{ nullptr, nullptr, 0 }
+#define ENTRY0(ID, MD5, FILESIZE) { ID, "", MD5, FILESIZE, Common::EN_ANY }
+#define TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY }
+
+const TADSGameDescription TADS_GAMES[] = {
+	ENTRY0("oncefuture", "4ed995d0784520ca6f0ec5391d92f4d8", 909993),
+	TABLE_END_MARKER
 };
 
 } // End of namespace Frotz


Commit: d2836360fbf4f8ec5f90cbaba99002bb23e0aeb6
    https://github.com/scummvm/scummvm/commit/d2836360fbf4f8ec5f90cbaba99002bb23e0aeb6
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: GLK window initialization

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


diff --git a/engines/glk/tads/tads.cpp b/engines/glk/tads/tads.cpp
index 8301813..aa30927 100644
--- a/engines/glk/tads/tads.cpp
+++ b/engines/glk/tads/tads.cpp
@@ -28,6 +28,36 @@ namespace Glk {
 namespace TADS {
 
 TADS::TADS(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+	/*
+	 * GLK Initialization
+	 */
+	mainwin = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+
+	if (!mainwin)
+		error("fatal: could not open window!\n");
+
+	// get default colors for main window
+	if (!glk_style_measure(mainwin, style_Normal, stylehint_TextColor, &mainfg))
+		mainfg = 0;
+
+	if (!glk_style_measure(mainwin, style_Normal, stylehint_BackColor, &mainbg))
+		mainbg = 0;
+
+	// get default colors for status window
+	statuswin = glk_window_open(mainwin, winmethod_Above | winmethod_Fixed, 1,
+		wintype_TextGrid, 0);
+
+	if (!glk_style_measure(statuswin, style_Normal, stylehint_TextColor, &statusfg))
+		statusfg = 0;
+
+	if (!glk_style_measure(statuswin, style_Normal, stylehint_BackColor, &statusbg))
+		statusbg = 0;
+
+	// close status window; reopened on request
+	glk_window_close(statuswin, 0);
+	statuswin = nullptr;
+
+	glk_set_window(mainwin);
 }
 
 Common::Error TADS::loadGameData(strid_t file) {
diff --git a/engines/glk/tads/tads.h b/engines/glk/tads/tads.h
index 8582897..0f1565a 100644
--- a/engines/glk/tads/tads.h
+++ b/engines/glk/tads/tads.h
@@ -33,6 +33,10 @@ namespace TADS {
  * tads game interpreter
  */
 class TADS : public GlkAPI {
+protected:
+	winid_t mainwin, statuswin;
+	glui32 mainfg, mainbg;
+	glui32 statusfg, statusbg;
 public:
 	/**
 	 * Constructor


Commit: 5ab65b0954ba69ed31edfdbd563a549f7da692af
    https://github.com/scummvm/scummvm/commit/5ab65b0954ba69ed31edfdbd563a549f7da692af
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: TADS2: Add ler file

Changed paths:
  A engines/glk/tads/tads2/ler.cpp
  A engines/glk/tads/tads2/ler.h
    engines/glk/module.mk


diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index bb55c98..9a3aa37 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -45,6 +45,7 @@ MODULE_OBJS := \
 	scott/scott.o \
 	tads/detection.o \
 	tads/tads.o \
+	tads/tads2/ler.o \
 	tads/tads2/tads2.o \
 	tads/tads3/tads3.o
 
diff --git a/engines/glk/tads/tads2/ler.cpp b/engines/glk/tads/tads2/ler.cpp
new file mode 100644
index 0000000..9213d0b
--- /dev/null
+++ b/engines/glk/tads/tads2/ler.cpp
@@ -0,0 +1,179 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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 "ler.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv) {
+    int    outlen = 0;
+    int    argi   = 0;
+    int    len;
+    char   buf[20];
+    char  *p = nullptr;
+    char   fmtchar;
+
+    while (*fmt != '\0' && outbufl > 1) {
+        switch(*fmt) {
+        case '\\':
+            ++fmt;
+            len = 1;
+            switch(*fmt) {
+            case '\0':
+                --fmt;
+                break;
+            case '\n':
+                p = "\n";
+                break;
+            case '\t':
+                p = "\t";
+                break;
+            default:
+                p = fmt;
+                break;
+            }
+            break;
+            
+        case '%':
+            ++fmt;
+            fmtchar = *fmt;
+            if (argi >= argc) fmtchar = 1;          // too many - ignore it
+            switch(fmtchar) {
+            case '\0':
+                --fmt;
+                len = 0;
+                break;
+                
+            case '%':
+                p = "%";
+                len = 1;
+                break;
+                
+            case 'd':
+                sprintf(buf, "%d", argv[argi].erraint);
+                len = strlen(buf);
+                p = buf;
+                break;
+                
+            case 'u':
+                sprintf(buf, "%u", argv[argi].erraint);
+                len = strlen(buf);
+                p = buf;
+                break;
+                
+            case 's':
+                p = argv[argi].errastr;
+                len = strlen(p);
+                break;
+                
+            default:
+                p = "";
+                len = 0;
+                --argi;
+                break;
+            }
+            ++argi;
+            break;
+            
+        default:
+            p = fmt;
+            len = 1;
+            break;
+        }
+
+        /* copy output that was set up above */
+        if (len != 0) {
+            if (outbufl >= len) {
+                memcpy(outbuf, p, (size_t)len);
+                outbufl -= len;
+                outbuf += len;
+            } else if (outbufl > 1) {
+                memcpy(outbuf, p, (size_t)outbufl - 1);
+                outbufl = 1;
+            }
+            outlen += len;
+        }
+        ++fmt;
+    }
+
+    // add a null terminator
+    if (outbufl != 0)
+        *outbuf++ = '\0';
+
+    // return the length
+    return outlen;
+}
+
+#if defined(DEBUG) && !defined(ERR_NO_MACRO)
+void errjmp(jmp_buf buf, int e) {
+    longjmp(buf, e);
+}
+#endif /* DEBUG */
+
+#ifdef ERR_NO_MACRO
+
+void errsign(errcxdef *ctx, int e, char *facility) {
+    strncpy(ctx->errcxptr->errfac, facility, ERRFACMAX);
+    ctx->errcxptr->errfac[ERRFACMAX] = '\0';
+    ctx->errcxofs = 0;
+    longjmp(ctx->errcxptr->errbuf, e);
+}
+
+void errsigf(errcxdef *ctx, char *facility, int e) {
+    errargc(ctx, 0);
+    errsign(ctx, e, facility);
+}
+
+char *errstr(errcxdef *ctx, const char *str, int len) {
+    char *ret = &ctx->errcxbuf[ctx->errcxofs];
+    
+    memcpy(ret, str, (size_t)len);
+    ret[len] = '\0';
+    ctx->errcxofs += len + 1;
+    return(ret);
+}
+
+void errrse1(errcxdef *ctx, errdef *fr) {
+    errargc(ctx, fr->erraac);
+    memcpy(ctx->errcxptr->erraav, fr->erraav,
+           (size_t)(fr->erraac * sizeof(erradef)));
+    errsign(ctx, fr->errcode, fr->errfac);
+}
+
+void errlogn(errcxdef *ctx, int err, char *facility) {
+    ctx->errcxofs = 0;
+    (*ctx->errcxlog)(ctx->errcxlgc, facility, err, ctx->errcxptr->erraac,
+                     ctx->errcxptr->erraav);
+}
+
+void errlogf(errcxdef *ctx, char *facility, int err) {
+    errargc(ctx, 0);
+    errlogn(ctx, err, facility);
+}
+
+#endif /* ERR_NO_MACRO */
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
diff --git a/engines/glk/tads/tads2/ler.h b/engines/glk/tads/tads2/ler.h
new file mode 100644
index 0000000..5301a0f
--- /dev/null
+++ b/engines/glk/tads/tads2/ler.h
@@ -0,0 +1,287 @@
+/* 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_TADS_TADS2_LER
+#define GLK_TADS_TADS2_LER
+
+#include "common/scummsys.h"
+#include "common/stream.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+// maximum length of a facility identifier
+#define ERRFACMAX    6
+
+union erradef {
+    int   erraint;		// integer argument
+    char *errastr;		// text string argument
+};
+
+struct errdef {
+    errdef *       errprv;               // previous error frame
+    int            errcode;              // error code of exception being handled
+    char           errfac[ERRFACMAX+1];  // facility of current error
+    erradef        erraav[10];           // parameters for error
+    int            erraac;               // count of parameters in argc
+//    jmp_buf        errbuf;               // jump buffer for current error frame
+};
+
+#define ERRBUFSIZ 512
+
+// seek location record for an error message by number
+struct errmfdef {
+    uint  errmfnum;   // error number
+    size_t errmfseek; // seek location of this message
+};
+
+struct errcxdef {
+    errdef   *errcxptr;               // current error frame
+    void    (*errcxlog)(void *, char *fac, int err, int argc, erradef *);
+                                      // error logging callback function
+    void     *errcxlgc;               // context for error logging callback
+    int       errcxofs;               // offset in argument buffer
+    char      errcxbuf[ERRBUFSIZ];    // space for argument strings
+    Common::SeekableReadStream *errcxfp;                // message file, if one is being used
+    errmfdef *errcxseek;              // seek locations of messages in file
+    uint      errcxsksz;              // size of errcxseek array
+    size_t    errcxbase;              // offset in physical file of logical error file
+    struct appctxdef *errcxappctx;    // host application context
+};
+typedef struct errcxdef errcxdef;
+
+// begin protected code
+#define ERRBEGIN(ctx) \
+  { \
+    errdef fr_; \
+    if ((fr_.errcode = setjmp(fr_.errbuf)) == 0) \
+    { \
+      fr_.errprv = (ctx)->errcxptr; \
+      (ctx)->errcxptr = &fr_;
+
+// end protected code, begin error handler
+#define ERRCATCH(ctx, e) \
+      assert(1==1 && (ctx)->errcxptr != fr_.errprv); \
+      (ctx)->errcxptr = fr_.errprv; \
+    } \
+    else \
+    { \
+      assert(2==2 && (ctx)->errcxptr != fr_.errprv); \
+      (e) = fr_.errcode; \
+      (ctx)->errcxptr = fr_.errprv;
+
+// retrieve argument (int, string) in current error frame
+#define errargint(argnum) (fr_.erraav[argnum].erraint)
+#define errargstr(argnum) (fr_.erraav[argnum].errastr)
+
+    
+#define ERREND(ctx) \
+    } \
+  }
+
+// end protected code, begin cleanup (no handling; just cleaning up)
+#define ERRCLEAN(ctx) \
+      assert((ctx)->errcxptr != fr_.errprv); \
+      (ctx)->errcxptr = fr_.errprv; \
+    } \
+    else \
+    { \
+      assert((ctx)->errcxptr != fr_.errprv); \
+      (ctx)->errcxptr = fr_.errprv;
+
+#define ERRENDCLN(ctx) \
+      errrse(ctx); \
+    } \
+  }
+
+
+
+// argument types for errors with arguments
+#define ERRTINT  erraint
+#define ERRTSTR  errastr
+
+// set argument count in error frame
+#define errargc(ctx,cnt) ((ctx)->errcxptr->erraac=(cnt))
+
+// enter string argument; returns pointer to argument used in errargv
+#ifdef ERR_NO_MACRO
+char *errstr(errcxdef *ctx, const char *str, int len);
+#else /* ERR_NO_MACRO */
+  
+#define errstr(ctx,str,len) \
+  ((memcpy(&(ctx)->errcxbuf[(ctx)->errcxofs],str,(size_t)len), \
+   (ctx)->errcxofs += (len), \
+   (ctx)->errcxbuf[(ctx)->errcxofs++] = '\0'), \
+   &(ctx)->errcxbuf[(ctx)->errcxofs-(len)-1])
+
+#endif /* ERR_NO_MACRO */
+
+/* set argument in error frame argument vector */
+#define errargv(ctx,index,typ,arg) \
+  ((ctx)->errcxptr->erraav[index].typ=(arg))
+
+// signal an error with argument count already set
+#ifdef ERR_NO_MACRO
+void errsign(errcxdef *ctx, int e, char *facility);
+#else /* ERR_NO_MACRO */
+# ifdef DEBUG
+void errjmp(jmp_buf buf, int e);
+#  define errsign(ctx, e, fac) \
+   (strncpy((ctx)->errcxptr->errfac, fac, ERRFACMAX),\
+    (ctx)->errcxptr->errfac[ERRFACMAX]='\0',\
+    (ctx)->errcxofs=0, errjmp((ctx)->errcxptr->errbuf, e))
+# else /* DEBUG */
+#  define errsign(ctx, e, fac) \
+   (strncpy((ctx)->errcxptr->errfac, fac, ERRFACMAX),\
+    (ctx)->errcxptr->errfac[ERRFACMAX]='\0',\
+    (ctx)->errcxofs=0, longjmp((ctx)->errcxptr->errbuf, e))
+# endif /* DEBUG */
+#endif /* ERR_NO_MACRO */
+
+
+// signal an error with no arguments
+#ifdef ERR_NO_MACRO
+void errsigf(errcxdef *ctx, char *facility, int err);
+#else /* ERR_NO_MACRO */
+#define errsigf(ctx, fac, e) (errargc(ctx,0),errsign(ctx,e,fac))
+#endif /* ERR_NO_MACRO */
+  
+// signal an error with one argument
+#define errsigf1(ctx, fac, e, typ1, arg1) \
+  (errargv(ctx,0,typ1,arg1),errargc(ctx,1),errsign(ctx,e,fac))
+
+// signal an error with two arguments
+#define errsigf2(ctx, fac, e, typ1, arg1, typ2, arg2) \
+  (errargv(ctx,0,typ1,arg1), errargv(ctx,1,typ2,arg2), \
+   errargc(ctx,2), errsign(ctx,e,fac))
+
+// resignal the current error - only usable within exception handlers
+#ifdef ERR_NO_MACRO
+void errrse1(errcxdef *ctx, errdef *fr);
+# define errrse(ctx) errrse1(ctx, &fr_)
+#else /* ERR_NO_MACRO */
+
+// void errrse(errcxdef *ctx);
+# define errrse(ctx) \
+  (errargc(ctx, fr_.erraac),\
+   memcpy((ctx)->errcxptr->erraav, fr_.erraav, \
+    (size_t)(fr_.erraac*sizeof(erradef))),\
+   errsign(ctx, fr_.errcode, fr_.errfac))
+
+#endif /* ERR_NO_MACRO */
+
+/**
+ *   For use in an error handler (ERRCATCH..ERREND) only: Copy the
+ *   parameters from the error currently being handled to the enclosing
+ *   frame.  This is useful when "keeping" an error being handled - i.e.,
+ *   the arguments will continue to be used outside of the
+ *   ERRCATCH..ERREND code. 
+ */
+#define errkeepargs(ctx) errcopyargs(ctx, &fr_)
+
+/** 
+ *   copy the parameters for an error from another frame into the current
+ *   frame - this can be used when we want to be able to display an error
+ *   that occurred in an inner frame within code that is protected by a
+ *   new enclosing error frame 
+ */
+#define errcopyargs(ctx, fr) \
+   (errargc((ctx), (fr)->erraac), \
+    memcpy((ctx)->errcxptr->erraav, (fr)->erraav, \
+           (size_t)((fr)->erraac*sizeof(erradef))))
+
+// log error that's been caught, using arguments already caught
+#define errclog(ctx) \
+ ((*(ctx)->errcxlog)((ctx)->errcxlgc,fr_.errfac,fr_.errcode,\
+  fr_.erraac,fr_.erraav))
+
+// log an error that's been set up but not signalled yet
+#define errprelog(ctx, err) \
+ ((*(ctx)->errcxlog)((ctx)->errcxlgc,(ctx)->errcxptr->errfac,\
+   err,(ctx)->errcxptr->erraac,\
+   (ctx)->errcxptr->erraav))
+
+// log an error (no signalling, just reporting)
+#ifdef ERR_NO_MACRO
+void errlogn(errcxdef *ctx, int err, char *facility);
+#else /* ERR_NO_MACRO */
+
+#define errlogn(ctx,err,fac) \
+ ((ctx)->errcxofs=0,\
+  (*(ctx)->errcxlog)((ctx)->errcxlgc,fac,err,(ctx)->errcxptr->erraac,\
+  (ctx)->errcxptr->erraav))
+
+#endif /* ERR_NO_MACRO */
+
+// log an error with no arguments
+#ifdef ERR_NO_MACRO
+void errlogf(errcxdef *ctx, char *facility, int err);
+#else /* ERR_NO_MACRO */
+
+// void errlogf(errcxdef *ctx, char *facility, int err);
+#define errlogf(ctx,fac,err) (errargc(ctx,0),errlogn(ctx,err,fac))
+
+#endif /* ERR_NO_MACRO */
+
+// log an error with one argument
+#define errlogf1(ctx, fac, e, typ1, arg1) \
+ (errargv(ctx,0,typ1,arg1),errargc(ctx,1),errlogn(ctx,e,fac))
+  
+// log an error with two arguments
+#define errlogf2(ctx, fac, e, typ1, arg1, typ2, arg2) \
+ (errargv(ctx,0,typ1,arg1),errargv(ctx,1,typ2,arg2),\
+  errargc(ctx,2),errlogn(ctx,e,fac))
+
+
+/**
+ *   Format an error message, sprintf-style, using arguments in an
+ *   erradef array (which is passed to the error-logging callback).
+ *   Returns the length of the output string, even if the actual
+ *   output string was truncated because the outbuf was too short.
+ *   (If called with outbufl == 0, nothing will be written out, but
+ *   the size of the buffer needed, minus the terminating null byte,
+ *   will be computed and returned.)
+ */
+extern int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv);
+  
+// get the text of an error
+void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err);
+  
+// initialize error subsystem, opening error message file if necessary
+void errini(errcxdef *ctx, Common::SeekableReadStream *fp);
+
+// allocate and initialize error context, free error context
+errcxdef *lerini();
+void      lerfre(errcxdef *ctx);
+
+// error message structure - number + text
+struct errmdef {
+    uint   errmerr;		// error number
+    char  *errmtxt;     // text of error message
+};
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
+
+#endif


Commit: 41b62ab00db1d1730b1d61b77fda0df2c65753c0
    https://github.com/scummvm/scummvm/commit/41b62ab00db1d1730b1d61b77fda0df2c65753c0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Add a number of GLK interface methods

Changed paths:
  A engines/glk/tads/tads2/os.cpp
  A engines/glk/tads/tads2/os.h
    engines/glk/glk_api.cpp
    engines/glk/glk_api.h
    engines/glk/module.mk
    engines/glk/tads/tads.cpp
    engines/glk/tads/tads.h
    engines/glk/tads/tads2/ler.cpp
    engines/glk/tads/tads2/ler.h
    engines/glk/tads/tads2/tads2.cpp
    engines/glk/tads/tads2/tads2.h


diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index 19f8aef..32d6177 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -394,7 +394,7 @@ void GlkAPI::glk_put_string_stream(strid_t str, const char *s) {
 	str->putBuffer(s, strlen(s));
 }
 
-void GlkAPI::glk_put_buffer(char *buf, glui32 len) {
+void GlkAPI::glk_put_buffer(const char *buf, glui32 len) {
 	_streams->getCurrent()->putBuffer(buf, len);
 }
 
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
index fe25930..3081ac2 100644
--- a/engines/glk/glk_api.h
+++ b/engines/glk/glk_api.h
@@ -100,7 +100,7 @@ public:
 	void glk_put_char_stream(strid_t str, unsigned char ch);
 	void glk_put_string(const char *s);
 	void glk_put_string_stream(strid_t str, const char *s);
-	void glk_put_buffer(char *buf, glui32 len);
+	void glk_put_buffer(const char *buf, glui32 len);
 	void glk_put_buffer_stream(strid_t str, const char *buf, glui32 len);
 	void glk_set_style(glui32 styl);
 	void glk_set_style_stream(strid_t str, glui32 styl);
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 9a3aa37..89be6a2 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -46,6 +46,7 @@ MODULE_OBJS := \
 	tads/detection.o \
 	tads/tads.o \
 	tads/tads2/ler.o \
+	tads/tads2/os.o \
 	tads/tads2/tads2.o \
 	tads/tads3/tads3.o
 
diff --git a/engines/glk/tads/tads.cpp b/engines/glk/tads/tads.cpp
index aa30927..5dc400b 100644
--- a/engines/glk/tads/tads.cpp
+++ b/engines/glk/tads/tads.cpp
@@ -31,33 +31,34 @@ TADS::TADS(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 	/*
 	 * GLK Initialization
 	 */
-	mainwin = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
 
-	if (!mainwin)
+	// Open the story window
+	story_win = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+	if (!story_win)
 		error("fatal: could not open window!\n");
 
 	// get default colors for main window
-	if (!glk_style_measure(mainwin, style_Normal, stylehint_TextColor, &mainfg))
+	if (!glk_style_measure(story_win, style_Normal, stylehint_TextColor, &mainfg))
 		mainfg = 0;
 
-	if (!glk_style_measure(mainwin, style_Normal, stylehint_BackColor, &mainbg))
+	if (!glk_style_measure(story_win, style_Normal, stylehint_BackColor, &mainbg))
 		mainbg = 0;
 
 	// get default colors for status window
-	statuswin = glk_window_open(mainwin, winmethod_Above | winmethod_Fixed, 1,
+	status_win = glk_window_open(story_win, winmethod_Above | winmethod_Fixed, 1,
 		wintype_TextGrid, 0);
 
-	if (!glk_style_measure(statuswin, style_Normal, stylehint_TextColor, &statusfg))
+	if (!glk_style_measure(status_win, style_Normal, stylehint_TextColor, &statusfg))
 		statusfg = 0;
 
-	if (!glk_style_measure(statuswin, style_Normal, stylehint_BackColor, &statusbg))
+	if (!glk_style_measure(status_win, style_Normal, stylehint_BackColor, &statusbg))
 		statusbg = 0;
 
 	// close status window; reopened on request
-	glk_window_close(statuswin, 0);
-	statuswin = nullptr;
+	glk_window_close(status_win, 0);
+	status_win = nullptr;
 
-	glk_set_window(mainwin);
+	glk_set_window(story_win);
 }
 
 Common::Error TADS::loadGameData(strid_t file) {
diff --git a/engines/glk/tads/tads.h b/engines/glk/tads/tads.h
index 0f1565a..ec9ea93 100644
--- a/engines/glk/tads/tads.h
+++ b/engines/glk/tads/tads.h
@@ -34,7 +34,7 @@ namespace TADS {
  */
 class TADS : public GlkAPI {
 protected:
-	winid_t mainwin, statuswin;
+	winid_t story_win, status_win;
 	glui32 mainfg, mainbg;
 	glui32 statusfg, statusbg;
 public:
diff --git a/engines/glk/tads/tads2/ler.cpp b/engines/glk/tads/tads2/ler.cpp
index 9213d0b..930fdf0 100644
--- a/engines/glk/tads/tads2/ler.cpp
+++ b/engines/glk/tads/tads2/ler.cpp
@@ -26,7 +26,9 @@ namespace Glk {
 namespace TADS {
 namespace TADS2 {
 
-int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv) {
+#define TRDLOGERR_PREFIX "\n[An error has occurred within TADS: "
+
+int errcxdef::errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv) {
     int    outlen = 0;
     int    argi   = 0;
     int    len;
@@ -125,55 +127,26 @@ int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv) {
     return outlen;
 }
 
-#if defined(DEBUG) && !defined(ERR_NO_MACRO)
-void errjmp(jmp_buf buf, int e) {
-    longjmp(buf, e);
-}
-#endif /* DEBUG */
-
-#ifdef ERR_NO_MACRO
-
-void errsign(errcxdef *ctx, int e, char *facility) {
-    strncpy(ctx->errcxptr->errfac, facility, ERRFACMAX);
-    ctx->errcxptr->errfac[ERRFACMAX] = '\0';
-    ctx->errcxofs = 0;
-    longjmp(ctx->errcxptr->errbuf, e);
-}
-
-void errsigf(errcxdef *ctx, char *facility, int e) {
-    errargc(ctx, 0);
-    errsign(ctx, e, facility);
-}
-
-char *errstr(errcxdef *ctx, const char *str, int len) {
-    char *ret = &ctx->errcxbuf[ctx->errcxofs];
-    
-    memcpy(ret, str, (size_t)len);
-    ret[len] = '\0';
-    ctx->errcxofs += len + 1;
-    return(ret);
+void errcxdef::errcxlog(void *ctx0, char *fac, int err, int argc, erradef *argv) {
+#ifdef TODO
+	errcxdef *ctx = (errcxdef *)ctx0;
+	char      buf[256];
+	char      msg[256];
+
+	// display the prefix message to the console and log file
+	sprintf(buf, TRDLOGERR_PREFIX, fac, err);
+	trdptf("%s", buf);
+	out_logfile_print(buf, false);
+
+	/* display the error message text to the console and log file */
+	errmsg(ctx, msg, (uint)sizeof(msg), err);
+	errfmt(buf, (int)sizeof(buf), msg, argc, argv);
+	trdptf("%s]\n", buf);
+	out_logfile_print(buf, false);
+	out_logfile_print("]", true);
+#endif
 }
 
-void errrse1(errcxdef *ctx, errdef *fr) {
-    errargc(ctx, fr->erraac);
-    memcpy(ctx->errcxptr->erraav, fr->erraav,
-           (size_t)(fr->erraac * sizeof(erradef)));
-    errsign(ctx, fr->errcode, fr->errfac);
-}
-
-void errlogn(errcxdef *ctx, int err, char *facility) {
-    ctx->errcxofs = 0;
-    (*ctx->errcxlog)(ctx->errcxlgc, facility, err, ctx->errcxptr->erraac,
-                     ctx->errcxptr->erraav);
-}
-
-void errlogf(errcxdef *ctx, char *facility, int err) {
-    errargc(ctx, 0);
-    errlogn(ctx, err, facility);
-}
-
-#endif /* ERR_NO_MACRO */
-
 } // End of namespace TADS2
 } // End of namespace TADS
 } // End of namespace Glk
diff --git a/engines/glk/tads/tads2/ler.h b/engines/glk/tads/tads2/ler.h
index 5301a0f..000aa09 100644
--- a/engines/glk/tads/tads2/ler.h
+++ b/engines/glk/tads/tads2/ler.h
@@ -25,6 +25,7 @@
 
 #include "common/scummsys.h"
 #include "common/stream.h"
+#include "common/algorithm.h"
 
 namespace Glk {
 namespace TADS {
@@ -49,26 +50,47 @@ struct errdef {
 
 #define ERRBUFSIZ 512
 
+class TADS2;
+
 // seek location record for an error message by number
 struct errmfdef {
     uint  errmfnum;   // error number
     size_t errmfseek; // seek location of this message
 };
 
-struct errcxdef {
-    errdef   *errcxptr;               // current error frame
-    void    (*errcxlog)(void *, char *fac, int err, int argc, erradef *);
-                                      // error logging callback function
+class errcxdef {
+public:
+	errdef   *errcxptr;               // current error frame
     void     *errcxlgc;               // context for error logging callback
     int       errcxofs;               // offset in argument buffer
     char      errcxbuf[ERRBUFSIZ];    // space for argument strings
-    Common::SeekableReadStream *errcxfp;                // message file, if one is being used
+    Common::SeekableReadStream *errcxfp;  // message file, if one is being used
     errmfdef *errcxseek;              // seek locations of messages in file
     uint      errcxsksz;              // size of errcxseek array
     size_t    errcxbase;              // offset in physical file of logical error file
-    struct appctxdef *errcxappctx;    // host application context
+    TADS2 *   errcxappctx;            // host application context
+public:
+	/**
+	 *   Format an error message, sprintf-style, using arguments in an
+	 *   erradef array (which is passed to the error-logging callback).
+	 *   Returns the length of the output string, even if the actual
+	 *   output string was truncated because the outbuf was too short.
+	 *   (If called with outbufl == 0, nothing will be written out, but
+	 *   the size of the buffer needed, minus the terminating null byte,
+	 *   will be computed and returned.)
+	 */
+	static int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv);
+ public:
+	errcxdef() : errcxptr(nullptr), errcxlgc(nullptr), errcxofs(0),
+		errcxseek(nullptr), errcxsksz(0), errcxbase(0), errcxappctx(nullptr) {
+		Common::fill(&errcxbuf[0], &errcxbuf[ERRBUFSIZ], '\0');
+	}
+
+	/**
+	 * Error logging method
+	 */
+	void errcxlog(void *ctx0, char *fac, int err, int argc, erradef *argv);
 };
-typedef struct errcxdef errcxdef;
 
 // begin protected code
 #define ERRBEGIN(ctx) \
@@ -252,18 +274,7 @@ void errlogf(errcxdef *ctx, char *facility, int err);
  (errargv(ctx,0,typ1,arg1),errargv(ctx,1,typ2,arg2),\
   errargc(ctx,2),errlogn(ctx,e,fac))
 
-
-/**
- *   Format an error message, sprintf-style, using arguments in an
- *   erradef array (which is passed to the error-logging callback).
- *   Returns the length of the output string, even if the actual
- *   output string was truncated because the outbuf was too short.
- *   (If called with outbufl == 0, nothing will be written out, but
- *   the size of the buffer needed, minus the terminating null byte,
- *   will be computed and returned.)
- */
-extern int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv);
-  
+ 
 // get the text of an error
 void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err);
   
diff --git a/engines/glk/tads/tads2/os.cpp b/engines/glk/tads/tads2/os.cpp
new file mode 100644
index 0000000..df7cb42
--- /dev/null
+++ b/engines/glk/tads/tads2/os.cpp
@@ -0,0 +1,309 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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/tads/tads2/os.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+OS::OS(OSystem *syst, const GlkGameDescription &gameDesc) : TADS(syst, gameDesc),
+		status_mode(0) {
+	Common::fill(&status_left[0], &status_left[OSS_STATUS_STRING_LEN], '\0');
+	Common::fill(&status_right[0], &status_right[OSS_STATUS_STRING_LEN], '\0');
+}
+
+void OS::os_terminate(int rc) {
+	glk_exit();
+}
+
+glui32 OS::oss_convert_prompt_type(int type) {
+	if (type == OS_AFP_OPEN)
+		return filemode_Read;
+	return filemode_ReadWrite;
+}
+
+glui32 OS::oss_convert_file_type(int type) {
+	if (type == OSFTSAVE)
+		return fileusage_SavedGame;
+	if (type == OSFTLOG || type == OSFTTEXT)
+		return fileusage_Transcript;
+	return fileusage_Data;
+}
+
+glui32 OS::oss_convert_fileref_to_string(frefid_t file_to_convert, char *buffer, int buf_len) {
+	char   temp_string[32];
+	glui32 value, i = 0, digit,
+		digit_flag = false,     // Have we put a digit in the string yet?
+		divisor = 1e9;          // The max 32-bit integer is 4294967295
+
+	// This could probably be done by using sprintf("%s%ld%s") but I don't want to risk it
+	value = (glui32)file_to_convert;
+	while (divisor != 1) {
+		digit = (char)(value / divisor);
+		if (digit != 0 || digit_flag) {     // This lets us handle, eg, 102
+			temp_string[i++] = digit + '0';
+			digit_flag = true;
+		}
+		value = value % divisor;
+		divisor /= 10;
+	}
+	temp_string[i++] = (char)value + '0';
+	temp_string[i] = 0;
+	if (strlen(temp_string) + strlen(OSS_FILEREF_STRING_PREFIX) +
+		strlen(OSS_FILEREF_STRING_SUFFIX) > (size_t)buf_len)
+		return false;
+	sprintf(buffer, "%s%s%s", OSS_FILEREF_STRING_PREFIX,
+		temp_string, OSS_FILEREF_STRING_SUFFIX);
+	return true;
+}
+
+frefid_t OS::oss_convert_string_to_fileref(char *buffer, glui32 usage) {
+	char temp_string[32];
+	glui32 value = 0, i, multiplier = 1;
+
+	// Does the buffer contain a hashed fileref?
+	if (oss_is_string_a_fileref(buffer)) {
+		// If so, we need only decode the string in the middle and return its value
+		strcpy(temp_string, buffer + strlen(OSS_FILEREF_STRING_PREFIX));
+		i = strlen(temp_string) - strlen(OSS_FILEREF_STRING_SUFFIX);
+		temp_string[i] = 0;
+		while (i != 0) {
+			i--;
+			value += ((glui32)(temp_string[i] - '0') * multiplier);
+			multiplier *= 10;
+		}
+		return ((frefid_t)value);
+	}
+
+	// If not, return the new fileref
+	return (glk_fileref_create_by_name(usage, os_get_root_name(buffer), 0));
+}
+
+bool OS::oss_is_string_a_fileref(char *buffer) {
+	if ((strncmp(buffer, OSS_FILEREF_STRING_PREFIX,
+		strlen(OSS_FILEREF_STRING_PREFIX)) == 0) &&
+		(strncmp(buffer + strlen(buffer) - strlen(OSS_FILEREF_STRING_SUFFIX),
+			OSS_FILEREF_STRING_SUFFIX,
+			strlen(OSS_FILEREF_STRING_SUFFIX)) == 0))
+		return true;
+	return false;
+}
+
+unsigned char OS::oss_convert_keystroke_to_tads(glui32 key) {
+	// Characters 0 - 255 we return per normal */
+	if (key <= 255)
+		return ((unsigned char)key);
+	switch (key) {
+	case keycode_Up:
+		return CMD_UP;
+	case keycode_Down:
+		return CMD_DOWN;
+	case keycode_Left:
+		return CMD_LEFT;
+	case keycode_Right:
+		return CMD_RIGHT;
+	case keycode_PageUp:
+		return CMD_PGUP;
+	case keycode_PageDown:
+		return CMD_PGDN;
+	case keycode_Home:
+		return CMD_HOME;
+	case keycode_End:
+		return CMD_END;
+	case keycode_Func1:
+		return CMD_F1;
+	case keycode_Func2:
+		return CMD_F2;
+	case keycode_Func3:
+		return CMD_F3;
+	case keycode_Func4:
+		return CMD_F4;
+	case keycode_Func5:
+		return CMD_F5;
+	case keycode_Func6:
+		return CMD_F6;
+	case keycode_Func7:
+		return CMD_F7;
+	case keycode_Func8:
+		return CMD_F8;
+	case keycode_Func9:
+		return CMD_F9;
+	case keycode_Func10:
+		return CMD_F10;
+	default:
+		return 0;
+	}
+}
+
+bool OS::oss_check_path(char *filename) {
+	return false;
+}
+
+void OS::oss_revert_path() {
+	// No implementation
+}
+
+osfildef *OS::oss_open_stream(char *buffer, glui32 tadsusage, glui32 tbusage,
+		glui32 fmode, glui32 rock) {
+	frefid_t fileref;
+	strid_t osf;
+	int      changed_dirs;
+
+	fileref = oss_convert_string_to_fileref(buffer,
+		oss_convert_file_type(tadsusage) | tbusage);
+	changed_dirs = oss_check_path(buffer);
+	osf = glk_stream_open_file(fileref, (FileMode)fmode, rock);
+	if (changed_dirs)
+		oss_revert_path();
+	return *osf;
+}
+
+void OS::oss_put_string_with_hilite(winid_t win, const char *str, size_t len) {
+	glk_set_window(win);
+	glk_put_buffer(str, len);
+}
+
+void OS::oss_draw_status_line(void) {
+	glui32 width, height, division;
+
+	if (status_win == nullptr) return;  // In case this is a CheapGlk port
+
+	glk_window_get_size(status_win, &width, &height);
+	if (height == 0) return;  // Don't bother if status is invisible
+	division = width - strlen(status_right) - 1;
+	glk_set_window(status_win);
+	glk_window_clear(status_win);
+	oss_put_string_with_hilite(status_win, status_left, strlen(status_left));
+	glk_window_move_cursor(status_win, division, 0);
+	glk_put_string(status_right);
+}
+
+void OS::oss_change_status_string(char *dest, const char *src, size_t len) {
+	if (len > OSS_STATUS_STRING_LEN - 1)
+		len = OSS_STATUS_STRING_LEN - 1;
+	memcpy(dest, src, len);
+	dest[len] = '\0';
+}
+
+void OS::oss_change_status_left(const char *str, size_t len) {
+	oss_change_status_string(status_left, str, len);
+	oss_draw_status_line();
+}
+
+void OS::oss_change_status_right(const char *str) {
+	oss_change_status_string(status_right, str, strlen(str));
+	oss_draw_status_line();
+}
+
+int OS::memicmp(char *s1, char *s2, int len) {
+	char *x1, *x2;
+	int i, result;
+
+	x1 = (char *)malloc(len); x2 = (char *)malloc(len);
+
+	if (!x1 || !x2) {
+		glk_set_window(story_win);
+		glk_put_string("memicmp has run out of memory. Quitting.\n");
+		os_waitc();
+		glk_exit();
+	}
+
+	for (i = 0; i < len; i++) {
+		if (Common::isUpper(s1[i]))
+			x1[i] = glk_char_to_lower((unsigned char)s1[i]);
+		else x1[i] = s1[i];
+
+		if (Common::isUpper(s2[i]))
+			x2[i] = glk_char_to_lower((unsigned char)s2[i]);
+		else x2[i] = s2[i];
+	}
+
+	result = memcmp(x1, x2, len);
+	free(x1);
+	free(x2);
+	return result;
+}
+
+void OS::os_flush() {
+	glk_tick();
+}
+
+void OS::os_print(const char *str, size_t len) {
+	int current_status_mode;
+
+	// Decide what to do based on our status mode
+	current_status_mode = os_get_status();
+	if (current_status_mode == OSS_STATUS_MODE_STORY) {
+		oss_put_string_with_hilite(story_win, str, len);
+	} else if (current_status_mode == OSS_STATUS_MODE_STATUS) {
+		const char *p;
+		size_t      rem;
+
+		// The string requires some fiddling for the status window
+		for (p = str, rem = len; rem != 0 && *p == '\n'; p++, --rem);
+		if (rem != 0 && p[rem - 1] == '\n')
+			--rem;
+
+		// if that leaves anything, update the statusline
+		if (rem != 0)
+			oss_change_status_left(p, rem);
+	}
+}
+
+void OS::os_expause() {
+	os_printz("(Strike any key to exit...)");
+	os_flush();
+	os_waitc();
+}
+
+int OS::oss_getc_from_window(winid_t win) {
+	static unsigned char buffered_char = 0;
+	int i;
+	event_t ev;
+
+	if (buffered_char != 0) {
+		i = (int)buffered_char;
+		buffered_char = 0;
+		return i;
+	}
+	glk_request_char_event(win);
+	do {
+		glk_select(&ev);
+		if (ev.type == evtype_Arrange)
+			oss_draw_status_line();
+	} while (ev.type != evtype_CharInput);
+	if (ev.val1 == keycode_Return)
+		ev.val1 = '\n';
+	else if (ev.val1 == keycode_Tab)
+		ev.val1 = '\t';
+	if (ev.val1 <= 255)
+		return ((int)ev.val1);
+
+	// We got a special character, so handle it appropriately
+	buffered_char = oss_convert_keystroke_to_tads(ev.val1);
+	return 0;
+}
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
diff --git a/engines/glk/tads/tads2/os.h b/engines/glk/tads/tads2/os.h
new file mode 100644
index 0000000..ba0058d
--- /dev/null
+++ b/engines/glk/tads/tads2/os.h
@@ -0,0 +1,334 @@
+/* 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_TADS_TADS2_OS
+#define GLK_TADS_TADS2_OS
+
+#include "glk/tads/tads.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+/**
+ * The character (or characters) which mark the beginning of a special fileref string.
+ * The important thing is that the string be one that is either not allowed in
+ * filenames on your platform or is unlikely to be the first part of a filename.
+ */
+#define OSS_FILEREF_STRING_PREFIX ":"
+
+/**
+ * The character (or characters) which mark the end of a special fileref string.
+ * Using this and OSS_FILEREF_STRING_PREFIX, you should be able to come up with
+ * something which forms an invalid filename
+ */
+#define OSS_FILEREF_STRING_SUFFIX ""
+
+/**
+ * Maximum length of status line text
+ */
+#define OSS_STATUS_STRING_LEN 80
+
+/**
+ * Important note: do not change these values when porting TADS.  These
+ * values can be used by games, so they must be the same on all platforms.  
+ */
+enum {
+	OS_AFP_OPEN = 1,			///< choose an existing file to open for reading
+	OS_AFP_SAVE = 2				///< choose a filename for saving to a file
+};
+
+/**
+ * File types.These type codes are used when opening or creating a file,
+ * so that the OS routine can set appropriate file system metadata
+ * to describe or find the file type.
+ *
+ * The type os_filetype_t is defined for documentary purposes; it's
+ * always just an int.
+ */
+enum os_filetype_t {
+	OSFTGAME  =  0,		///< a game data file (.gam)
+	OSFTSAVE  =  1,     ///< a saved game (.sav)
+	OSFTLOG   =  2,     ///< a transcript (log) file
+	OSFTSWAP  =  3,     ///< swap file
+	OSFTDATA  =  4,     ///< user data file (used with the TADS fopen() call)
+	OSFTCMD   =  5,     ///< QA command/log file
+	OSFTERRS  =  6,     ///< error message file
+	OSFTTEXT  =  7,     ///< text file - used for source files
+	OSFTBIN   =  8,     ///< binary file of unknown type - resources, etc
+	OSFTCMAP  =  9,     ///< character mapping file
+	OSFTPREF  = 10,     ///< preferences file
+	OSFTUNK   = 11,     ///< unknown - as a filter, matches any file type
+	OSFTT3IMG = 12,     ///< T3 image file (.t3 - formerly .t3x)
+	OSFTT3OBJ = 13,     ///< T3 object file (.t3o)
+	OSFTT3SYM = 14,     ///< T3 symbol export file (.t3s)
+	OSFTT3SAV = 15      ///< T3 saved state file (.t3v)
+};
+
+/**
+ * Constants for os_getc() when returning commands.  When used for command line
+ * editing, special keys (arrows, END, etc.)  should cause os_getc() to return 0,
+ * and return the appropriate CMD_ value on the NEXT call.  Hence, os_getc() must
+ * keep the appropriate information around statically for the next call when a
+ * command key is issued.
+ *
+ * The comments indicate which CMD_xxx codes are "translated" codes and which are
+ * "raw"; the difference is that, when a particular keystroke could be interpreted
+ * as two different CMD_xxx codes, one translated and the other raw, os_getc()
+ * should always return the translated version of the key, and os_getc_raw()
+ * should return the raw version.
+ */
+enum KeyCmd {
+	CMD_UP    =  1,		///< move up/up arrow (translated)
+	CMD_DOWN  =  2,		///< move down/down arrow (translated)
+	CMD_RIGHT =  3,		///< move right/right arrow (translated)
+	CMD_LEFT  =  4,		///< move left/left arrow (translated)
+	CMD_END   =  5,		///< move cursor to end of line (translated)
+	CMD_HOME  =  6,		///< move cursor to start of line (translated)
+	CMD_DEOL  =  7,		///< delete to end of line (translated)
+	CMD_KILL  =  8,		///< delete entire line (translated)
+	CMD_DEL   =  9,		///< delete current character (translated)
+	CMD_SCR   = 10,		///< toggle scrollback mode (translated)
+	CMD_PGUP  = 11,		///< page up (translated)
+	CMD_PGDN  = 12,		///< page down (translated)
+	CMD_TOP   = 13,		///< top of file (translated)
+	CMD_BOT   = 14,		///< bottom of file (translated)
+	CMD_F1    = 15,		///< function key F1 (raw)
+	CMD_F2    = 16,		///< function key F2 (raw)
+	CMD_F3    = 17,		///< function key F3 (raw)
+	CMD_F4    = 18,		///< function key F4 (raw)
+	CMD_F5    = 19,		///< function key F5 (raw)
+	CMD_F6    = 20,		///< function key F6 (raw)
+	CMD_F7    = 21,		///< function key F7 (raw)
+	CMD_F8    = 22,		///< function key F8 (raw)
+	CMD_F9    = 23,		///< function key F9 (raw)
+	CMD_F10   = 24,		///< function key F10 (raw)
+	CMD_CHOME = 25,		///< control-home (raw)
+	CMD_TAB   = 26,		///< tab (translated)
+	CMD_SF2   = 27,		///< shift-F2 (raw)
+	///< not used (obsolete) - 28
+	CMD_WORD_LEFT  = 29,	///< word left (ctrl-left on dos) (translated)
+	CMD_WORD_RIGHT = 30,	///< word right (ctrl-right on dos) (translated)
+	CMD_WORDKILL   = 31,	///< delete word right (translated)
+	CMD_EOF        = 32,	///< end-of-file (raw)
+	CMD_BREAK      = 33,	///< break (Ctrl-C or local equivalent) (translated)
+	CMD_INS        = 34,	///< insert key (raw)
+
+	/**
+	 * ALT-keys - add alphabetical code to CMD_ALT: ALT-A == CMD_ALT + 0,
+	 * ALT-B == CMD_ALT + 1, ALT-C == CMD_ALT + 2, etc
+	 *
+	 * These keys are all raw (untranslated).
+	 */
+	CMD_ALT   = 128		///< start of ALT keys
+};
+
+/**
+ * Status mode codes
+ */
+enum StatusMode {
+	OSS_STATUS_MODE_STORY = 0,
+	OSS_STATUS_MODE_STATUS = 1
+};
+
+typedef Common::SeekableReadStream osfildef;
+
+/**
+ * Operating system compatibility layer
+ */
+class OS : public TADS {
+protected:
+	char status_left[OSS_STATUS_STRING_LEN];
+	char status_right[OSS_STATUS_STRING_LEN];
+	int status_mode;
+protected:
+	/**
+	 * Constructor
+	 */
+	OS(OSystem *syst, const GlkGameDescription &gameDesc);
+
+	/**
+	 * Terminates the game
+	 */
+	void os_terminate(int rc);
+
+	/**
+	 * \defgroup Type Conversions
+	 * @{
+	 */
+
+	/**
+	 * Change a TADS prompt type (OS_AFP_*) into a Glk prompt type.
+	 */
+	glui32 oss_convert_prompt_type(int type);
+	
+	/**
+	 * Change a TADS file type (OSFT*) into a Glk file type.
+	 */
+	glui32 oss_convert_file_type(int type);
+
+	/**
+	 * Change a fileref ID (frefid_t) to a special string and put it in the
+	 * buffer which is passed to it. The string is given by
+	 *   OSS_FILEREF_STRING_PREFIX + 'nnnnn' + OSS_FILEREF_STRING_SUFFIX
+	 * where 'nnnnn' is the frefid_t pointer converted into a string of decimal
+	 * numbers. This is only really practical for 32-bit pointers; if we use
+	 * 64-bit pointers I'll have to start using a hash table or use hex
+	 * numbers.
+	 */
+	glui32 oss_convert_fileref_to_string(frefid_t file_to_convert, char *buffer, int buf_len);
+
+	/**
+	 * Turn a filename or a special fileref string into an actual fileref.
+	 * Notice that, since Glk doesn't know paths, we take this opportunity to
+	 * call oss_check_path, which should do the OS-dependent path changing
+	 * in the event that the filename contains path information
+	 */
+	frefid_t oss_convert_string_to_fileref(char *buffer, glui32 usage);
+
+	/**
+	 * Tell us if the passed string is a hashed fileref or not
+	 */
+	bool oss_is_string_a_fileref(char *buffer);
+
+	/**
+	 * Change a Glk key into a TADS one, using the CMD_xxx codes
+	 */
+	unsigned char oss_convert_keystroke_to_tads(glui32 key);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Directory/File methods
+	 * @{
+	 */
+
+	/**
+	 * If a filename contains path information, change dirs to that path.
+	 * Returns true if the path was fiddled with
+	 */
+	bool oss_check_path(char *filename);
+
+	/**
+	 * In case we changed directories in oss_check_path, change back to the
+	 * original executable directory
+	 */
+	void oss_revert_path();
+
+	/**
+	 * Open a stream, given a string, usage, and a filemode. tadsusage is the
+	 * TADS filemode (OSFT*); tbusage is either fileusage_TextMode or
+	 * fileusage_BinaryMode (from Glk).
+	 */
+	osfildef *oss_open_stream(char *buffer, glui32 tadsusage, glui32 tbusage,
+		glui32 fmode, glui32 rock);
+
+	/**
+	 * Get a pointer to the root name portion of a filename.  This is the part
+	 * of the filename after any path or directory prefix.  For example, on
+	 * Unix, given the string "/home/mjr/deep.gam", this function should return
+	 * a pointer to the 'd' in "deep.gam".  If the filename doesn't appear to
+	 * have a path prefix, it should simply return the argument unchanged.
+	 */
+	const char *os_get_root_name(const char *buf) const { return buf; }
+
+	/**@}*/
+
+	/**
+	 * \defgroup The main text area print routines
+	 * @{
+	 */
+
+	/**
+	 * Process hilighting codes while printing a string
+	 */
+	void oss_put_string_with_hilite(winid_t win, const char *str, size_t len);
+
+	/**
+	 * Status line handling
+	 */
+	void oss_draw_status_line();
+
+	void oss_change_status_string(char *dest, const char *src, size_t len);
+	void oss_change_status_left(const char *str, size_t len);
+	void oss_change_status_right(const char *str);
+	int os_get_status() const { return status_mode; }
+
+	/**
+	 * Flush the output
+	 */
+	void os_flush();
+
+	/**
+	 * Print a null terminated string
+	 */
+	void os_printz(const char *str) { os_print(str, strlen(str)); }
+
+	/**
+	 * Print a string
+	 */
+	void os_print(const char *str, size_t len);
+
+	/**
+	 * Compare two strings
+	 */
+	int memicmp(char *s1, char *s2, int len);
+
+	/**@}*/
+
+	/**
+	 * \defgroup Input routines
+	 * @{
+	 */
+
+	/**
+	 * Wait for a key to be hit
+	 */
+	void os_waitc(void) { os_getc(); }
+
+	/**
+	 * Get a character from the keyboard. For extended characters, return 0,
+	 * then return the extended key at the next call to this function
+	 */
+	int os_getc() {
+		return oss_getc_from_window(story_win);
+	}
+
+	/**
+	 * Accept a keystroke in the passed window
+	 */
+	int oss_getc_from_window(winid_t win);
+
+
+	/**
+	 * Print a message (with os_print) and wait for a key
+	 */
+	void os_expause();
+
+	/**@}*/
+};
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/tads/tads2/tads2.cpp b/engines/glk/tads/tads2/tads2.cpp
index 81f7272..9c50c20d 100644
--- a/engines/glk/tads/tads2/tads2.cpp
+++ b/engines/glk/tads/tads2/tads2.cpp
@@ -26,11 +26,46 @@ namespace Glk {
 namespace TADS {
 namespace TADS2 {
 
-TADS2::TADS2(OSystem *syst, const GlkGameDescription &gameDesc) : TADS(syst, gameDesc) {
+TADS2::TADS2(OSystem *syst, const GlkGameDescription &gameDesc) : OS(syst, gameDesc) {
 }
 
 void TADS2::runGame(Common::SeekableReadStream *gameFile) {
-	// TODO
+	errcxdef errctx;
+	errctx.errcxlgc = &errctx;
+	errctx.errcxfp = nullptr;
+	errctx.errcxofs = 0;
+	errctx.errcxappctx = this;
+
+	/* copyright-date-string */
+#ifdef T2_COPYRIGHT_NOTICE
+	trdptf("%s - A %s TADS %s Interpreter.\n",
+		G_tads_oem_app_name, G_tads_oem_display_mode,
+		TADS_RUNTIME_VERSION);
+	trdptf("%sopyright (c) 1993, 2012 by Michael J. Roberts.\n",
+		G_tads_oem_copyright_prefix ? "TADS c" : "C");
+	trdptf("%s\n", G_tads_oem_author);
+#endif
+
+	trdmain1(&errctx);
+
+	// pause before exiting if the OS desires it
+	os_expause();
+}
+
+void TADS2::trdmain1(errcxdef *errctx) {
+
+}
+
+void TADS2::trdptf(const char *fmt, ...) {
+	va_list va;
+
+	// format the string */
+	va_start(va, fmt);
+	Common::String msg = Common::String::vformat(fmt, va);
+	va_end(va);
+
+	// print the formatted buffer
+	os_printz(msg);
 }
 
 } // End of namespace TADS2
diff --git a/engines/glk/tads/tads2/tads2.h b/engines/glk/tads/tads2/tads2.h
index e07b5c8..2c799a3 100644
--- a/engines/glk/tads/tads2/tads2.h
+++ b/engines/glk/tads/tads2/tads2.h
@@ -23,7 +23,8 @@
 #ifndef GLK_TADS_TADS2
 #define GLK_TADS_TADS2
 
-#include "glk/tads/tads.h"
+#include "glk/tads/tads2/os.h"
+#include "glk/tads/tads2/ler.h"
 
 namespace Glk {
 namespace TADS {
@@ -32,7 +33,10 @@ namespace TADS2 {
 /**
  * TADS 2 game interpreter
  */
-class TADS2 : public TADS {
+class TADS2 : public OS {
+private:
+	// STUBS
+	void os_printz(const Common::String &s) {}
 public:
 	/**
 	 * Constructor
@@ -48,8 +52,17 @@ public:
 	 * Returns the running interpreter type
 	 */
 	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_TADS2; }
+
+	void trdmain1(errcxdef *errctx);
+
+	/**
+	 * printf-style formatting
+	 */
+	void trdptf(const char *fmt, ...);
 };
 
+typedef TADS2 appctxdef;
+
 } // End of namespace TADS2
 } // End of namespace TADS
 } // End of namespace Glk


Commit: d3c46f7078c05fc38bde26d38f6d767167c54c6f
    https://github.com/scummvm/scummvm/commit/d3c46f7078c05fc38bde26d38f6d767167c54c6f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Add Regex class

Changed paths:
    engines/glk/module.mk
    engines/glk/tads/tads2/os.cpp
    engines/glk/tads/tads2/os.h


diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 89be6a2..a812850 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -47,6 +47,7 @@ MODULE_OBJS := \
 	tads/tads.o \
 	tads/tads2/ler.o \
 	tads/tads2/os.o \
+	tads/tads2/regex.o \
 	tads/tads2/tads2.o \
 	tads/tads3/tads3.o
 
diff --git a/engines/glk/tads/tads2/os.cpp b/engines/glk/tads/tads2/os.cpp
index df7cb42..248611d 100644
--- a/engines/glk/tads/tads2/os.cpp
+++ b/engines/glk/tads/tads2/os.cpp
@@ -215,7 +215,7 @@ void OS::oss_change_status_right(const char *str) {
 	oss_draw_status_line();
 }
 
-int OS::memicmp(char *s1, char *s2, int len) {
+int OS::memicmp(const char *s1, const char *s2, int len) {
 	char *x1, *x2;
 	int i, result;
 
diff --git a/engines/glk/tads/tads2/os.h b/engines/glk/tads/tads2/os.h
index ba0058d..3c50cb5 100644
--- a/engines/glk/tads/tads2/os.h
+++ b/engines/glk/tads/tads2/os.h
@@ -30,6 +30,17 @@ namespace TADS {
 namespace TADS2 {
 
 /**
+ * Allocate a memory block
+ */
+#define mchalo(CTX, SIZE, COMMENT) ((byte *)new byte[SIZE])
+
+/**
+ * Free a memory block
+ */
+#define mchfre(PTR) delete[] (byte *)PTR
+
+
+/**
  * The character (or characters) which mark the beginning of a special fileref string.
  * The important thing is that the string be one that is either not allowed in
  * filenames on your platform or is unlikely to be the first part of a filename.
@@ -288,10 +299,17 @@ protected:
 	 */
 	void os_print(const char *str, size_t len);
 
+	/**@}*/
+
+	/**
+	 * \defgroup Memory routines
+	 * @{
+	 */
+
 	/**
 	 * Compare two strings
 	 */
-	int memicmp(char *s1, char *s2, int len);
+	int memicmp(const char *s1, const char *s2, int len);
 
 	/**@}*/
 


Commit: f20d07381cfdbb625550167e259a2ea58f81fcd9
    https://github.com/scummvm/scummvm/commit/f20d07381cfdbb625550167e259a2ea58f81fcd9
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Added cmap file

Changed paths:
  A engines/glk/tads/tads2/regex.cpp
  A engines/glk/tads/tads2/regex.h
  A engines/glk/tads/tads2/tads2_cmap.cpp
  A engines/glk/tads/tads2/types.h
    engines/glk/module.mk
    engines/glk/tads/tads2/ler.h
    engines/glk/tads/tads2/os.cpp
    engines/glk/tads/tads2/os.h
    engines/glk/tads/tads2/tads2.cpp
    engines/glk/tads/tads2/tads2.h


diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index a812850..5c17362 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -49,6 +49,7 @@ MODULE_OBJS := \
 	tads/tads2/os.o \
 	tads/tads2/regex.o \
 	tads/tads2/tads2.o \
+	tads/tads2/tads2_cmap.o \
 	tads/tads3/tads3.o
 
 # This module can be built as a plugin
diff --git a/engines/glk/tads/tads2/ler.h b/engines/glk/tads/tads2/ler.h
index 000aa09..8edb945 100644
--- a/engines/glk/tads/tads2/ler.h
+++ b/engines/glk/tads/tads2/ler.h
@@ -34,6 +34,314 @@ namespace TADS2 {
 // maximum length of a facility identifier
 #define ERRFACMAX    6
 
+enum ErrorCode {
+	/* memory/cache manager errors */
+	ERR_NOMEM    =  1,                                     /* out of memory */
+	ERR_FSEEK    =  2,                             /* error seeking in file */
+	ERR_FREAD    =  3,                           /* error reading from file */
+	ERR_NOPAGE   =  4,                                /* no more page slots */
+	ERR_REALCK   =  5,          /* attempting to reallocate a locked object */
+	ERR_SWAPBIG  =  6,    /* swapfile limit reached - out of virtual memory */
+	ERR_FWRITE   =  7,                                /* error writing file */
+	ERR_SWAPPG   =  8,                    /* exceeded swap page table limit */
+	ERR_CLIUSE   =  9,     /* requested client object number already in use */
+	ERR_CLIFULL  = 10,                      /* client mapping table is full */
+	ERR_NOMEM1   = 11,   /* swapping/garbage collection failed to find enuf */
+	ERR_NOMEM2   = 12,            /* no memory to resize (expand) an object */
+	ERR_OPSWAP   = 13,                          /* unable to open swap file */
+	ERR_NOHDR    = 14,                     /* can't get a new object header */
+	ERR_NOLOAD   = 15,   /* mcm cannot find object to load (internal error) */
+	ERR_LCKFRE   = 16,     /* attempting to free a locked object (internal) */
+	ERR_INVOBJ   = 17,                                    /* invalid object */
+	ERR_BIGOBJ   = 18,  /* object too big - exceeds memory allocation limit */
+
+	/* lexical analysis errors */
+	ERR_INVTOK   = 100,                                    /* invalid token */
+	ERR_STREOF   = 101,                /* end of file while scanning string */
+	ERR_TRUNC    = 102,                      /* symbol too long - truncated */
+	ERR_NOLCLSY  = 103,                   /* no space in local symbol table */
+	ERR_PRPDIR   = 104,               /* invalid preprocessor (#) directive */
+	ERR_INCNOFN  = 105,                /* no filename in #include directive */
+	ERR_INCSYN   = 106,                          /* invalid #include syntax */
+	ERR_INCSEAR  = 107,                         /* can't find included file */
+	ERR_INCMTCH  = 108,       /* no matching delimiter in #include filename */
+	ERR_MANYSYM  = 109,                    /* out of space for symbol table */
+	ERR_LONGLIN  = 110,                                    /* line too long */
+	ERR_INCRPT   = 111,           /* header file already included - ignored */
+	ERR_PRAGMA   = 112,                         /* unknown pragma (ignored) */
+	ERR_BADPELSE = 113,                                 /* unexpected #else */
+	ERR_BADENDIF = 114,                                /* unexpected #endif */
+	ERR_BADELIF  = 115,                                 /* unexpected #elif */
+	ERR_MANYPIF  = 116,                             /* #if nesting too deep */
+	ERR_DEFREDEF = 117,                           /* symbol already defined */
+	ERR_PUNDEF   = 118,                        /* #undef symbol not defined */
+	ERR_NOENDIF  = 119,                                   /* missing #endif */
+	ERR_MACNEST  = 120,                         /* macros nested too deeply */
+	ERR_BADISDEF = 121,          /* invalid argument for defined() operator */
+	ERR_PIF_NA   = 122,                           /* #if is not implemented */
+	ERR_PELIF_NA = 123,                         /* #elif is not implemented */
+	ERR_P_ERROR  = 124,                              /* error directive: %s */
+	ERR_LONG_FILE_MACRO = 125,               /* __FILE__ expansion too long */
+	ERR_LONG_LINE_MACRO = 126,               /* __LINE__ expansion too long */
+
+	/* undo errors */
+	ERR_UNDOVF   = 200,                /* operation is too big for undo log */
+	ERR_NOUNDO   = 201,                         /* no more undo information */
+	ERR_ICUNDO   = 202,          /* incomplete undo (no previous savepoint) */
+
+	/* parser errors */
+	ERR_REQTOK   = 300,    /* expected token (arg 1) - found something else */
+	ERR_REQSYM   = 301,                                  /* expected symbol */
+	ERR_REQPRP   = 302,                         /* expected a property name */
+	ERR_REQOPN   = 303,                                 /* expected operand */
+	ERR_REQARG   = 304,       /* expected comma or closing paren (arg list) */
+	ERR_NONODE   = 305,                      /* no space for new parse node */
+	ERR_REQOBJ   = 306,                             /* epxected object name */
+	ERR_REQEXT   = 307,           /* redefining symbol as external function */
+	ERR_REQFCN   = 308,                    /* redefining symbol as function */
+	ERR_NOCLASS  = 309,  /* can't use CLASS with function/external function */
+	ERR_REQUNO   = 310,                          /* required unary operator */
+	ERR_REQBIN   = 311,                         /* required binary operator */
+	ERR_INVBIN   = 312,                          /* invalid binary operator */
+	ERR_INVASI   = 313,                               /* invalid assignment */
+	ERR_REQVAR   = 314,                           /* required variable name */
+	ERR_LCLSYN   = 315,        /* required comma or semicolon in local list */
+	ERR_REQRBR   = 316,   /* required right brace (eof before end of group) */
+	ERR_BADBRK   = 317,                          /* 'break' without 'while' */
+	ERR_BADCNT   = 318,                       /* 'continue' without 'while' */
+	ERR_BADELS   = 319,                              /* 'else' without 'if' */
+	ERR_WEQASI   = 320, /* warning: possible use of '=' where ':=' intended */
+	ERR_EOF      = 321,                           /* unexpected end of file */
+	ERR_SYNTAX   = 322,                             /* general syntax error */
+	ERR_INVOP    = 323,                             /* invalid operand type */
+	ERR_NOMEMLC  = 324,             /* no memory for new local symbol table */
+	ERR_NOMEMAR  = 325,              /* no memory for argument symbol table */
+	ERR_FREDEF   = 326,   /* redefining a function which is already defined */
+	ERR_NOSW     = 327,      /* 'case' or 'default' and not in switch block */
+	ERR_SWRQCN   = 328,           /* constant required in switch case value */
+	ERR_REQLBL   = 329,                        /* label required for 'goto' */
+	ERR_NOGOTO   = 330,                       /* 'goto' label never defined */
+	ERR_MANYSC   = 331,                 /* too many superclasses for object */
+	ERR_OREDEF   = 332,                      /* redefining symbol as object */
+	ERR_PREDEF   = 333,               /* property being redefined in object */
+	ERR_BADPVL   = 334,                           /* invalid property value */
+	ERR_BADVOC   = 335,                    /* bad vocabulary property value */
+	ERR_BADTPL   = 336,       /* bad template property value (need sstring) */
+	ERR_LONGTPL  = 337,             /* template base property name too long */
+	ERR_MANYTPL  = 338,     /* too many templates (internal compiler limit) */
+	ERR_BADCMPD  = 339,   /* bad value for compound word (sstring required) */
+	ERR_BADFMT   = 340,     /* bad value for format string (sstring needed) */
+	ERR_BADSYN   = 341,     /* invalid value for synonym (sstring required) */
+	ERR_UNDFSYM  = 342,                                 /* undefined symbol */
+	ERR_BADSPEC  = 343,                                 /* bad special word */
+	ERR_NOSELF   = 344,                 /* "self" not valid in this context */
+	ERR_STREND   = 345,            /* warning: possible unterminated string */
+	ERR_MODRPLX  = 346,    /* modify/replace not allowed with external func */
+	ERR_MODFCN   = 347,                 /* modify not allowed with function */
+	ERR_MODFWD   = 348,     /* modify/replace not allowed with forward func */
+	ERR_MODOBJ   = 349,    /* modify can only be used with a defined object */
+	ERR_RPLSPEC  = 350,                 /* warning - replacing specialWords */
+	ERR_SPECNIL  = 351,        /* nil only allowed with modify specialWords */
+	ERR_BADLCL   = 353,   /* 'local' statement must precede executable code */
+	ERR_IMPPROP  = 354,          /* implied verifier '%s' is not a property */
+	ERR_BADTPLF  = 355,                    /* invalid command template flag */
+	ERR_NOTPLFLG  = 356,      /* flags are not allowed with old file format */
+	ERR_AMBIGBIN  = 357,          /* warning: operator '%s' could be binary */
+	ERR_PIA       = 358,          /* warning: possibly incorrect assignment */
+	ERR_BADSPECEXPR = 359,                /* invalid speculation evaluation */
+
+	/* code generation errors */
+	ERR_OBJOVF   = 400,     /* object cannot grow any bigger - code too big */
+	ERR_NOLBL    = 401,                  /* no more temporary labels/fixups */
+	ERR_LBNOSET  = 402,                 /* (internal error) label never set */
+	ERR_INVLSTE  = 403,                /* invalid datatype for list element */
+	ERR_MANYDBG  = 404,  /* too many debugger line records (internal limit) */
+
+	/* vocabulary setup errors */
+	ERR_VOCINUS  = 450,            /* vocabulary being redefined for object */
+	ERR_VOCMNPG  = 451,          /* too many vocwdef pages (internal limit) */
+	ERR_VOCREVB  = 452,                             /* redefining same verb */
+	ERR_VOCREVB2 = 453,             /* redefining same verb - two arguments */
+
+	/* set-up errors */
+	ERR_LOCNOBJ  = 500,           /* location of object %s is not an object */
+	ERR_CNTNLST  = 501,                /* contents of object %s is not list */
+	ERR_SUPOVF   = 502,           /* overflow trying to build contents list */
+	ERR_RQOBJNF  = 503,                     /* required object %s not found */
+	ERR_WRNONF   = 504,                    /* warning - object %s not found */
+	ERR_MANYBIF  = 505,     /* too many built-in functions (internal error) */
+
+	/* fio errors */
+	ERR_OPWGAM    = 600,                 /* unable to open game for writing */
+	ERR_WRTGAM    = 601,                      /* error writing to game file */
+	ERR_FIOMSC    = 602,             /* too many sc's for writing in fiowrt */
+	ERR_UNDEFF    = 603,                              /* undefined function */
+	ERR_UNDEFO    = 604,                                /* undefined object */
+	ERR_UNDEF     = 605,                         /* undefined symbols found */
+	ERR_OPRGAM    = 606,                 /* unable to open game for reading */
+	ERR_RDGAM     = 607,                         /* error reading game file */
+	ERR_BADHDR    = 608,    /* file has invalid header - not TADS game file */
+	ERR_UNKRSC    = 609,              /* unknown resource type in .gam file */
+	ERR_UNKOTYP   = 610,             /* unknown object type in OBJ resource */
+	ERR_BADVSN    = 611,    /* file saved by different incompatible version */
+	ERR_LDGAM     = 612,                  /* error loading object on demand */
+	ERR_LDBIG     = 613, /* object too big for load region (prob. internal) */
+	ERR_UNXEXT    = 614,                /* did not expect external function */
+	ERR_WRTVSN    = 615,     /* compiler cannot write the requested version */
+	ERR_VNOCTAB   = 616,        /* format version cannot be used with -ctab */
+	ERR_BADHDRRSC = 617,         /* invalid resource file header in file %s */
+	ERR_RDRSC     = 618,               /* error reading resource file "xxx" */
+
+	/* character mapping errors */
+	ERR_CHRNOFILE = 700,           /* unable to load character mapping file */
+
+	/* user interrupt */
+	ERR_USRINT    = 990,      /* user requested cancel of current operation */
+
+	/* run-time errors */
+	ERR_STKOVF    = 1001,                                 /* stack overflow */
+	ERR_HPOVF     = 1002,                                  /* heap overflow */
+	ERR_REQNUM    = 1003,                         /* numeric value required */
+	ERR_STKUND    = 1004,                                /* stack underflow */
+	ERR_REQLOG    = 1005,                         /* logical value required */
+	ERR_INVCMP    = 1006,     /* invalid datatypes for magnitude comparison */
+	ERR_REQSTR    = 1007,                          /* string value required */
+	ERR_INVADD    = 1008,             /* invalid datatypes for '+' operator */
+	ERR_INVSUB    = 1009,      /* invalid datatypes for binary '-' operator */
+	ERR_REQVOB    = 1010,                           /* require object value */
+	ERR_REQVFN    = 1011,                      /* required function pointer */
+	ERR_REQVPR    = 1012,                 /* required property number value */
+
+	/* non-error conditions:  run-time EXIT, ABORT, ASKIO, ASKDO */
+	ERR_RUNEXIT    = 1013,                     /* 'exit' statement executed */
+	ERR_RUNABRT    = 1014,                    /* 'abort' statement executed */
+	ERR_RUNASKD    = 1015,                    /* 'askdo' statement executed */
+	ERR_RUNASKI    = 1016,           /* 'askio' executed; int arg 1 is prep */
+	ERR_RUNQUIT    = 1017,                               /* 'quit' executed */
+	ERR_RUNRESTART = 1018,                              /* 'reset' executed */
+	ERR_RUNEXITOBJ = 1019,                            /* 'exitobj' executed */
+
+	ERR_REQVLS     = 1020,                             /* list value required */
+	ERR_LOWINX     = 1021,              /* index value too low (must be >= 1) */
+	ERR_HIGHINX    = 1022,  /* index value too high (must be <= length(list)) */
+	ERR_INVTBIF    = 1023,              /* invalid type for built-in function */
+	ERR_INVVBIF    = 1024,             /* invalid value for built-in function */
+	ERR_BIFARGC    = 1025,           /* wrong number of arguments to built-in */
+	ERR_ARGC       = 1026,      /* wrong number of arguments to user function */
+	ERR_FUSEVAL    = 1027,     /* string/list not allowed for fuse/daemon arg */
+	ERR_BADSETF    = 1028,      /* internal error in setfuse/setdaemon/notify */
+	ERR_MANYFUS    = 1029,                                  /* too many fuses */
+	ERR_MANYDMN    = 1030,                                /* too many daemons */
+	ERR_MANYNFY    = 1031,                              /* too many notifiers */
+	ERR_NOFUSE     = 1032,                       /* fuse not found in remfuse */
+	ERR_NODMN      = 1033,                   /* daemon not found in remdaemon */
+	ERR_NONFY      = 1034,                  /* notifier not found in unnotify */
+	ERR_BADREMF    = 1035,    /* internal error in remfuse/remdaemon/unnotify */
+	ERR_DMDLOOP    = 1036,     /* load-on-demand loop: property not being set */
+	ERR_UNDFOBJ    = 1037,             /* undefined object in vocabulary tree */
+	ERR_BIFCSTR    = 1038,            /* c-string conversion overflows buffer */
+	ERR_INVOPC     = 1039,                                  /* invalid opcode */
+	ERR_RUNNOBJ    = 1040,     /* runtime error: property taken of non-object */
+	ERR_EXTLOAD    = 1041,           /* unable to load external function "%s" */
+	ERR_EXTRUN     = 1042,          /* error executing external function "%s" */
+	ERR_CIRCSYN    = 1043,                                /* circular synonym */
+	ERR_DIVZERO    = 1044,                                  /* divide by zero */
+	ERR_BADDEL     = 1045,      /* can only delete objects created with "new" */
+	ERR_BADNEWSC   = 1046,     /* superclass for "new" cannot be a new object */
+	ERR_VOCSTK     = 1047,              /* insufficient space in parser stack */
+	ERR_BADFILE    = 1048,                             /* invalid file handle */
+
+	ERR_RUNEXITPRECMD = 1049,                       /* exited from preCommand */
+
+	/* run-time parser errors */
+	ERR_PRS_SENT_UNK    = 1200,          /* sentence structure not recognized */
+	ERR_PRS_VERDO_FAIL  = 1201,                           /* verDoVerb failed */
+	ERR_PRS_VERIO_FAIL  = 1202,                           /* verIoVerb failed */
+	ERR_PRS_NO_VERDO    = 1203,             /* no verDoVerb for direct object */
+	ERR_PRS_NO_VERIO    = 1204,             /* no verIoVerb for direct object */
+	ERR_PRS_VAL_DO_FAIL = 1205,            /* direct object validation failed */
+	ERR_PRS_VAL_IO_FAIL = 1206,          /* indirect object validation failed */
+
+	/* compiler/runtime/debugger driver errors */
+	ERR_USAGE     = 1500,                                /* invalid usage */
+	ERR_OPNINP    = 1501,                     /* error opening input file */
+	ERR_NODBG     = 1502,              /* game not compiled for debugging */
+	ERR_ERRFIL    = 1503,            /* unable to open error capture file */
+	ERR_PRSCXSIZ  = 1504,            /* parse pool + local size too large */
+	ERR_STKSIZE   = 1505,                         /* stack size too large */
+	ERR_OPNSTRFIL = 1506,            /* error opening string capture file */
+	ERR_INVCMAP   = 1507,                   /* invalid character map file */
+
+	/* debugger errors */
+	ERR_BPSYM         = 2000,                 /* symbol not found for breakpoint */
+	ERR_BPPROP        = 2002,             /* breakpoint symbol is not a property */
+	ERR_BPFUNC        = 2003,             /* breakpoint symbol is not a function */
+	ERR_BPNOPRP       = 2004,              /* property is not defined for object */
+	ERR_BPPRPNC       = 2005,                            /* property is not code */
+	ERR_BPSET         = 2006,         /* breakpoint already set at this location */
+	ERR_BPNOTLN       = 2007,     /* breakpoint is not at a line (OPCLINE instr) */
+	ERR_MANYBP        = 2008,                            /* too many breakpoints */
+	ERR_BPNSET        = 2009,            /* breakpoint to be deleted was not set */
+	ERR_DBGMNSY       = 2010,  /* too many symbols in debug expression (int lim) */
+	ERR_NOSOURC       = 2011,                   /* unable to find source file %s */
+	ERR_WTCHLCL       = 2012,        /* illegal to assign to local in watch expr */
+	ERR_INACTFR       = 2013, /* inactive frame (expression value not available) */
+	ERR_MANYWX        = 2014,                      /* too many watch expressions */
+	ERR_WXNSET        = 2015,                              /* watchpoint not set */
+	ERR_EXTRTXT       = 2016,               /* extraneous text at end of command */
+	ERR_BPOBJ         = 2017,              /* breakpoint symbol is not an object */
+	ERR_DBGINACT      = 2018,                          /* debugger is not active */
+	ERR_BPINUSE       = 2019,                      /* breakpoint is already used */
+	ERR_RTBADSPECEXPR = 2020,                  /* invalid speculative expression */
+	ERR_NEEDLIN2      = 2021,     /* -ds2 information not found - must recompile */
+
+	/* usage error messages */
+	ERR_TCUS1         = 3000,                          /* first tc usage message */
+	ERR_TCUSL         = 3024,                           /* last tc usage message */
+	ERR_TCTGUS1       = 3030,                         /* first tc toggle message */
+	ERR_TCTGUSL       = 3032,
+	ERR_TCZUS1        = 3040,            /* first tc -Z suboptions usage message */
+	ERR_TCZUSL        = 3041,
+	ERR_TC1US1        = 3050,            /* first tc -1 suboptions usage message */
+	ERR_TC1USL        = 3058,
+	ERR_TCMUS1        = 3070,            /* first tc -m suboptions usage message */
+	ERR_TCMUSL        = 3076,
+	ERR_TCVUS1        = 3080,                /* first -v suboption usage message */
+	ERR_TCVUSL        = 3082,
+	ERR_TRUSPARM      = 3099,
+	ERR_TRUS1         = 3100,                          /* first tr usage message */
+	ERR_TRUSL         = 3117,
+	ERR_TRUSFT1       = 3118,                       /* first tr "footer" message */
+	ERR_TRUSFTL       = 3119,                        /* last tr "footer" message */
+	ERR_TRSUS1        = 3150,            /* first tr -s suboptions usage message */
+	ERR_TRSUSL        = 3157,
+	ERR_TDBUSPARM     = 3199,
+	ERR_TDBUS1        = 3200,                         /* first tdb usage message */
+	ERR_TDBUSL        = 3214,                          /* last tdb usage message */
+
+	/* TR 16-bit MSDOS-specific usage messages */
+	ERR_TRUS_DOS_1   = 3300,
+	ERR_TRUS_DOS_L   = 3300,
+
+	/* TR 32-bit MSDOS console mode usage messages */
+	ERR_TRUS_DOS32_1 = 3310,
+	ERR_TRUS_DOS32_L = 3312,
+
+	/* TADS/Graphic errors */
+	ERR_GNOFIL       = 4001,                      /* can't find graphics file %s */
+	ERR_GNORM        = 4002,                               /* can't find room %s */
+	ERR_GNOOBJ       = 4003,                    /* can't find hot spot object %s */
+	ERR_GNOICN       = 4004                         /* can't find icon object %s */
+};
+
+/*
+ *   Special error flag - this is returned from execmd() when preparseCmd
+ *   returns a command list.  This indicates to voc1cmd that it should try
+ *   the command over again, using the words in the new list. 
+ */
+#define ERR_PREPRSCMDREDO  30000             /* preparseCmd returned a list */
+#define ERR_PREPRSCMDCAN   30001    /* preparseCmd returned 'nil' to cancel */
+
 union erradef {
     int   erraint;		// integer argument
     char *errastr;		// text string argument
@@ -274,6 +582,17 @@ void errlogf(errcxdef *ctx, char *facility, int err);
  (errargv(ctx,0,typ1,arg1),errargv(ctx,1,typ2,arg2),\
   errargc(ctx,2),errlogn(ctx,e,fac))
 
+/**
+ * For compatility with old facility-free mechanism, signal with facility "TADS" 
+ */
+#define errsig(ctx, err) errsigf(ctx, "TADS", err)
+#define errsig1(c, e, t, a) errsigf1(c,"TADS",e,t,a)
+//#define errsig2(c, e, t1, a1, t2, a2) errsigf2(c,"TADS",e,t1,a1,t2,a2)
+#define errlog(c, e) errlogf(c, "TADS", e)
+#define errlog1(c, e, t, a) errlogf1(c,"TADS",e,t,a)
+#define errlog2(c, e, t1, a1, t2, a2) errlogf2(c,"TADS",e,t1,a1,t2,a2)
+
+#define errsig2(c, e, t1, a1, t2, a2) error("Error occurred")
  
 // get the text of an error
 void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err);
diff --git a/engines/glk/tads/tads2/os.cpp b/engines/glk/tads/tads2/os.cpp
index 248611d..514a912 100644
--- a/engines/glk/tads/tads2/os.cpp
+++ b/engines/glk/tads/tads2/os.cpp
@@ -178,6 +178,42 @@ osfildef *OS::oss_open_stream(char *buffer, glui32 tadsusage, glui32 tbusage,
 	return *osf;
 }
 
+osfildef *OS::osfoprb(const char *fname, uint typ) {
+	Common::File *f = new Common::File();
+	if (f->open(fname))
+		return f;
+
+	f->close();
+	delete f;
+	return nullptr;
+}
+
+void OS::os_gen_charmap_filename(char *filename, const char *internal_id,
+		const char *argv0) {
+	const char *p;
+	const char *rootname;
+	size_t pathlen;
+
+	// find the path prefix of the original executable filename
+	for (p = rootname = argv0; *p != '\0'; ++p) {
+		if (*p == '/' || *p == '\\' || *p == ':')
+			rootname = p + 1;
+	}
+
+	// copy the path prefix
+	pathlen = rootname - argv0;
+	memcpy(filename, argv0, pathlen);
+
+	// if there's no trailing backslash, add one
+	if (pathlen == 0 || filename[pathlen - 1] != '\\')
+		filename[pathlen++] = '\\';
+
+	// add "win_", plus the character set ID, plus the extension
+	strcpy(filename + pathlen, "win_");
+	strcat(filename + pathlen, internal_id);
+	strcat(filename + pathlen, ".tcp");
+}
+
 void OS::oss_put_string_with_hilite(winid_t win, const char *str, size_t len) {
 	glk_set_window(win);
 	glk_put_buffer(str, len);
diff --git a/engines/glk/tads/tads2/os.h b/engines/glk/tads/tads2/os.h
index 3c50cb5..ecd0468 100644
--- a/engines/glk/tads/tads2/os.h
+++ b/engines/glk/tads/tads2/os.h
@@ -24,146 +24,13 @@
 #define GLK_TADS_TADS2_OS
 
 #include "glk/tads/tads.h"
+#include "glk/tads/tads2/types.h"
 
 namespace Glk {
 namespace TADS {
 namespace TADS2 {
 
 /**
- * Allocate a memory block
- */
-#define mchalo(CTX, SIZE, COMMENT) ((byte *)new byte[SIZE])
-
-/**
- * Free a memory block
- */
-#define mchfre(PTR) delete[] (byte *)PTR
-
-
-/**
- * The character (or characters) which mark the beginning of a special fileref string.
- * The important thing is that the string be one that is either not allowed in
- * filenames on your platform or is unlikely to be the first part of a filename.
- */
-#define OSS_FILEREF_STRING_PREFIX ":"
-
-/**
- * The character (or characters) which mark the end of a special fileref string.
- * Using this and OSS_FILEREF_STRING_PREFIX, you should be able to come up with
- * something which forms an invalid filename
- */
-#define OSS_FILEREF_STRING_SUFFIX ""
-
-/**
- * Maximum length of status line text
- */
-#define OSS_STATUS_STRING_LEN 80
-
-/**
- * Important note: do not change these values when porting TADS.  These
- * values can be used by games, so they must be the same on all platforms.  
- */
-enum {
-	OS_AFP_OPEN = 1,			///< choose an existing file to open for reading
-	OS_AFP_SAVE = 2				///< choose a filename for saving to a file
-};
-
-/**
- * File types.These type codes are used when opening or creating a file,
- * so that the OS routine can set appropriate file system metadata
- * to describe or find the file type.
- *
- * The type os_filetype_t is defined for documentary purposes; it's
- * always just an int.
- */
-enum os_filetype_t {
-	OSFTGAME  =  0,		///< a game data file (.gam)
-	OSFTSAVE  =  1,     ///< a saved game (.sav)
-	OSFTLOG   =  2,     ///< a transcript (log) file
-	OSFTSWAP  =  3,     ///< swap file
-	OSFTDATA  =  4,     ///< user data file (used with the TADS fopen() call)
-	OSFTCMD   =  5,     ///< QA command/log file
-	OSFTERRS  =  6,     ///< error message file
-	OSFTTEXT  =  7,     ///< text file - used for source files
-	OSFTBIN   =  8,     ///< binary file of unknown type - resources, etc
-	OSFTCMAP  =  9,     ///< character mapping file
-	OSFTPREF  = 10,     ///< preferences file
-	OSFTUNK   = 11,     ///< unknown - as a filter, matches any file type
-	OSFTT3IMG = 12,     ///< T3 image file (.t3 - formerly .t3x)
-	OSFTT3OBJ = 13,     ///< T3 object file (.t3o)
-	OSFTT3SYM = 14,     ///< T3 symbol export file (.t3s)
-	OSFTT3SAV = 15      ///< T3 saved state file (.t3v)
-};
-
-/**
- * Constants for os_getc() when returning commands.  When used for command line
- * editing, special keys (arrows, END, etc.)  should cause os_getc() to return 0,
- * and return the appropriate CMD_ value on the NEXT call.  Hence, os_getc() must
- * keep the appropriate information around statically for the next call when a
- * command key is issued.
- *
- * The comments indicate which CMD_xxx codes are "translated" codes and which are
- * "raw"; the difference is that, when a particular keystroke could be interpreted
- * as two different CMD_xxx codes, one translated and the other raw, os_getc()
- * should always return the translated version of the key, and os_getc_raw()
- * should return the raw version.
- */
-enum KeyCmd {
-	CMD_UP    =  1,		///< move up/up arrow (translated)
-	CMD_DOWN  =  2,		///< move down/down arrow (translated)
-	CMD_RIGHT =  3,		///< move right/right arrow (translated)
-	CMD_LEFT  =  4,		///< move left/left arrow (translated)
-	CMD_END   =  5,		///< move cursor to end of line (translated)
-	CMD_HOME  =  6,		///< move cursor to start of line (translated)
-	CMD_DEOL  =  7,		///< delete to end of line (translated)
-	CMD_KILL  =  8,		///< delete entire line (translated)
-	CMD_DEL   =  9,		///< delete current character (translated)
-	CMD_SCR   = 10,		///< toggle scrollback mode (translated)
-	CMD_PGUP  = 11,		///< page up (translated)
-	CMD_PGDN  = 12,		///< page down (translated)
-	CMD_TOP   = 13,		///< top of file (translated)
-	CMD_BOT   = 14,		///< bottom of file (translated)
-	CMD_F1    = 15,		///< function key F1 (raw)
-	CMD_F2    = 16,		///< function key F2 (raw)
-	CMD_F3    = 17,		///< function key F3 (raw)
-	CMD_F4    = 18,		///< function key F4 (raw)
-	CMD_F5    = 19,		///< function key F5 (raw)
-	CMD_F6    = 20,		///< function key F6 (raw)
-	CMD_F7    = 21,		///< function key F7 (raw)
-	CMD_F8    = 22,		///< function key F8 (raw)
-	CMD_F9    = 23,		///< function key F9 (raw)
-	CMD_F10   = 24,		///< function key F10 (raw)
-	CMD_CHOME = 25,		///< control-home (raw)
-	CMD_TAB   = 26,		///< tab (translated)
-	CMD_SF2   = 27,		///< shift-F2 (raw)
-	///< not used (obsolete) - 28
-	CMD_WORD_LEFT  = 29,	///< word left (ctrl-left on dos) (translated)
-	CMD_WORD_RIGHT = 30,	///< word right (ctrl-right on dos) (translated)
-	CMD_WORDKILL   = 31,	///< delete word right (translated)
-	CMD_EOF        = 32,	///< end-of-file (raw)
-	CMD_BREAK      = 33,	///< break (Ctrl-C or local equivalent) (translated)
-	CMD_INS        = 34,	///< insert key (raw)
-
-	/**
-	 * ALT-keys - add alphabetical code to CMD_ALT: ALT-A == CMD_ALT + 0,
-	 * ALT-B == CMD_ALT + 1, ALT-C == CMD_ALT + 2, etc
-	 *
-	 * These keys are all raw (untranslated).
-	 */
-	CMD_ALT   = 128		///< start of ALT keys
-};
-
-/**
- * Status mode codes
- */
-enum StatusMode {
-	OSS_STATUS_MODE_STORY = 0,
-	OSS_STATUS_MODE_STATUS = 1
-};
-
-typedef Common::SeekableReadStream osfildef;
-
-/**
  * Operating system compatibility layer
  */
 class OS : public TADS {
@@ -262,6 +129,27 @@ protected:
 	 */
 	const char *os_get_root_name(const char *buf) const { return buf; }
 
+	/**
+	 * Open a file for access
+	 */
+	osfildef *osfoprb(const char *fname, uint typ = 0);
+
+	/**
+	 * Receive notification that a character mapping file has been loaded.  We
+	 * don't need to do anything with this information, since we we're relying
+	 * on the Glk layer and ScummVM backend to handle all that
+	 */
+	void os_advise_load_charmap(const char *id, const char *ldesc, const char *sysinfo) {
+		// No implementation needed
+	}
+
+	/**
+	 * Generate a filename for a character mapping table.  On Windows, the
+	 * filename is always simply "win" plus the internal ID plus ".tcp".
+	 */
+	void os_gen_charmap_filename(char *filename, const char *internal_id,
+		const char *argv0);
+
 	/**@}*/
 
 	/**
diff --git a/engines/glk/tads/tads2/regex.cpp b/engines/glk/tads/tads2/regex.cpp
new file mode 100644
index 0000000..f6a009a
--- /dev/null
+++ b/engines/glk/tads/tads2/regex.cpp
@@ -0,0 +1,1276 @@
+/* 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.
+ *
+ */
+
+/*
+Regular Expression Parser and Recognizer for TADS
+Function
+  Parses and recognizes regular expressions
+Notes
+  Regular expression syntax:
+
+	 abc|def    either abc or def
+	 (abc)      abc
+	 abc+       abc, abcc, abccc, ...
+	 abc*     ab, abc, abcc, ...
+	 abc?       ab or abc
+	 .          any single character
+	 abc$       abc at the end of the line
+	 ^abc       abc at the beginning of the line
+	 %^abc      literally ^abc
+	 [abcx-z]   matches a, b, c, x, y, or z
+	 [^abcx-z]  matches any character except a, b, c, x, y, or z
+	 [^]-q]     matches any character except ], -, or q
+
+  Note that using ']' or '-' in a character range expression requires
+  special ordering.  If ']' is to be used, it must be the first character
+  after the '^', if present, within the brackets.  If '-' is to be used,
+  it must be the first character after the '^' and/or ']', if present.
+
+  '%' is used to escape the special characters: | . ( ) * ? + ^ $ % [
+  (We use '%' rather than a backslash because it's less trouble to
+  enter in a TADS string -- a backslash needs to be quoted with another
+  backslash, which is error-prone and hard to read.  '%' doesn't need
+  any special quoting in a TADS string, which makes it a lot more
+  readable.)
+
+  In addition, '%' is used to introduce additional special sequences:
+
+	 %1         text matching first parenthesized expression
+	 %9         text matching ninth parenthesized experssion
+	 %<         matches at the beginning of a word only
+	 %>         matches at the end of a word only
+	 %w         matches any word character
+	 %W         matches any non-word character
+	 %b         matches at any word boundary (beginning or end of word)
+	 %B         matches except at a word boundary
+
+  For the word matching sequences, a word is any sequence of letters and
+  numbers.
+*/
+
+#include "engines/glk/tads/tads2/regex.h"
+#include "engines/glk/tads/tads2/ler.h"
+#include "engines/glk/tads/tads2/os.h"
+//#include "engines/glk/tads/tads2/std.h"
+#//include "engines/glk/tads/tads2/ler.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+/**
+ * A "machine" (i.e., a finite state automaton) is a set of state
+ * transition tuples.  A tuple has three elements: the state ID, the ID
+ * of the state that we transition to, and the condition for the
+ * transition.  The condition is simply the character that we must match
+ * to make the transition, or a special distinguished symbol "epsilon,"
+ * which refers to a transition with no input character consumed.
+ * 
+ * The primitive elements of our machines guarantee that we never have
+ * more than two transitions out of a particular state, so we can
+ * denormalize the representation of a state by storing the two possible
+ * tuples for that state in a single combined tuple.  This has the
+ * performance advantage that we can use the state ID as an index into
+ * an array of state tuples.
+ * 
+ * A particular machine always has a single initial and single final
+ * (successful) state, so we can define a machine by its initial and
+ * final state ID's.  
+ */
+enum {
+	// the special symbol value for "epsilon"
+	RE_EPSILON            = '\001',
+
+	// the special symbol value for a wildcard character
+	RE_WILDCARD           = '\002',
+
+	// special symbol values for beginning and end of text
+	RE_TEXT_BEGIN         = '\003',
+	RE_TEXT_END           = '\004',
+
+	// special symbol values for start and end of a word
+	RE_WORD_BEGIN         = '\005',
+	RE_WORD_END           = '\006',
+
+	// special symbols for word-char and non-word-char
+	RE_WORD_CHAR          = '\007',
+	RE_NON_WORD_CHAR      = '\010',
+
+	// special symbols for word-boundary and non-word-boundary
+	RE_WORD_BOUNDARY      = '\011',
+	RE_NON_WORD_BOUNDARY  = '\012',
+
+	// special symbol for a character range/exclusion range
+	RE_RANGE              = '\013',
+	RE_RANGE_EXCL         = '\014',
+
+	// a range of special symbol values for group matchers
+	RE_GROUP_MATCH_0      = '\015',
+	RE_GROUP_MATCH_9      = (RE_GROUP_MATCH_0 + 9)
+};
+
+re_context::re_context(errcxdef *errctx) {
+	// save the error context
+	_errctx = errctx;
+	
+	// clear states
+	_next_state = RE_STATE_FIRST_VALID;
+
+	// clear groups
+	_cur_group = 0;
+
+	// no string buffer yet
+	_strbuf = 0;
+}
+
+re_context::~re_context() {
+	// reset state
+	reset();
+
+	// if we allocated a string buffer, delete it
+	if (_strbuf != 0) {
+		mchfre(_strbuf);
+		_strbuf = nullptr;
+	}
+}
+
+void re_context::reset() {
+	int i;
+	
+	// delete any range tables we've allocated
+	for (i = 0 ; i < _next_state ; ++i) {
+		if (_tuple_arr[i].char_range != 0) {
+			mchfre(_tuple_arr[i].char_range);
+			_tuple_arr[i].char_range = 0;
+		}
+	}
+
+	// clear states
+	_next_state = RE_STATE_FIRST_VALID;
+
+	// clear groups
+	_cur_group = 0;
+}
+
+re_state_id re_context::alloc_state() {
+	// If we don't have enough room for another state, expand the array 
+	if (_next_state >= (int)_tuple_arr.size()) {
+		// bump the size by a bit
+		_tuple_arr.resize(_tuple_arr.size() + 100);
+	}
+
+	// initialize the next state
+	_tuple_arr[_next_state].next_state_1 = RE_STATE_INVALID;
+	_tuple_arr[_next_state].next_state_2 = RE_STATE_INVALID;
+	_tuple_arr[_next_state].ch = RE_EPSILON;
+	_tuple_arr[_next_state].flags = 0;
+	_tuple_arr[_next_state].char_range = 0;
+
+	// return the new state's ID
+	return _next_state++;
+}
+
+void re_context::set_trans(re_state_id id, re_state_id dest_id, char ch) {
+	re_tuple *tuple;
+	
+	/* 
+	 * get the tuple containing the transitions for this state ID - the
+	 * state ID is the index of the state's transition tuple in the array 
+	 */
+	tuple = &_tuple_arr[id];
+
+	/*
+	 * If the first state pointer hasn't been set yet, set it to the new
+	 * destination.  Otherwise, set the second state pointer.
+	 * 
+	 * Only set the character on setting the first state.  When setting
+	 * the second state, we must assume that the character for the state
+	 * has already been set, since any given state can have only one
+	 * character setting.  
+	 */
+	if (tuple->next_state_1 == RE_STATE_INVALID) {
+		/* 
+		 * set the character ID, unless the state has been marked with a
+		 * special flag which indicates that the character value has
+		 * another meaning (in particular, a group marker) 
+		 */
+		if (!(tuple->flags & (RE_STATE_GROUP_BEGIN | RE_STATE_GROUP_END)))
+			tuple->ch = ch;
+
+		// set the first transition
+		tuple->next_state_1 = dest_id;
+	} else {
+		// set only the second transition state - don't set the character
+		tuple->next_state_2 = dest_id;
+	}
+}
+
+void re_context::init_machine(re_machine *machine) {
+	machine->init = alloc_state();
+	machine->final = alloc_state();
+}
+
+void re_context::build_char(re_machine *machine, char ch) {
+	// initialize our new machine
+	init_machine(machine);
+
+	// allocate a transition tuple for the new state
+	set_trans(machine->init, machine->final, ch);
+}
+
+void re_context::build_char_range(re_machine *machine, unsigned char *range, int exclusion) {
+	unsigned char *range_copy;
+	
+	// initialize our new machine
+	init_machine(machine);
+
+	// allocate a transition table for the new state
+	set_trans(machine->init, machine->final, (char)(exclusion ? RE_RANGE_EXCL : RE_RANGE));
+
+	// allocate a copy of the range bit vector
+	range_copy = (unsigned char *)mchalo(_errctx, 32, "regex range");
+
+	// copy the caller's range
+	memcpy(range_copy, range, 32);
+
+	// store it in the tuple
+	_tuple_arr[machine->init].char_range = range_copy;
+}
+
+void re_context::build_group_matcher(re_machine *machine, int group_num) {
+	// initialize our new machine
+	init_machine(machine);
+
+	/* 
+	 * Allocate a transition tuple for the new state, using the group ID
+	 * as the character code.  Store the special code for a group
+	 * recognizer rather than the normal literal character code.  
+	 */
+	set_trans(machine->init, machine->final, (char)(group_num + RE_GROUP_MATCH_0));
+}
+
+void re_context::build_concat(re_machine *new_machine, re_machine *lhs, re_machine *rhs) {
+	// initialize the new machine
+	init_machine(new_machine);
+
+	/* 
+	 * Set up an epsilon transition from the new machine's initial state
+	 * to the first submachine's initial state 
+	 */
+	set_trans(new_machine->init, lhs->init, RE_EPSILON);
+
+	/*
+	 * Set up an epsilon transition from the first submachine's final
+	 * state to the second submachine's initial state 
+	 */
+	set_trans(lhs->final, rhs->init, RE_EPSILON);
+
+	/*
+	 * Set up an epsilon transition from the second submachine's final
+	 * state to our new machine's final state 
+	 */
+	set_trans(rhs->final, new_machine->final, RE_EPSILON);
+}
+
+void re_context::build_group(re_machine *new_machine, re_machine *sub_machine, int group_id) {
+	// initialize the container machine
+	init_machine(new_machine);
+
+	/* 
+	 * set up an epsilon transition from the new machine's initial state
+	 * into the initial state of the group, and another transition from
+	 * the group's final state into the container's final state 
+	 */
+	set_trans(new_machine->init, sub_machine->init, RE_EPSILON);
+	set_trans(sub_machine->final, new_machine->final, RE_EPSILON);
+
+	// Mark the initial and final states of the group machine as being group markers
+	_tuple_arr[new_machine->init].flags |= RE_STATE_GROUP_BEGIN;
+	_tuple_arr[new_machine->final].flags |= RE_STATE_GROUP_END;
+
+	// store the group ID in the 'ch' member of the start and end states
+	_tuple_arr[new_machine->init].ch = group_id;
+	_tuple_arr[new_machine->final].ch = group_id;
+}
+
+void re_context::build_alter(re_machine *new_machine, re_machine *lhs, re_machine *rhs) {
+	// initialize the new machine
+	init_machine(new_machine);
+
+	/*
+	 * Set up an epsilon transition from our new machine's initial state
+	 * to the initial state of each submachine 
+	 */
+	set_trans(new_machine->init, lhs->init, RE_EPSILON);
+	set_trans(new_machine->init, rhs->init, RE_EPSILON);
+
+	/*
+	 * Set up an epsilon transition from the final state of each
+	 * submachine to our final state 
+	 */
+	set_trans(lhs->final, new_machine->final, RE_EPSILON);
+	set_trans(rhs->final, new_machine->final, RE_EPSILON);
+}
+
+void re_context::build_closure(re_machine *new_machine, re_machine *sub, char specifier) {
+	// initialize the new machine
+	init_machine(new_machine);
+
+	/* 
+	 * Set up an epsilon transition from our initial state to the submachine's initial
+	 * state, and from the submachine's final state to our final state 
+	 */
+	set_trans(new_machine->init, sub->init, RE_EPSILON);
+	set_trans(sub->final, new_machine->final, RE_EPSILON);
+
+	/*
+	 * If this is an unbounded closure ('*' or '+', but not '?'), set up
+	 * the loop transition that takes us from the new machine's final
+	 * state back to its initial state.  We don't do this on the
+	 * zero-or-one closure, because we can only match the expression
+	 * once.  
+	 */
+	if (specifier != '?')
+		set_trans(sub->final, sub->init, RE_EPSILON);
+
+	/*
+	 * If this is a zero-or-one closure or a zero-or-more closure, set
+	 * up an epsilon transition from our initial state to our final
+	 * state, since we can skip the entire subexpression.  We don't do
+	 * this on the one-or-more closure, because we can't skip the
+	 * subexpression in this case.  
+	 */
+	if (specifier != '+')
+		set_trans(new_machine->init, new_machine->final, RE_EPSILON);
+}
+
+void re_context::concat_onto(re_machine *dest, re_machine *rhs) {
+	// check for a null destination machine
+	if (dest->isNull()) {
+		/* 
+		 * the first machine is null - simply copy the second machine
+		 * onto the first unchanged 
+		 */
+		*dest = *rhs;
+	} else {
+		re_machine new_machine;
+		
+		// build the concatenated machine
+		build_concat(&new_machine, dest, rhs);
+
+		// copy the concatenated machine onto the first machine
+		*dest = new_machine;
+	}
+}
+
+void re_context::alternate_onto(re_machine *dest, re_machine *rhs) {
+	// check to see if the first machine is null
+	if (dest->isNull()) {
+		/* 
+		 * the first machine is null - simply copy the second machine
+		 * onto the first 
+		 */
+		*dest = *rhs;
+	} else {
+		/* 
+		 * if the second machine is null, don't do anything; otherwise,
+		 * build the alternation 
+		 */
+		if (!rhs->isNull()) {
+			re_machine new_machine;
+			
+			// build the alternation
+			build_alter(&new_machine, dest, rhs);
+
+			// replace the first machine with the alternation
+			*dest = new_machine;
+		}
+	}
+}
+
+/**
+ * Set a bit in a bit vector.
+ */
+#define re_set_bit(set, bit) \
+	(((unsigned char *)(set))[(bit) >> 3] |= (1 << ((bit) & 7)))
+
+/**
+ * Test a bit in a bit vector 
+ */
+#define re_is_bit_set(set, bit) \
+	((((unsigned char *)(set))[(bit) >> 3] & (1 << ((bit) & 7))) != 0)
+
+re_status_t re_context::compile(const char *expr, size_t exprlen, re_machine *result_machine) {
+	re_machine cur_machine;
+	re_machine alter_machine;
+	re_machine new_machine;
+	size_t group_stack_level;
+	struct {
+		re_machine old_cur;
+		re_machine old_alter;
+		int group_id;
+	} group_stack[50];
+
+	// reset everything
+	reset();
+
+	// start out with no current machine and no alternate machine
+	cur_machine.build_null_machine();
+	alter_machine.build_null_machine();
+
+	// nothing on the stack yet
+	group_stack_level = 0;
+
+	// loop until we run out of expression to parse
+	for (; exprlen != 0 ; ++expr, --exprlen) {
+		switch(*expr) {
+		case '^':
+			/*
+			 * beginning of line - if we're not at the beginning of the
+			 * current expression (i.e., we already have some
+			 * concatentations accumulated), treat it as an ordinary
+			 * character 
+			 */
+			if (!cur_machine.isNull())
+				goto normal_char;
+
+			// build a new start-of-text recognizer
+			build_char(&new_machine, RE_TEXT_BEGIN);
+
+			/* 
+			 * concatenate it onto the string - note that this can't
+			 * have any postfix operators 
+			 */
+			concat_onto(&cur_machine, &new_machine);
+			break;
+
+		case '$':
+			/*
+			 * End of line specifier - if there's anything left after
+			 * the '$' other than a close parens or alternation
+			 * specifier, great it as a normal character 
+			 */
+			if (exprlen > 1
+				&& (*(expr+1) != ')' && *(expr+1) != '|'))
+				goto normal_char;
+
+			// build a new end-of-text recognizer
+			build_char(&new_machine, RE_TEXT_END);
+
+			/* 
+			 * concatenate it onto the string - note that this can't
+			 * have any postfix operators 
+			 */
+			concat_onto(&cur_machine, &new_machine);
+			break;
+			
+		case '(':
+			/* 
+			 * Add a nesting level.  Push the current machine and
+			 * alternate machines onto the group stack, and clear
+			 * everything out for the new group. 
+			 */
+			if (group_stack_level > sizeof(group_stack)/sizeof(group_stack[0])) {
+				/* we cannot proceed - return an error */
+				return RE_STATUS_GROUP_NESTING_TOO_DEEP;
+			}
+
+			// save the current state on the stack
+			group_stack[group_stack_level].old_cur = cur_machine;
+			group_stack[group_stack_level].old_alter = alter_machine;
+
+			/* 
+			 * Assign the group a group ID - groups are numbered in
+			 * order of their opening (left) parentheses, so we want to
+			 * assign a group number now.  We won't actually need to
+			 * know the group number until we get to the matching close
+			 * paren, but we need to assign it now, so store it in the
+			 * group stack. 
+			 */
+			group_stack[group_stack_level].group_id = _cur_group;
+
+			// consume the group number
+			_cur_group++;
+
+			// push the level
+			++group_stack_level;
+
+			// start the new group with empty machines
+			cur_machine.build_null_machine();
+			alter_machine.build_null_machine();
+			break;
+
+		case ')':
+			// if there's nothing on the stack, ignore this
+			if (group_stack_level == 0)
+				break;
+
+			// take a level off the stack
+			--group_stack_level;
+
+			/* 
+			 * Remove a nesting level.  If we have a pending alternate
+			 * expression, build the alternation expression.  This will
+			 * leave the entire group expression in alter_machine,
+			 * regardless of whether an alternation was in progress or
+			 * not.  
+			 */
+			alternate_onto(&alter_machine, &cur_machine);
+
+			/*
+			 * Create a group machine that encloses the group and marks
+			 * it with a group number.  We assigned the group number
+			 * when we parsed the open paren, so read that group number
+			 * from the stack.
+			 * 
+			 * Note that this will leave 'new_machine' with the entire
+			 * group machine.  
+			 */
+			build_group(&new_machine, &alter_machine,
+				group_stack[group_stack_level].group_id);
+
+			/*
+			 * Pop the stack - restore the alternation and current
+			 * machines that were in progress before the group started. 
+			 */
+			cur_machine = group_stack[group_stack_level].old_cur;
+			alter_machine = group_stack[group_stack_level].old_alter;
+
+			/*
+			 * Check the group expression (in new_machine) for postfix
+			 * expressions 
+			 */
+			goto apply_postfix;
+
+		case '|':
+			/* 
+			 * Start a new alternation.  This ends the current
+			 * alternation; if we have a previous pending alternate,
+			 * build an alternation machine out of the previous
+			 * alternate and the current machine and move that to the
+			 * alternate; otherwise, simply move the current machine to
+			 * the pending alternate. 
+			 */
+			alternate_onto(&alter_machine, &cur_machine);
+
+			/* 
+			 * the alternation starts out with a blank slate, so null
+			 * out the current machine 
+			 */
+			cur_machine.build_null_machine();
+			break;
+
+		case '%':
+			// quoted character - skip the quote mark and see what we have 
+			++expr;
+			--exprlen;
+
+			// check to see if we're at the end of the expression
+			if (exprlen == 0) {
+				/* 
+				 * end of the string - ignore it, but undo the extra
+				 * increment of the expression index so that we exit the
+				 * enclosing loop properly 
+				 */
+				--expr;
+				++exprlen;
+				break;
+			}
+
+			// see what we have
+			switch(*expr) {
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				// group match - build a new literal group recognizer
+				build_group_matcher(&new_machine, (int)(*expr - '1'));
+
+				// apply any postfix expression to the group recognizer
+				goto apply_postfix;
+
+			case '<':
+				// build a beginning-of-word recognizer
+				build_char(&new_machine, RE_WORD_BEGIN);
+
+				// it can't be postfixed - just concatenate it
+				concat_onto(&cur_machine, &new_machine);
+				break;
+
+			case '>':
+				// build an end-of-word recognizer */
+				build_char(&new_machine, RE_WORD_END);
+
+				// it can't be postfixed - just concatenate it
+				concat_onto(&cur_machine, &new_machine);
+				break;
+
+			case 'w':
+				// word character
+				build_char(&new_machine, RE_WORD_CHAR);
+				goto apply_postfix;
+
+			case 'W':
+				// non-word character
+				build_char(&new_machine, RE_NON_WORD_CHAR);
+				goto apply_postfix;
+
+			case 'b':
+				// word boundary
+				build_char(&new_machine, RE_WORD_BOUNDARY);
+
+				// it can't be postfixed
+				concat_onto(&cur_machine, &new_machine);
+				break;
+
+			case 'B':
+				// not a word boundary
+				build_char(&new_machine, RE_NON_WORD_BOUNDARY);
+
+				// it can't be postfixed
+				concat_onto(&cur_machine, &new_machine);
+				break;
+
+			default:
+				// build a new literal character recognizer
+				build_char(&new_machine, *expr);
+
+				// apply any postfix expression to the character
+				goto apply_postfix;
+			}
+			break;
+
+		case '.':
+			/* 
+			 * wildcard character - build a single character recognizer
+			 * for the special wildcard symbol, then go check it for a
+			 * postfix operator 
+			 */
+			build_char(&new_machine, RE_WILDCARD);
+			goto apply_postfix;
+			break;
+
+		case '[': {
+			// range expression
+			int is_exclusive = false;
+			unsigned char set[32];
+
+			// clear out the set of characters in the range
+			memset(set, 0, sizeof(set));
+
+			// first, skip the open bracket
+			++expr;
+			--exprlen;
+
+			// check to see if starts with the exclusion character
+			if (exprlen != 0 && *expr == '^') {
+				// skip the exclusion specifier
+				++expr;
+				--exprlen;
+
+				// note it
+				is_exclusive = true;
+			}
+
+			// if the first character is a ']', include it in the range 
+			if (exprlen != 0 && *expr == ']') {
+				re_set_bit(set, (int)']');
+				++expr;
+				--exprlen;
+			}
+
+			// if the next character is a '-', include it in the range        
+			if (exprlen != 0 && *expr == '-') {
+				re_set_bit(set, (int)'-');
+				++expr;
+				--exprlen;
+			}
+
+			// scan the character set
+			while (exprlen != 0 && *expr != ']') {
+				int ch;
+
+				// note this character
+				ch = (int)(unsigned char)*expr;
+
+				// set it
+				re_set_bit(set, ch);
+
+				// skip this character of the expression
+				++expr;
+				--exprlen;
+
+				// check for a range
+				if (exprlen != 0 && *expr == '-') {
+					int ch2;
+	
+					// skip the '-'
+					++expr;
+					--exprlen;
+					if (exprlen != 0) {
+						// get the other end of the range
+						ch2 = (int)(unsigned char)*expr;
+
+						// skip the second character
+						++expr;
+						--exprlen;
+
+						// if the range is reversed, swap it
+						if (ch > ch2)
+							SWAP(ch, ch2);
+
+						// fill in the range
+						for ( ; ch <= ch2 ; ++ch)
+							re_set_bit(set, ch);
+					}
+				}
+			}
+
+			// create a character range machine
+			build_char_range(&new_machine, set, is_exclusive);
+
+			// apply any postfix operator
+			goto apply_postfix;
+			break;
+		}
+
+		default:
+		normal_char:
+			/* 
+			 * it's an ordinary character - build a single character
+			 * recognizer machine, and then concatenate it onto any
+			 * existing machine 
+			 */
+			build_char(&new_machine, *expr);
+
+		apply_postfix:
+			/*
+			 * Check for a postfix operator, and apply it to the machine
+			 * in 'new_machine' if present.  In any case, concatenate
+			 * the 'new_machine' (modified by a postix operator or not)
+			 * to the current machien.  
+			 */
+			if (exprlen > 1) {
+				switch(*(expr+1)) {
+				case '*':
+				case '+':
+				case '?':
+					/*
+					 * We have a postfix closure operator.  Build a new
+					 * closure machine out of 'new_machine'.  
+					 */
+					{
+						re_machine closure_machine;
+						
+						// move onto the closure operator
+						++expr;
+						--exprlen;
+						
+						// build the closure machine
+						build_closure(&closure_machine, &new_machine, *expr);
+						
+						// replace the original machine with the closure
+						new_machine = closure_machine;
+						
+						/* 
+						 * skip any redundant closure symbols, keeping
+						 * only the first one we saw 
+						 */
+						while (exprlen > 1 && (*(expr+1) == '?'
+											   || *(expr+1) == '+'
+											   || *(expr+1) == '*')) {
+							++expr;
+							--exprlen;
+						}
+					}
+					break;
+					
+				default:
+					/* no postfix operator */
+					break;
+				}
+			}
+
+			/*
+			 * Concatenate the new machine onto the current machine
+			 * under construction.  
+			 */
+			concat_onto(&cur_machine, &new_machine);
+			break;
+		}
+	}
+
+	// complete any pending alternation
+	alternate_onto(&alter_machine, &cur_machine);
+
+	// store the resulting machine in the caller's machine descriptor
+	*result_machine = alter_machine;
+
+	// no errors encountered
+	return RE_STATUS_SUCCESS;
+}
+
+void re_context::note_group(re_group_register *regs, re_state_id id, const char *p) {
+	int group_index;
+
+	/*
+	 * Check to see if this is a valid state and it's a group marker -
+	 * if not, there's nothing to do 
+	 */
+	if (id == RE_STATE_INVALID
+		|| !(_tuple_arr[id].flags
+			 & (RE_STATE_GROUP_BEGIN | RE_STATE_GROUP_END))
+		|| (group_index = (int)_tuple_arr[id].ch) >= RE_GROUP_REG_CNT)
+		return;
+
+	// It's a valid group marker - note the appropriate register value 
+	if ((_tuple_arr[id].flags & RE_STATE_GROUP_BEGIN) != 0)
+		regs[group_index].start_ofs = p;
+	else
+		regs[group_index].end_ofs = p;
+}
+
+bool re_context::is_word_char(char c) const {
+	return Common::isAlnum(c);
+}
+
+int re_context::match(const char *entire_str, const char *str, size_t origlen,
+		const re_machine *machine, re_group_register *regs) {
+	re_state_id cur_state;
+	const char *p;
+	size_t curlen;
+
+	// start at the machine's initial state
+	cur_state = machine->init;
+
+	// start at the beginning of the string
+	p = str;
+	curlen = origlen;
+
+	// note any group involved in the initial state
+	note_group(regs, cur_state, p);
+
+	/* 
+	 * if we're starting in the final state, immediately return success
+	 * with a zero-length match 
+	 */
+	if (cur_state == machine->final) {
+		// return success with a zero-length match
+		return 0;
+	}
+
+	// run the machine
+	for (;;) {
+		re_tuple *tuple;
+
+		// get the tuple for this state
+		tuple = &_tuple_arr[cur_state];
+
+		// if this is a group state, adjust the group registers
+		note_group(regs, cur_state, p);
+		
+		// see what kind of state we're in
+		if (!(tuple->flags & (RE_STATE_GROUP_BEGIN | RE_STATE_GROUP_END))
+			&& tuple->ch != RE_EPSILON) {
+			/*
+			 * This is a character or group recognizer state.  If we
+			 * match the character or group, continue on to the next
+			 * state; otherwise, return failure.
+			 */
+			switch(tuple->ch) {
+			case RE_GROUP_MATCH_0:
+			case RE_GROUP_MATCH_0 + 1:
+			case RE_GROUP_MATCH_0 + 2:
+			case RE_GROUP_MATCH_0 + 3:
+			case RE_GROUP_MATCH_0 + 4:
+			case RE_GROUP_MATCH_0 + 5:
+			case RE_GROUP_MATCH_0 + 6:
+			case RE_GROUP_MATCH_0 + 7:
+			case RE_GROUP_MATCH_0 + 8:
+			case RE_GROUP_MATCH_0 + 9: {
+				int group_num;
+				re_group_register *group_reg;
+				size_t reg_len;
+
+				// it's a group - get the group number
+				group_num = tuple->ch - RE_GROUP_MATCH_0;
+				group_reg = &regs[group_num];
+					
+				/* 
+				 * if this register isn't defined, there's nothing
+				 * to match, so fail 
+				 */
+				if (group_reg->start_ofs == 0 || group_reg->end_ofs == 0)
+					return -1;
+					
+				// calculate the length of the register value
+				reg_len = group_reg->end_ofs - group_reg->start_ofs;
+
+				// if we don't have enough left to match, it fails
+				if (curlen < reg_len)
+					return -1;
+
+				// if the string doesn't match exactly, we fail
+				if (memcmp(p, group_reg->start_ofs, reg_len) != 0)
+					return -1;
+					
+				/*
+				 * It matches exactly - skip the entire length of
+				 * the register in the source string 
+				 */
+				p += reg_len;
+				curlen -= reg_len;
+				break;
+			}
+
+			case RE_TEXT_BEGIN:
+				/* 
+				 * Match only the exact beginning of the string - if
+				 * we're anywhere else, this isn't a match.  If this
+				 * succeeds, we don't skip any characters.  
+				 */
+				if (p != entire_str)
+					return -1;
+				break;
+
+			case RE_TEXT_END:
+				/*
+				 * Match only the exact end of the string - if we're
+				 * anywhere else, this isn't a match.  Don't skip any
+				 * characters on success.  
+				 */
+				if (curlen != 0)
+					return -1;
+				break;
+
+			case RE_WORD_BEGIN:
+				/* 
+				 * if the previous character is a word character, we're
+				 * not at the beginning of a word 
+				 */
+				if (p != entire_str && is_word_char(*(p - 1)))
+					return -1;
+
+				/* 
+				 * if we're at the end of the string, or the current
+				 * character isn't the start of a word, we're not at the
+				 * beginning of a word 
+				 */
+				if (curlen == 0 || !is_word_char(*p))
+					return -1;
+				break;
+
+			case RE_WORD_END:
+				/*
+				 * if the current character is a word character, we're not
+				 * at the end of a word 
+				 */
+				if (curlen != 0 && is_word_char(*p))
+					return -1;
+
+				/*
+				 * if we're at the beginning of the string, or the
+				 * previous character is not a word character, we're not
+				 * at the end of a word 
+				 */
+				if (p == entire_str || !is_word_char(*(p - 1)))
+					return -1;
+				break;
+
+			case RE_WORD_CHAR:
+				/* if it's not a word character, it's a failure */
+				if (curlen == 0 || !is_word_char(*p))
+					return -1;
+
+				/* skip this character of input */
+				++p;
+				--curlen;
+				break;
+
+			case RE_NON_WORD_CHAR:
+				/* if it's a word character, it's a failure */
+				if (curlen == 0 || is_word_char(*p))
+					return -1;
+
+				/* skip the input */
+				++p;
+				--curlen;
+				break;
+
+			case RE_WORD_BOUNDARY:
+			case RE_NON_WORD_BOUNDARY:
+				{
+					int prev_is_word;
+					int next_is_word;
+					int boundary;
+
+					/*
+					 * Determine if the previous character is a word
+					 * character -- if we're at the beginning of the
+					 * string, it's obviously not, otherwise check its
+					 * classification 
+					 */
+					prev_is_word = (p != entire_str
+									&& is_word_char(*(p - 1)));
+
+					/* make the same check for the current character */
+					next_is_word = (curlen != 0
+									&& is_word_char(*p));
+
+					/*
+					 * Determine if this is a boundary - it is if the
+					 * two states are different 
+					 */
+					boundary = ((prev_is_word != 0) ^ (next_is_word != 0));
+
+					/* 
+					 * make sure it matches what was desired, and return
+					 * failure if not 
+					 */
+					if ((tuple->ch == RE_WORD_BOUNDARY && !boundary)
+						|| (tuple->ch == RE_NON_WORD_BOUNDARY && boundary))
+						return -1;
+				}
+				break;
+
+			case RE_WILDCARD:
+				// make sure we have a character to match
+				if (curlen == 0)
+					return -1;
+
+				// skip this character
+				++p;
+				--curlen;
+				break;
+
+			case RE_RANGE:
+			case RE_RANGE_EXCL: {
+				int match;
+
+				// make sure we have a character to match
+				if (curlen == 0)
+					return -1;
+
+				// see if we match
+				match = re_is_bit_set(tuple->char_range,
+										(int)(unsigned char)*p);
+					
+				// make sure we got what we wanted
+				if ((tuple->ch == RE_RANGE && !match)
+					|| (tuple->ch == RE_RANGE_EXCL && match))
+					return -1;
+
+				// skip this character of the input
+				++p;
+				--curlen;
+				break;
+			}
+
+			default:
+				// make sure we have an exact match
+				if (curlen == 0 || tuple->ch != *p)
+					return -1;
+
+				// skip this character of the input
+				++p;
+				--curlen;
+				break;
+			}
+
+			/* 
+			 * if we got this far, we were successful - move on to the
+			 * next state 
+			 */
+			cur_state = tuple->next_state_1;
+		} else if (tuple->next_state_2 == RE_STATE_INVALID) {
+			/*
+			 * We have only one transition, so this state is entirely
+			 * deterministic.  Simply move on to the next state. 
+			 */
+			cur_state = tuple->next_state_1;
+		} else {
+			re_machine sub_machine;
+			re_group_register regs1[RE_GROUP_REG_CNT];
+			re_group_register regs2[RE_GROUP_REG_CNT];
+			int ret1;
+			int ret2;
+			
+			/*
+			 * This state has two possible transitions, and we don't
+			 * know which one to take.  So, try both, see which one
+			 * works better, and return the result.  Try the first
+			 * transition first.  Note that each separate attempt must
+			 * use a separate copy of the registers.  
+			 */
+			memcpy(regs1, regs, sizeof(regs1));
+			sub_machine.init = tuple->next_state_1;
+			sub_machine.final = machine->final;
+			ret1 = match(entire_str, p, curlen, &sub_machine, regs1);
+
+			/*
+			 * Now try the second transition 
+			 */
+			memcpy(regs2, regs, sizeof(regs2));
+			sub_machine.init = tuple->next_state_2;
+			sub_machine.final = machine->final;
+			ret2 = match(entire_str, p, curlen, &sub_machine, regs2);
+
+			/*
+			 * If they both failed, the whole thing failed.  Otherwise,
+			 * return the longer of the two, plus the length we
+			 * ourselves matched previously.  Note that we return the
+			 * register set from the winning match.  
+			 */
+			if (ret1 < 0 && ret2 < 0) {
+				// they both failed
+				return -1;
+			} else if (ret1 > ret2) {
+				// use the first register set and result length
+				memcpy(regs, regs1, sizeof(regs1));
+				return ret1 + (p - str);
+			} else {
+				// use the second register set and result length
+				memcpy(regs, regs2, sizeof(regs2));
+				return ret2 + (p - str);
+			}
+		}
+
+		// If we're in the final state, return success 
+		if (cur_state == machine->final) {
+			// finish off any group involved in the final state
+			note_group(regs, cur_state, p);
+			
+			// return the length we matched
+			return p - str;
+		}
+	}
+}
+
+int re_context::search(const char *str, size_t len, const re_machine *machine,
+		re_group_register *regs, int *result_len) {
+	int ofs;
+	
+	/*
+	 * Starting at the first character in the string, search for the
+	 * pattern at each subsequent character until we either find the
+	 * pattern or run out of string to test. 
+	 */
+	for (ofs = 0 ; ofs < (int)len ; ++ofs) {
+		int matchlen;
+		
+		// check for a match
+		matchlen = match(str, str + ofs, len - ofs, machine, regs);
+		if (matchlen >= 0) {
+			// we found a match here - return the length and offset
+			*result_len = matchlen;
+			return ofs;
+		}
+	}
+
+	// we didn't find a match
+	return -1;
+}
+
+void re_context::save_search_str(const char *str, size_t len) {
+	// if the string is empty, this is easy
+	if (len == 0) {
+		// nothing to store - just save the length and return
+		_curlen = 0;
+		return;
+	}
+	
+	// if the current buffer isn't big enough, allocate a new one
+	if (_strbuf == 0 || _strbufsiz < len) {
+		/* 
+		 * free any previous buffer - its contents are no longer
+		 * important, since we're about to overwrite it with a new
+		 * string 
+		 */
+		if (_strbuf != 0)
+			mchfre(_strbuf);
+
+		/* 
+		 * allocate a new buffer; round up to the next 256-byte
+		 * increment to make sure we're not constantly reallocating to
+		 * random sizes 
+		 */
+		_strbufsiz = ((len + 255) & ~255);
+
+		// allocate it
+		_strbuf = (char *)mchalo(_errctx, _strbufsiz, "regex str");
+	}
+
+	// copy the string
+	memcpy(_strbuf, str, len);
+
+	// save the length
+	_curlen = len;
+}
+
+int re_context::compile_and_search(const char *pattern, size_t patlen,
+		const char *searchstr, size_t searchlen, int *result_len) {
+	re_machine machine;
+	
+	// compile the expression - return failure if we get an error
+	if (compile(pattern, patlen, &machine) != RE_STATUS_SUCCESS)
+		return -1;
+
+	// save the search string in our internal buffer
+	save_search_str(searchstr, searchlen);
+
+	// clear the group registers
+	memset(_regs, 0, sizeof(_regs));
+
+	/* 
+	 * search for the pattern in our copy of the string - use the copy
+	 * so that the group registers stay valid even if the caller
+	 * deallocates the original string after we return
+	 */
+	return search(_strbuf, _curlen, &machine, _regs, result_len);
+}
+
+int re_context::compile_and_match(const char *pattern, size_t patlen,
+		const char *searchstr, size_t searchlen) {
+	re_machine machine;
+
+	// compile the expression - return failure if we get an error
+	if (compile(pattern, patlen, &machine) != RE_STATUS_SUCCESS)
+		return 0;
+
+	// save the search string in our internal buffer
+	save_search_str(searchstr, searchlen);
+
+	// clear the group registers
+	memset(_regs, 0, sizeof(_regs));
+
+	// match the string
+	return match(_strbuf, _strbuf, _curlen, &machine, _regs);
+}
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // Engine of namespace GLK
diff --git a/engines/glk/tads/tads2/regex.h b/engines/glk/tads/tads2/regex.h
new file mode 100644
index 0000000..9040a02
--- /dev/null
+++ b/engines/glk/tads/tads2/regex.h
@@ -0,0 +1,315 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GLK_TADS_TADS2_REGEX
+#define GLK_TADS_TADS2_REGEX
+
+#include "common/array.h"
+#include "engines/glk/tads/tads2/ler.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+/**
+ * state ID
+ */
+typedef int re_state_id;
+
+/**
+ * invalid state ID - used to mark null machines
+ */
+#define RE_STATE_INVALID   ((re_state_id)-1)
+
+/**
+ * first valid state ID
+ */
+#define RE_STATE_FIRST_VALID  ((re_state_id)0)
+
+
+/**
+ *   Group register structure.  Each register keeps track of the starting
+ *   and ending offset of the group's text.  
+ */
+struct re_group_register {
+    const char *start_ofs;
+    const char *end_ofs;
+};
+
+/**
+ * number of group registers we keep
+ */
+#define RE_GROUP_REG_CNT  10
+
+/**
+ * Denormalized state transition tuple.  Each tuple represents the
+ * complete set of transitions out of a particular state.  A particular
+ * state can have one character transition, or two epsilon transitions.
+ * Note that we don't need to store the state ID in the tuple, because
+ * the state ID is the index of the tuple in an array of state tuples.  
+ */
+struct re_tuple {
+    // the character we must match to transition to the target state
+    char ch;
+
+    // the target states
+    re_state_id next_state_1;
+    re_state_id next_state_2;
+
+    // character range match table, if used
+    unsigned char *char_range;
+
+    // flags
+    byte flags;
+};
+
+
+/**
+ * Tuple flags 
+ */
+enum {
+	// this state is the start of a group - the 'ch' value is the group ID
+	RE_STATE_GROUP_BEGIN = 0x02,
+
+	// this state is the end of a group - 'ch' is the group ID */
+	RE_STATE_GROUP_END = 0x04
+};
+
+/**
+ * Status codes 
+ */
+typedef enum {
+    // success
+    RE_STATUS_SUCCESS = 0,
+
+    // compilation error - group nesting too deep
+    RE_STATUS_GROUP_NESTING_TOO_DEEP
+} re_status_t;
+
+
+/**
+ * Regular expression compilation.  This tracks the state of the compilation and
+ * stores the resources associated with the compiled expression.  
+ */
+class re_context {
+	/**
+	 * A machine description.  Machines are fully described by their initial
+	 * and final state ID's.  
+	 */
+	struct re_machine {
+		re_state_id init;		///< the machine's initial state
+		re_state_id final;		///< the machine's final state
+
+		re_machine() : init(0), final(0) {}
+
+		/**
+		 * Build a null machine
+		 */
+		void build_null_machine() {
+			init = final = RE_STATE_INVALID;
+		}
+
+		/**
+		 * Determine if a machine is null
+		 */
+		bool isNull() const {
+			return (init == RE_STATE_INVALID);
+		}
+	};
+private:
+	/**
+	 * Reset compiler - clears states and tuples
+	 */
+	void reset();
+
+	/**
+	 * Set a transition from a state to a given destination state
+	 */
+	void set_trans(re_state_id id, re_state_id dest_id, char ch);
+
+	/**
+	 * Initialize a new machine, giving it an initial and final state
+	 */
+	void init_machine(re_machine *machine);
+
+	/**
+	 * Build a character recognizer
+	 */
+	void build_char(re_machine *machine, char ch);
+
+	/**
+	 * Build a character range recognizer.  'range' is a 256-bit (32-byte) bit vector.
+	 */
+	void build_char_range(re_machine *machine, unsigned char *range, int exclusion);
+
+	/**
+	 * Build a group recognizer.  This is almost the same as a character
+	 * recognizer, but matches a previous group rather than a literal character.
+	 */
+	void build_group_matcher(re_machine *machine, int group_num);
+
+	/**
+	 *   Build a concatenation recognizer 
+	 */
+	void build_concat(re_machine *new_machine, re_machine *lhs, re_machine *rhs);
+
+	/**
+	 * Build a group machine.  sub_machine contains the machine that
+	 * expresses the group's contents; we'll fill in new_machine with a
+	 * newly-created machine that encloses and marks the group.
+	 */
+	void build_group(re_machine *new_machine, re_machine *sub_machine, int group_id);
+
+	/**
+	 * Build an alternation recognizer 
+	 */
+	void build_alter(re_machine *new_machine, re_machine *lhs, re_machine *rhs);
+
+	/**
+	 * Build a closure recognizer
+	 */
+	void build_closure(re_machine *new_machine, re_machine *sub, char specifier);
+
+	/**
+	 * Concatenate the second machine onto the first machine, replacing the
+	 * first machine with the resulting machine.  If the first machine is a
+	 * null machine (created with re_build_null_machine), we'll simply copy
+	 * the second machine into the first.
+	 */
+	void concat_onto(re_machine *dest, re_machine *rhs);
+
+	/**
+	 * Alternate the second machine onto the first machine, replacing the
+	 * first machine with the resulting machine.  If the first machine is a
+	 * null machine, this simply replaces the first machine with the second
+	 * machine.  If the second machine is null, this simply leaves the first
+	 * machine unchanged. 
+	 */
+	void alternate_onto(re_machine *dest, re_machine *rhs);
+
+	/**
+	 * Compile an expression 
+	 */
+	re_status_t compile(const char *expr, size_t exprlen, re_machine *result_machine);
+
+	/**
+	 * Note a group position if appropriate
+	 */
+	void note_group(re_group_register *regs, re_state_id id, const char *p);
+
+	/**
+	 * Determine if a character is part of a word.  We consider letters and
+	 * numbers to be word characters.
+	 */
+	bool is_word_char(char c) const;
+
+	/**
+	 * Match a string to a compiled expression.  Returns the length of the
+	 * match if successful, or -1 if no match was found.
+	 */
+	int match(const char *entire_str, const char *str, size_t origlen,
+		const re_machine *machine, re_group_register *regs);
+
+	/**
+	 * Search for a regular expression within a string.  Returns -1 if the string
+	 * cannot be found, otherwise returns the offset from the start of the string
+	 * to be searched of the start of the first match for the pattern.
+	 */
+	int search(const char *str, size_t len, const re_machine *machine,
+		re_group_register *regs, int *result_len);
+
+	/**
+	 * Make a copy of a search string in our private buffer.
+	 */
+	void save_search_str(const char *str, size_t len);
+public:
+	errcxdef *_errctx;			///< error context
+    re_state_id _next_state;	///< next available state ID 
+
+    /**
+     * The array of transition tuples.  We'll allocate this array and
+     * expand it as necessary.  
+     */
+    Common::Array<re_tuple> _tuple_arr;
+
+    // current group ID
+    int _cur_group;
+
+    // group registers
+    re_group_register _regs[RE_GROUP_REG_CNT];
+
+    /**
+     * Buffer for retaining a copy of the last string we scanned.  We
+     * retain our own copy of each string, and point the group registers
+     * into this copy rather than the caller's original string -- this
+     * ensures that the group registers remain valid even after the
+     * caller has deallocated the original string.  
+     */
+    char *_strbuf;
+
+    /**
+	 * length of the string currently in the buffer
+	 */
+    size_t _curlen;
+
+    /**
+	 * size of the buffer allocated to strbuf
+	 */
+    size_t _strbufsiz;
+public:
+	/**
+	 * Constructor.  The memory for the context structure itself
+	 * must be allocated and maintained by the caller. 
+	 */
+	re_context(errcxdef *errctx);
+
+	/**
+	 * Destructor
+	 */
+	~re_context();
+
+	/**
+	 * Allocate a new state ID 
+	 */
+	re_state_id alloc_state();
+
+	/**
+	 * Compile an expression and search for a match within the given string.
+	 * Returns the offset of the match, or -1 if no match was found.  
+	 */
+	int compile_and_search(const char *pattern, size_t patlen,
+		const char *searchstr, size_t searchlen, int *result_len);
+
+	/**
+	 * Compile an expression and check for a match.  Returns the length of the match
+	 * if we found a match, -1 if we found no match.  This is not a search function;
+	 * we merely match the leading substring of the given string to the given pattern.  
+	 */
+	int compile_and_match(const char *pattern, size_t patlen,
+		const char *searchstr, size_t searchlen);
+};
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // Engine of namespace GLK
+
+#endif
diff --git a/engines/glk/tads/tads2/tads2.cpp b/engines/glk/tads/tads2/tads2.cpp
index 9c50c20d..79a36fa 100644
--- a/engines/glk/tads/tads2/tads2.cpp
+++ b/engines/glk/tads/tads2/tads2.cpp
@@ -27,6 +27,7 @@ namespace TADS {
 namespace TADS2 {
 
 TADS2::TADS2(OSystem *syst, const GlkGameDescription &gameDesc) : OS(syst, gameDesc) {
+	cmap_init_default();
 }
 
 void TADS2::runGame(Common::SeekableReadStream *gameFile) {
diff --git a/engines/glk/tads/tads2/tads2.h b/engines/glk/tads/tads2/tads2.h
index 2c799a3..f0c42b3 100644
--- a/engines/glk/tads/tads2/tads2.h
+++ b/engines/glk/tads/tads2/tads2.h
@@ -31,27 +31,75 @@ namespace TADS {
 namespace TADS2 {
 
 /**
+ * map a native character (read externally) into an internal character
+ */
+#define cmap_n2i(c) (G_cmap_input[(unsigned char)(c)])
+
+/**
+ * map an internal character into a native character (for display)
+ */
+#define cmap_i2n(c) (G_cmap_output[(unsigned char)(c)])
+
+/**
+ * the full name (for display purposes) of the loaded character set
+ */
+#define CMAP_LDESC_MAX_LEN  40
+
+/**
+ * Maximum expansion for an HTML entity mapping 
+ */
+#define CMAP_MAX_ENTITY_EXPANSION  50
+
+
+/**
  * TADS 2 game interpreter
  */
 class TADS2 : public OS {
 private:
 	// STUBS
 	void os_printz(const Common::String &s) {}
-public:
+	void tio_set_html_expansion(unsigned int html_char_val,
+		const char *expansion, size_t expansion_len) {}
+private:
 	/**
-	 * Constructor
+	 * \defgroup cmap
+	 * @{
 	 */
-	TADS2(OSystem *syst, const GlkGameDescription &gameDesc);
 
 	/**
-	 * Execute the game
+	 * flag: true -> a character set has been explicitly loaded, so we
+	 * should ignore any game character set setting 
 	 */
-	virtual void runGame(Common::SeekableReadStream *gameFile) override;
+	bool S_cmap_loaded;
 
 	/**
-	 * Returns the running interpreter type
+	 * input-mapping table - for native character 'n', cmap_input[n] yields
+	 * the internal character code 
+	 */
+	unsigned char G_cmap_input[256];
+
+	/**
+	 * output-mapping table - for internal character 'n', cmap_output[n]
+	 * yields the output character code 
+	 */
+	unsigned char G_cmap_output[256];
+
+	/**
+	 * the ID of the loaded character set
+	 */
+	char G_cmap_id[5];
+
+	/**
+	 * the full name (for display purposes) of the loaded character set
+	 */
+	char G_cmap_ldesc[CMAP_LDESC_MAX_LEN + 1];
+
+	/**@}*/
+private:
+	/**
+	 * \defgroup trd
+	 * @{
 	 */
-	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_TADS2; }
 
 	void trdmain1(errcxdef *errctx);
 
@@ -59,6 +107,72 @@ public:
 	 * printf-style formatting
 	 */
 	void trdptf(const char *fmt, ...);
+
+	/**@}*/
+
+	/**
+	 * \defgroup cmap
+	 * @{
+	 */
+
+	/**
+	 * Initialize the default character mappings.  If no mapping file is to
+	 * be read, this function will establish identify mappings that leave
+	 * characters untranslated. 
+	 */
+	void cmap_init_default();
+
+	/**
+	 * Load a character map file.  Returns zero on success, non-zero on
+	 * failure.  If filename is null, we'll use the default mapping.
+	 */
+	int cmap_load(const char *filename);
+
+	/**
+	 * Turn off character translation.  This overrides any game character
+	 * set that we find and simply uses the default translation. 
+	 */
+	void cmap_override(void);
+
+	/**
+	 * Set the game's internal character set.  This should be called when a
+	 * game is loaded, and the game specifies an internal character set.  If
+	 * there is no character map file explicitly loaded, we will attempt to
+	 * load a character mapping file that maps this character set to the
+	 * current native character set.  Signals an error on failure.  This
+	 * routine will succeed (without doing anything) if a character set has
+	 * already been explicitly loaded, since an explicitly-loaded character
+	 * set overrides the automatic character set selection that we attempt
+	 * when loading a game.
+	 * 
+	 * argv0 must be provided so that we know where to look for our mapping
+	 * file on systems where mapping files are stored in the same directory
+	 * as the TADS executables.  
+	 */
+	void cmap_set_game_charset(errcxdef *errctx, const char *internal_id,
+		const char *internal_ldesc, const char *argv0);
+
+	/**
+	 * Internal routine to load a character map from a file 
+	 */
+	int cmap_load_internal(const char *filename);
+
+	/**@}*/
+public:
+	/**
+	 * Constructor
+	 */
+	TADS2(OSystem *syst, const GlkGameDescription &gameDesc);
+
+	/**
+	 * Execute the game
+	 */
+	virtual void runGame(Common::SeekableReadStream *gameFile) override;
+
+	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_TADS2; }
 };
 
 typedef TADS2 appctxdef;
diff --git a/engines/glk/tads/tads2/tads2_cmap.cpp b/engines/glk/tads/tads2/tads2_cmap.cpp
new file mode 100644
index 0000000..cf63c9a
--- /dev/null
+++ b/engines/glk/tads/tads2/tads2_cmap.cpp
@@ -0,0 +1,268 @@
+/* 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/tads/tads2/tads2.h"
+#include "glk/tads/tads2/types.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+/**
+ * Signatures for character map files.  The signature is stored at the
+ * beginning of the file.  
+ */
+// single-byte character map version 1.0.0
+#define CMAP_SIG_S100  "TADS2 charmap S100\n\r\01a"
+
+void TADS2::cmap_init_default() {
+    size_t i;
+
+    // initialize the input table
+    for (i = 0 ; i < sizeof(G_cmap_input)/sizeof(G_cmap_input[0]) ; ++i)
+        G_cmap_input[i] = (unsigned char)i;
+
+    // initialize the output table
+    for (i = 0 ; i < sizeof(G_cmap_output)/sizeof(G_cmap_output[0]) ; ++i)
+        G_cmap_output[i] = (unsigned char)i;
+
+    // we have a null ID
+    memset(G_cmap_id, 0, sizeof(G_cmap_id));
+
+    // indicate that it's the default
+    strcpy(G_cmap_ldesc, "(native/no mapping)");
+
+    // note that we have no character set loaded
+    S_cmap_loaded = false;
+}
+
+int TADS2::cmap_load_internal(const char *filename) {
+    osfildef *fp;
+    static char sig1[] = CMAP_SIG_S100;
+    char buf[256];
+    uchar lenbuf[2];
+    size_t len;
+    int sysblk;
+
+    // if there's no mapping file, use the default mapping
+    if (filename == 0) {
+        // initialize with the default mapping
+        cmap_init_default();
+
+        // return success
+        return 0;
+    }
+    
+    // open the file
+    fp = osfoprb(filename, OSFTCMAP);
+    if (fp == 0)
+        return 1;
+
+    // check the signature
+    if (osfrb(fp, buf, sizeof(sig1))
+        || memcmp(buf, sig1, sizeof(sig1)) != 0) {
+        osfcls(fp);
+        return 2;
+    }
+
+    // load the ID
+    G_cmap_id[4] = '\0';
+    if (osfrb(fp, G_cmap_id, 4)) {
+        osfcls(fp);
+        return 3;
+    }
+
+    // load the long description
+    if (osfrb(fp, lenbuf, 2)
+        || (len = osrp2(lenbuf)) > sizeof(G_cmap_ldesc)
+        || osfrb(fp, G_cmap_ldesc, len)) {
+        osfcls(fp);
+        return 4;
+    }
+
+    // load the two tables - input, then output
+    if (osfrb(fp, G_cmap_input, sizeof(G_cmap_input))
+        || osfrb(fp, G_cmap_output, sizeof(G_cmap_output))) {
+        osfcls(fp);
+        return 5;
+    }
+
+    // read the next section header
+    if (osfrb(fp, buf, 4)) {
+        osfcls(fp);
+        return 6;
+    }
+
+    // if it's "SYSI", read the system information string
+    if (!memcmp(buf, "SYSI", 4)) {
+        // read the length prefix, then the string
+        if (osfrb(fp, lenbuf, 2)
+            || (len = osrp2(lenbuf)) > sizeof(buf)
+            || osfrb(fp, buf, len)) {
+            osfcls(fp);
+            return 7;
+        }
+
+        // we have a system information block
+        sysblk = true;
+    } else {
+        // there's no system information block
+        sysblk = false;
+    }
+
+    /*
+     * call the OS code, so that it can do any system-dependent
+     * initialization for the new character mapping 
+     */
+    os_advise_load_charmap(G_cmap_id, G_cmap_ldesc, sysblk ? buf : "");
+
+    // read the next section header
+    if (sysblk && osfrb(fp, buf, 4)) {
+        osfcls(fp);
+        return 8;
+    }
+
+    // see if we have an entity list
+    if (!memcmp(buf, "ENTY", 4)) {
+        // read the entities
+        for (;;) {
+            unsigned int cval;
+            char expansion[CMAP_MAX_ENTITY_EXPANSION];
+            
+            // read the next item's length and character value
+            if (osfrb(fp, buf, 4)) {
+                osfcls(fp);
+                return 9; 
+            }
+
+            // decode the values
+            len = osrp2(buf);
+            cval = osrp2(buf+2);
+
+            // if we've reached the zero marker, we're done
+            if (len == 0 && cval == 0)
+                break;
+
+            // read the string
+            if (len > CMAP_MAX_ENTITY_EXPANSION
+                || osfrb(fp, expansion, len)) {
+                osfcls(fp);
+                return 10;
+            }
+
+            // tell the output code about the expansion
+            tio_set_html_expansion(cval, expansion, len);
+        }
+    }
+
+    /* 
+     * ignore anything else we find - if the file format is updated to
+     * include extra information in the future, and this old code tries
+     * to load an updated file, we'll just ignore the new information,
+     * which should always be placed after the "SYSI" block (if present)
+     * to ensure compatibility with past versions (such as this code)
+     */
+    // no problems - close the file and return success
+    osfcls(fp);
+    return 0;
+}
+
+int TADS2::cmap_load(const char *filename) {
+    int err;
+    
+    // try loading the file
+    if ((err = cmap_load_internal(filename)) != 0)
+        return err;
+
+    /*
+     * note that we've explicitly loaded a character set, if they named
+     * a character set (if not, this simply establishes the default
+     * setting, so we haven't explicitly loaded anything) 
+     */
+    if (filename != nullptr)
+        S_cmap_loaded = true;
+
+    // success
+    return 0;
+}
+
+void TADS2::cmap_override() {
+    // apply the default mapping
+    cmap_init_default();
+
+    /* 
+     * pretend we have a character map loaded, so that we don't try to
+     * load another one if the game specifies a character set
+     */
+    S_cmap_loaded = true;
+}
+
+void TADS2::cmap_set_game_charset(errcxdef *ec, const char *internal_id,
+		const char *internal_ldesc, const char *argv0) {
+    char filename[OSFNMAX];
+    
+    /* 
+     * If a character set is already explicitly loaded, ignore the
+     * game's character set - the player asked us to use a particular
+     * mapping, so ignore what the game wants.  (This will probably
+     * result in incorrect display of non-ASCII character values, but
+     * the player is most likely to use this to avoid errors when an
+     * appropriate mapping file for the game is not available.  In this
+     * case, the player informs us by setting the option that he or she
+     * knows and accepts that the game will not look exactly right.)  
+     */
+    if (S_cmap_loaded)
+        return;
+
+    /*
+     * ask the operating system to name the mapping file -- this routine
+     * will determine, if possible, the current native character set,
+     * and apply a system-specific naming convention to tell us what
+     * mapping file we should open 
+     */
+    os_gen_charmap_filename(filename, internal_id, argv0);
+
+    // try loading the mapping file
+    if (cmap_load_internal(filename))
+        errsig2(ec, ERR_CHRNOFILE,
+                ERRTSTR, errstr(ec, filename, strlen(filename)),
+                ERRTSTR, errstr(ec, internal_ldesc, strlen(internal_ldesc)));
+
+    /**
+     * We were successful - the game's internal character set is now
+     * mapped to the current native character set.  Even though we
+     * loaded an ldesc from the mapping file, forget that and store the
+     * internal ldesc that the game specified.  The reason we do this is
+     * that it's possible that the player will dynamically switch native
+     * character sets in the future, at which point we'll need to
+     * re-load the mapping table, which could raise an error if a
+     * mapping file for the new character set isn't available.  So, we
+     * may need to provide the same explanation later that we needed to
+     * provide here.  Save the game's character set ldesc for that
+     * eventuality, since it describes exactly what the *game* wanted.  
+     */
+    strcpy(G_cmap_ldesc, internal_ldesc);
+}
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
diff --git a/engines/glk/tads/tads2/types.h b/engines/glk/tads/tads2/types.h
new file mode 100644
index 0000000..52a5309
--- /dev/null
+++ b/engines/glk/tads/tads2/types.h
@@ -0,0 +1,188 @@
+/* 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_TADS_TADS2_TYPES
+#define GLK_TADS_TADS2_TYPES
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+typedef unsigned char uchar;
+typedef Common::SeekableReadStream osfildef;
+
+/**
+ * Allocate a memory block
+ */
+#define mchalo(CTX, SIZE, COMMENT) ((byte *)new byte[SIZE])
+
+/**
+ * Free a memory block
+ */
+#define mchfre(PTR) delete[] (byte *)PTR
+
+/**
+ * Close a file
+ */
+#define osfcls(FP) delete FP
+
+/**
+ * Read from a file
+ */
+#define osfrb(FP, BUF, SIZE) FP->read(BUF, SIZE)
+
+/**
+ * Read a 16-bit integer from memory
+ */
+#define osrp2(MEM) READ_LE_UINT16(MEM)
+
+/**
+ * The character (or characters) which mark the beginning of a special fileref string.
+ * The important thing is that the string be one that is either not allowed in
+ * filenames on your platform or is unlikely to be the first part of a filename.
+ */
+#define OSS_FILEREF_STRING_PREFIX ":"
+
+/**
+ * The character (or characters) which mark the end of a special fileref string.
+ * Using this and OSS_FILEREF_STRING_PREFIX, you should be able to come up with
+ * something which forms an invalid filename
+ */
+#define OSS_FILEREF_STRING_SUFFIX ""
+
+/**
+ * Maximum length of status line text
+ */
+#define OSS_STATUS_STRING_LEN 80
+
+/**
+ * Maximum size for filenames
+ */
+#define OSFNMAX 1024
+
+/**
+ * Important note: do not change these values when porting TADS.  These
+ * values can be used by games, so they must be the same on all platforms.
+ */
+enum {
+	OS_AFP_OPEN = 1,			///< choose an existing file to open for reading
+	OS_AFP_SAVE = 2				///< choose a filename for saving to a file
+};
+
+/**
+ * File types.These type codes are used when opening or creating a file,
+ * so that the OS routine can set appropriate file system metadata
+ * to describe or find the file type.
+ *
+ * The type os_filetype_t is defined for documentary purposes; it's
+ * always just an int.
+ */
+enum os_filetype_t {
+	OSFTGAME  =  0,  ///< a game data file (.gam)
+	OSFTSAVE  =  1,  ///< a saved game (.sav)
+	OSFTLOG   =  2,  ///< a transcript (log) file
+	OSFTSWAP  =  3,  ///< swap file
+	OSFTDATA  =  4,  ///< user data file (used with the TADS fopen() call)
+	OSFTCMD   =  5,  ///< QA command/log file
+	OSFTERRS  =  6,  ///< error message file
+	OSFTTEXT  =  7,  ///< text file - used for source files
+	OSFTBIN   =  8,  ///< binary file of unknown type - resources, etc
+	OSFTCMAP  =  9,  ///< character mapping file
+	OSFTPREF  = 10,  ///< preferences file
+	OSFTUNK   = 11,  ///< unknown - as a filter, matches any file type
+	OSFTT3IMG = 12,  ///< T3 image file (.t3 - formerly .t3x)
+	OSFTT3OBJ = 13,  ///< T3 object file (.t3o)
+	OSFTT3SYM = 14,  ///< T3 symbol export file (.t3s)
+	OSFTT3SAV = 15   ///< T3 saved state file (.t3v)
+};
+
+/**
+ * Constants for os_getc() when returning commands.  When used for command line
+ * editing, special keys (arrows, END, etc.)  should cause os_getc() to return 0,
+ * and return the appropriate CMD_ value on the NEXT call.  Hence, os_getc() must
+ * keep the appropriate information around statically for the next call when a
+ * command key is issued.
+ *
+ * The comments indicate which CMD_xxx codes are "translated" codes and which are
+ * "raw"; the difference is that, when a particular keystroke could be interpreted
+ * as two different CMD_xxx codes, one translated and the other raw, os_getc()
+ * should always return the translated version of the key, and os_getc_raw()
+ * should return the raw version.
+ */
+enum KeyCmd {
+	CMD_UP = 1,		    ///< move up/up arrow (translated)
+	CMD_DOWN = 2,		///< move down/down arrow (translated)
+	CMD_RIGHT = 3,		///< move right/right arrow (translated)
+	CMD_LEFT = 4,		///< move left/left arrow (translated)
+	CMD_END = 5,		///< move cursor to end of line (translated)
+	CMD_HOME = 6,		///< move cursor to start of line (translated)
+	CMD_DEOL = 7,		///< delete to end of line (translated)
+	CMD_KILL = 8,		///< delete entire line (translated)
+	CMD_DEL = 9,		///< delete current character (translated)
+	CMD_SCR = 10,		///< toggle scrollback mode (translated)
+	CMD_PGUP = 11,		///< page up (translated)
+	CMD_PGDN = 12,		///< page down (translated)
+	CMD_TOP = 13,		///< top of file (translated)
+	CMD_BOT = 14,		///< bottom of file (translated)
+	CMD_F1 = 15,		///< function key F1 (raw)
+	CMD_F2 = 16,		///< function key F2 (raw)
+	CMD_F3 = 17,		///< function key F3 (raw)
+	CMD_F4 = 18,		///< function key F4 (raw)
+	CMD_F5 = 19,		///< function key F5 (raw)
+	CMD_F6 = 20,		///< function key F6 (raw)
+	CMD_F7 = 21,		///< function key F7 (raw)
+	CMD_F8 = 22,		///< function key F8 (raw)
+	CMD_F9 = 23,		///< function key F9 (raw)
+	CMD_F10 = 24,		///< function key F10 (raw)
+	CMD_CHOME = 25,		///< control-home (raw)
+	CMD_TAB = 26,		///< tab (translated)
+	CMD_SF2 = 27,		///< shift-F2 (raw)
+						///< not used (obsolete) - 28
+	CMD_WORD_LEFT = 29,	///< word left (ctrl-left on dos) (translated)
+	CMD_WORD_RIGHT = 30,///< word right (ctrl-right on dos) (translated)
+	CMD_WORDKILL = 31,	///< delete word right (translated)
+	CMD_EOF = 32,       ///< end-of-file (raw)
+	CMD_BREAK = 33,     ///< break (Ctrl-C or local equivalent) (translated)
+	CMD_INS = 34,       ///< insert key (raw)
+
+					/**
+					 * ALT-keys - add alphabetical code to CMD_ALT: ALT-A == CMD_ALT + 0,
+					 * ALT-B == CMD_ALT + 1, ALT-C == CMD_ALT + 2, etc
+					 *
+					 * These keys are all raw (untranslated).
+					 */
+	CMD_ALT = 128       ///< start of ALT keys
+};
+
+/**
+ * Status mode codes
+ */
+enum StatusMode {
+	OSS_STATUS_MODE_STORY = 0,
+	OSS_STATUS_MODE_STATUS = 1
+};
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
+
+#endif


Commit: 14871c28bc9ec1b210412a072f0ab292febfd368
    https://github.com/scummvm/scummvm/commit/14871c28bc9ec1b210412a072f0ab292febfd368
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Added Data class

Changed paths:
  A engines/glk/tads/tads2/data.cpp
  A engines/glk/tads/tads2/data.h
  A engines/glk/tads/tads2/vocabulary.cpp
  A engines/glk/tads/tads2/vocabulary.h
    engines/glk/module.mk
    engines/glk/tads/tads2/types.h


diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 5c17362..003f8dc 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -45,11 +45,13 @@ MODULE_OBJS := \
 	scott/scott.o \
 	tads/detection.o \
 	tads/tads.o \
+	tads/tads2/data.o \
 	tads/tads2/ler.o \
 	tads/tads2/os.o \
 	tads/tads2/regex.o \
 	tads/tads2/tads2.o \
 	tads/tads2/tads2_cmap.o \
+	tads/tads2/vocabulary.o \
 	tads/tads3/tads3.o
 
 # This module can be built as a plugin
diff --git a/engines/glk/tads/tads2/data.cpp b/engines/glk/tads/tads2/data.cpp
new file mode 100644
index 0000000..e142f37
--- /dev/null
+++ b/engines/glk/tads/tads2/data.cpp
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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/glk/tads/tads2/data.h"
+#include "engines/glk/tads/tads2/types.h"
+#include "engines/glk/tads/tads2/vocabulary.h"
+#include "common/algorithm.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+Data::Data(DataType type) : _type(type) {
+}
+
+size_t Data::size() const {
+	switch (_type) {
+	case DAT_NUMBER:
+		return 4;		// numbers are in 4-byte lsb-first format
+
+	case DAT_OBJECT:
+		return 2;      // object numbers are in 2-byte lsb-first format
+
+	case DAT_SSTRING:
+	case DAT_DSTRING:
+	case DAT_LIST:
+		return osrp2(_ptr);
+
+	case DAT_NIL:
+	case DAT_TRUE:
+		return 0;
+
+	case DAT_PROPNUM:
+	case DAT_SYN:
+	case DAT_FNADDR:
+	case DAT_REDIR:
+		return 2;
+
+	case DAT_TPL:
+		// template is counted array of 10-byte entries, plus length byte */
+		return 1 + ((*(uchar *)_ptr) * VOCTPLSIZ);
+
+	case DAT_TPL2:
+		return 1 + ((*(uchar *)_ptr) * VOCTPL2SIZ);
+
+	default:
+		return 0;
+	}
+}
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // Engine of namespace GLK
diff --git a/engines/glk/tads/tads2/data.h b/engines/glk/tads/tads2/data.h
new file mode 100644
index 0000000..b2d8df6
--- /dev/null
+++ b/engines/glk/tads/tads2/data.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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_TADS_TADS2_DATA
+#define GLK_TADS_TADS2_DATA
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+enum DataType {
+	DAT_NUMBER  =  1,
+	DAT_OBJECT  =  2,
+	DAT_SSTRING =  3,
+	DAT_BASEPTR =  4,
+	DAT_NIL     =  5,		///< nil, as in FALSE or empty list
+	DAT_CODE    =  6,
+	DAT_LIST    =  7,
+	DAT_TRUE    =  8,		///< inverse of nil
+	DAT_DSTRING =  9,
+	DAT_FNADDR  = 10,		///< a function address
+	DAT_TPL     = 11,		///< template list pointer
+	DAT_PROPNUM = 13,		///< a property number
+	DAT_DEMAND  = 14,		///< special flag: use callback to set on use
+	DAT_SYN     = 15,		///< synonym to indicated property value
+	DAT_REDIR   = 16,		///< redirection to different object
+	DAT_TPL2    = 17		///< new-style template
+};
+
+class Data {
+private:
+	DataType _type;
+	void *_ptr;
+public:
+	/**
+	 * Constructor
+	 */
+	Data(DataType type);
+
+	/**
+	 * Return the size of the data
+	 */
+	size_t size() const;
+};
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // Engine of namespace Glk
+
+#endif
diff --git a/engines/glk/tads/tads2/types.h b/engines/glk/tads/tads2/types.h
index 52a5309..5da759f 100644
--- a/engines/glk/tads/tads2/types.h
+++ b/engines/glk/tads/tads2/types.h
@@ -23,6 +23,8 @@
 #ifndef GLK_TADS_TADS2_TYPES
 #define GLK_TADS_TADS2_TYPES
 
+#include "common/stream.h"
+
 namespace Glk {
 namespace TADS {
 namespace TADS2 {
diff --git a/engines/glk/tads/tads2/vocabulary.cpp b/engines/glk/tads/tads2/vocabulary.cpp
new file mode 100644
index 0000000..4647b1b
--- /dev/null
+++ b/engines/glk/tads/tads2/vocabulary.cpp
@@ -0,0 +1,33 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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/tads/tads2/vocabulary.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+// TODO: Rest of vocabulary stuff
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
diff --git a/engines/glk/tads/tads2/vocabulary.h b/engines/glk/tads/tads2/vocabulary.h
new file mode 100644
index 0000000..b407993
--- /dev/null
+++ b/engines/glk/tads/tads2/vocabulary.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * Defines TADS vocabulary (player command parser) functionality
+ */
+
+#ifndef GLK_TADS_TADS2_OS
+#define GLK_TADS_TADS2_OS
+
+#include "glk/tads/tads2/types.h"
+
+namespace Glk {
+namespace TADS {
+namespace TADS2 {
+
+// TODO: Rest of vocabulary stuff
+#define VOCTPLSIZ 10
+#define VOCTPL2SIZ  16
+
+} // End of namespace TADS2
+} // End of namespace TADS
+} // End of namespace Glk
+
+#endif


Commit: c68542dba8ecf7a9ad061c015061832eaa4bb016
    https://github.com/scummvm/scummvm/commit/c68542dba8ecf7a9ad061c015061832eaa4bb016
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Compilation fix

Changed paths:
    engines/glk/tads/tads2/data.h


diff --git a/engines/glk/tads/tads2/data.h b/engines/glk/tads/tads2/data.h
index b2d8df6..2276d37 100644
--- a/engines/glk/tads/tads2/data.h
+++ b/engines/glk/tads/tads2/data.h
@@ -23,6 +23,8 @@
 #ifndef GLK_TADS_TADS2_DATA
 #define GLK_TADS_TADS2_DATA
 
+#include "common/scummsys.h"
+
 namespace Glk {
 namespace TADS {
 namespace TADS2 {


Commit: 7212924b1ead017b4c769ca4fb8cdad6bf95e78e
    https://github.com/scummvm/scummvm/commit/7212924b1ead017b4c769ca4fb8cdad6bf95e78e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: TADS: Compilation fixes

Changed paths:
    engines/glk/picture.cpp
    engines/glk/tads/tads2/ler.cpp
    engines/glk/tads/tads2/os.cpp
    engines/glk/tads/tads2/regex.cpp


diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 4422e46..631f9be 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -146,7 +146,8 @@ Picture *Pictures::load(uint32 id) {
 			pal[idx] = pic->format.RGBToColor(palette[idx * 3],
 				palette[idx * 3 + 1], palette[idx * 3 + 2]);
 		
-		byte *srcP = (byte *)img->getPixels(), *destP = (byte *)pic->getPixels();
+		const byte *srcP = (const byte *)img->getPixels();
+		byte *destP = (byte *)pic->getPixels();
 		for (int idx = 0; idx < img->w * img->h; ++idx, srcP++, destP += pic->format.bytesPerPixel) {
 			uint val = (*srcP >= palCount) ? 0 : pal[*srcP];
 			if (pic->format.bytesPerPixel == 2)
diff --git a/engines/glk/tads/tads2/ler.cpp b/engines/glk/tads/tads2/ler.cpp
index 930fdf0..970fe31 100644
--- a/engines/glk/tads/tads2/ler.cpp
+++ b/engines/glk/tads/tads2/ler.cpp
@@ -33,7 +33,7 @@ int errcxdef::errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *ar
     int    argi   = 0;
     int    len;
     char   buf[20];
-    char  *p = nullptr;
+    const char  *p = nullptr;
     char   fmtchar;
 
     while (*fmt != '\0' && outbufl > 1) {
diff --git a/engines/glk/tads/tads2/os.cpp b/engines/glk/tads/tads2/os.cpp
index 514a912..17bdc34 100644
--- a/engines/glk/tads/tads2/os.cpp
+++ b/engines/glk/tads/tads2/os.cpp
@@ -51,6 +51,7 @@ glui32 OS::oss_convert_file_type(int type) {
 }
 
 glui32 OS::oss_convert_fileref_to_string(frefid_t file_to_convert, char *buffer, int buf_len) {
+#ifdef TODO
 	char   temp_string[32];
 	glui32 value, i = 0, digit,
 		digit_flag = false,     // Have we put a digit in the string yet?
@@ -74,10 +75,12 @@ glui32 OS::oss_convert_fileref_to_string(frefid_t file_to_convert, char *buffer,
 		return false;
 	sprintf(buffer, "%s%s%s", OSS_FILEREF_STRING_PREFIX,
 		temp_string, OSS_FILEREF_STRING_SUFFIX);
+#endif
 	return true;
 }
 
 frefid_t OS::oss_convert_string_to_fileref(char *buffer, glui32 usage) {
+#ifdef TODO
 	char temp_string[32];
 	glui32 value = 0, i, multiplier = 1;
 
@@ -97,6 +100,9 @@ frefid_t OS::oss_convert_string_to_fileref(char *buffer, glui32 usage) {
 
 	// If not, return the new fileref
 	return (glk_fileref_create_by_name(usage, os_get_root_name(buffer), 0));
+#else
+	return nullptr;
+#endif
 }
 
 bool OS::oss_is_string_a_fileref(char *buffer) {
diff --git a/engines/glk/tads/tads2/regex.cpp b/engines/glk/tads/tads2/regex.cpp
index f6a009a..d5eee3a 100644
--- a/engines/glk/tads/tads2/regex.cpp
+++ b/engines/glk/tads/tads2/regex.cpp
@@ -1069,19 +1069,18 @@ int re_context::match(const char *entire_str, const char *str, size_t origlen,
 
 			case RE_RANGE:
 			case RE_RANGE_EXCL: {
-				int match;
+				int match_val;
 
 				// make sure we have a character to match
 				if (curlen == 0)
 					return -1;
 
 				// see if we match
-				match = re_is_bit_set(tuple->char_range,
-										(int)(unsigned char)*p);
+				match_val = re_is_bit_set(tuple->char_range, (int)(unsigned char)*p);
 					
 				// make sure we got what we wanted
-				if ((tuple->ch == RE_RANGE && !match)
-					|| (tuple->ch == RE_RANGE_EXCL && match))
+				if ((tuple->ch == RE_RANGE && !match_val)
+					|| (tuple->ch == RE_RANGE_EXCL && match_val))
 					return -1;
 
 				// skip this character of the input


Commit: 1933130dfcc417c96e0dfd5eadee68833daf0821
    https://github.com/scummvm/scummvm/commit/1933130dfcc417c96e0dfd5eadee68833daf0821
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix setting cwin field when window changes

Changed paths:
    engines/glk/frotz/processor_screen.cpp


diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index 6a518cf..6627bf6 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -396,9 +396,9 @@ void Processor::z_set_text_style() {
 }
 
 void Processor::z_set_window() {
-	int win = zargs[0];
+	cwin = zargs[0];
 
-	if (win == 0) {
+	if (cwin == 0) {
 		glk_set_window(gos_lower);
 		gos_curwin = gos_lower;
 	} else {
@@ -407,7 +407,7 @@ void Processor::z_set_window() {
 		gos_curwin = gos_upper;
 	}
 
-	if (win == 0)
+	if (cwin == 0)
 		enable_scripting = true;
 	else
 		enable_scripting = false;


Commit: 63079834fa7fd7f4592b0a52e709d01b431740e2
    https://github.com/scummvm/scummvm/commit/63079834fa7fd7f4592b0a52e709d01b431740e2
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Improved rendering of Beyond Zork description area

Changed paths:
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/processor_screen.cpp
    engines/glk/window_text_grid.h
    engines/glk/windows.h


diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index c2d6ca2..7367375 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -203,6 +203,8 @@ bool GlkInterface::initPictures() {
 }
 
 int GlkInterface::os_char_width(zchar z) {
+	// Note: I'm presuming this is 1 because Glk Text Grid windows take care of font sizes internally,
+	// so we can pretend that any font has a 1x1 size
 	return 1;
 }
 
diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index 6627bf6..2ec0128 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -181,8 +181,9 @@ void Processor::z_get_cursor() {
 
 void Processor::z_print_table() {
 	zword addr = zargs[0];
-	zword x;
+	zword xs = curx;
 	int i, j;
+	zbyte c;
 
 	// Supply default arguments
 	if (zargc < 3)
@@ -191,18 +192,10 @@ void Processor::z_print_table() {
 		zargs[3] = 0;
 
 	// Write text in width x height rectangle
-	x = curx;
-
-	for (i = 0; i < zargs[2]; i++) {
-		if (i != 0) {
-			cury += 1;
-			curx = x;
-		}
+	for (i = 0; i < zargs[2]; i++, curx = xs, cury++) {
+		glk_window_move_cursor(cwin == 0 ? gos_lower : gos_upper, xs - 1, cury - 1);
 
 		for (j = 0; j < zargs[1]; j++) {
-
-			zbyte c;
-
 			LOW_BYTE(addr, c);
 			addr++;
 
@@ -330,17 +323,33 @@ void Processor::z_set_font() {
 }
 
 void Processor::z_set_cursor() {
-	cury = zargs[0];
-	curx = zargs[1];
+	int x = (int16)zargs[1], y = (int16)zargs[0];
+	assert(gos_upper);
 
-	if (gos_upper) {
-		if (cury > mach_status_ht) {
-			mach_status_ht = cury;
-			reset_status_ht();
-		}
+	flush_buffer();
 
-		glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
+	if (y < 0) {
+		// Cursor on/off
+		error("TODO: Turning cursor on/off");
 	}
+
+	if (!x || !y) {
+		Point cursorPos = gos_upper->getCursor();
+		if (!x)
+			x = cursorPos.x;
+		if (!y)
+			y = cursorPos.y;
+	}
+
+	curx = x;
+	cury = y;
+
+	if (cury > mach_status_ht) {
+		mach_status_ht = cury;
+		reset_status_ht();
+	}
+
+	glk_window_move_cursor(gos_upper, curx - 1, cury - 1);
 }
 
 void Processor::z_set_text_style() {
diff --git a/engines/glk/window_text_grid.h b/engines/glk/window_text_grid.h
index 30a0f79..d7830e0 100644
--- a/engines/glk/window_text_grid.h
+++ b/engines/glk/window_text_grid.h
@@ -108,6 +108,11 @@ public:
 	virtual bool unputCharUni(uint32 ch) override;
 
 	/**
+	 * Get the cursor position
+	 */
+	virtual Point getCursor() const override { return Point(_curX, _curY); }
+
+	/**
 	 * Move the cursor
 	 */
 	virtual void moveCursor(const Point &newPos) override;
diff --git a/engines/glk/windows.h b/engines/glk/windows.h
index 27380fa..4b71aa0 100644
--- a/engines/glk/windows.h
+++ b/engines/glk/windows.h
@@ -440,6 +440,11 @@ public:
 	}
 
 	/**
+	 * Get the cursor position
+	 */
+	virtual Point getCursor() const { return Point(); }
+
+	/**
 	 * Move the cursor
 	 */
 	virtual void moveCursor(const Point &newPos);


Commit: d7c015a17e83c5bc0ce6ceb2601e15e14928b872
    https://github.com/scummvm/scummvm/commit/d7c015a17e83c5bc0ce6ceb2601e15e14928b872
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Detect Infocom games using .zip extensions

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


diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index 895dd4e..c869c92 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -46,7 +46,7 @@ PlainGameDescriptor FrotzMetaEngine::findGame(const char *gameId) {
 }
 
 bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
-	const char *const EXTENSIONS[10] = { ".dat", ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb" };
+	const char *const EXTENSIONS[11] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".zblorb", ".dat", ".zip" };
 
 	// Loop through the files of the folder
 	for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
@@ -55,7 +55,7 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 			continue;
 		Common::String filename = file->getName();
 		bool hasExt = false;
-		for (int idx = 0; idx < 10 && !hasExt; ++idx)
+		for (int idx = 0; idx < 11 && !hasExt; ++idx)
 			hasExt = filename.hasSuffixIgnoreCase(EXTENSIONS[idx]);
 		if (!hasExt)
 			continue;
@@ -83,8 +83,8 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 
 		DetectedGame gd;
 		if (!p->_gameId) {
-			// Generic .dat files don't get reported as matches unless they have a known md5
-			if (filename.hasSuffixIgnoreCase(".dat"))
+			// Generic .dat/.zip files don't get reported as matches unless they have a known md5
+			if (filename.hasSuffixIgnoreCase(".dat") || filename.hasSuffixIgnoreCase(".zip"))
 				continue;
 
 			if (gDebugLevel > 0) {


Commit: 4894ce2134b6ec903c9be7dc26d9826ac947e008
    https://github.com/scummvm/scummvm/commit/4894ce2134b6ec903c9be7dc26d9826ac947e008
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Add all the new V6 methods, commented out until I can fix each one

Changed paths:
  A engines/glk/frotz/processor_windows.cpp
    engines/glk/frotz/config.cpp
    engines/glk/frotz/processor.cpp
    engines/glk/frotz/processor.h
    engines/glk/frotz/processor_screen.cpp
    engines/glk/module.mk


diff --git a/engines/glk/frotz/config.cpp b/engines/glk/frotz/config.cpp
index e53cd8c..1d0264b 100644
--- a/engines/glk/frotz/config.cpp
+++ b/engines/glk/frotz/config.cpp
@@ -96,9 +96,6 @@ void Header::loadHeader(Common::SeekableReadStream &f) {
 	if (h_version < V1 || h_version > V8)
 		error("Unknown Z-code version");
 
-	if (h_version == V6)
-		error("Cannot play Z-code version 6");
-
 	if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED))
 		error("Byte swapped story file");
 
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
index 09eb82b..e9f0d87 100644
--- a/engines/glk/frotz/processor.cpp
+++ b/engines/glk/frotz/processor.cpp
@@ -104,10 +104,10 @@ Opcode Processor::ext_opcodes[64] = {
 	&Processor::z_log_shift,
 	&Processor::z_art_shift,
 	&Processor::z_set_font,
-	&Processor::__illegal__,		// glkify - Processor::z_draw_picture,
-	&Processor::__illegal__,		// glkify - Processor::z_picture_data,
-	&Processor::__illegal__,		// glkify - Processor::z_erase_picture,
-	&Processor::__illegal__,		// glkify - Processor::z_set_margins,
+	&Processor::z_draw_picture,
+	&Processor::z_picture_data,
+	&Processor::z_erase_picture,
+	&Processor::z_set_margins,
 	&Processor::z_save_undo,
 	&Processor::z_restore_undo,
 	&Processor::z_print_unicode,
@@ -115,20 +115,20 @@ Opcode Processor::ext_opcodes[64] = {
 	&Processor::z_set_true_colour,	// spec 1.1
 	&Processor::__illegal__,
 	&Processor::__illegal__,
-	&Processor::__illegal__,		// glkify - Processor::z_move_window,
-	&Processor::__illegal__,		// glkify - Processor::z_window_size,
-	&Processor::__illegal__,		// glkify - Processor::z_window_style,
-	&Processor::__illegal__,		// glkify - Processor::z_get_wind_prop,
-	&Processor::__illegal__,		// glkify - Processor::z_scroll_window,
+	&Processor::z_move_window,
+	&Processor::z_window_size,
+	&Processor::z_window_style,
+	&Processor::z_get_wind_prop,
+	&Processor::z_scroll_window,
 	&Processor::z_pop_stack,
-	&Processor::__illegal__,		// glkify - Processor::z_read_mouse,
-	&Processor::__illegal__,		// glkify - Processor::z_mouse_window,
+	&Processor::z_read_mouse,
+	&Processor::z_mouse_window,
 	&Processor::z_push_stack,
-	&Processor::__illegal__,		// glkify - Processor::z_put_wind_prop,
+	&Processor::z_put_wind_prop,
 	&Processor::z_print_form,
 	&Processor::z_make_menu,
-	&Processor::__illegal__,		// glkify - Processor::z_picture_table
-	&Processor::z_buffer_screen,	// spec 1.1
+	&Processor::z_picture_table,
+	&Processor::z_buffer_screen		// spec 1.1
 };
 
 Processor::Processor(OSystem *syst, const GlkGameDescription &gameDesc) : 
diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index fb9c12f..dcf3986 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -1545,6 +1545,116 @@ protected:
 	void z_store();
 
 	/**@}*/
+
+	/**
+	 * \defgroup Window/V6 Opcode methods
+	 * @{
+	 */
+
+	/**
+	 * z_draw_picture, draw a picture.
+	 *
+	 *	zargs[0] = number of picture to draw
+	 *	zargs[1] = y-coordinate of top left corner
+	 *	zargs[2] = x-coordinate of top left corner
+	 */
+	void z_draw_picture();
+
+	/**
+	 * Get information on a picture or the graphics file.
+	 *
+	 *	zargs[0] = number of picture or 0 for the graphics file
+	 *	zargs[1] = address to write information to
+	 */
+	void z_picture_data();
+
+	/**
+	 * Erase a picture with background colour.
+	 *
+	 *	zargs[0] = number of picture to erase
+	 *	zargs[1] = y-coordinate of top left corner (optional)
+	 *	zargs[2] = x-coordinate of top left corner (optional)
+	 */
+	void z_erase_picture();
+
+	/**
+	 * Set the left and right margins of a window.
+	 *
+	 *	zargs[0] = left margin in pixels
+	 *	zargs[1] = right margin in pixels
+	 *	zargs[2] = window (-3 is the current one, optional)
+	 */
+	void z_set_margins();
+
+
+	/**
+	 * Place a window on the screen.
+	 *
+	 *	zargs[0] = window (-3 is the current one)
+	 *	zargs[1] = y-coordinate
+	 *	zargs[2] = x-coordinate
+	 *
+	 */
+	void z_move_window();
+
+	/**
+	 * Change the width and height of a window.
+	 *
+	 *	zargs[0] = window (-3 is the current one)
+	 *	zargs[1] = new height in screen units
+	 *	zargs[2] = new width in screen units
+	 */
+	void z_window_size();
+
+	/**
+	 * Set / clear / toggle window attributes.
+	 *
+	 *	zargs[0] = window (-3 is the current one)
+	 *	zargs[1] = window attribute flags
+	 *	zargs[2] = operation to perform (optional, defaults to 0)
+	 */
+	void z_window_style();
+
+	/**
+	 * Store the value of a window property.
+	 *
+	 *	zargs[0] = window (-3 is the current one)
+	 *	zargs[1] = number of window property to be stored
+	 */
+	void z_get_wind_prop();
+
+	/**
+	 * Set the value of a window property.
+	 *
+	 *	zargs[0] = window (-3 is the current one)
+	 *	zargs[1] = number of window property to set
+	 *	zargs[2] = value to set window property to
+	 */
+	void z_put_wind_prop();
+
+	/**
+	 * Scroll a window up or down.
+	 *
+	 *	zargs[0] = window (-3 is the current one)
+	 *	zargs[1] = #screen units to scroll up (positive) or down (negative)
+	 */
+	void z_scroll_window();
+
+	/**
+	 * Select a window as mouse window.
+	 *
+	 *	zargs[0] = window number (-3 is the current) or -1 for the screen
+	 */
+	void z_mouse_window();
+
+	/**
+	 * Prepare a group of pictures for faster display.
+	 *
+	 *	zargs[0] = address of table holding the picture numbers
+	 */
+	void z_picture_table();
+
+	 /**@}*/
 public:
 	/**
 	 * Constructor
diff --git a/engines/glk/frotz/processor_screen.cpp b/engines/glk/frotz/processor_screen.cpp
index 2ec0128..3adec66 100644
--- a/engines/glk/frotz/processor_screen.cpp
+++ b/engines/glk/frotz/processor_screen.cpp
@@ -127,7 +127,11 @@ void Processor::z_buffer_mode() {
 }
 
 void Processor::z_buffer_screen() {
+#ifdef TODO
+store((zword)os_buffer_screen((zargs[0] == (zword)-1) ? -1 : zargs[0]));
+#else
 	store(0);
+#endif
 }
 
 void Processor::z_erase_line() {
diff --git a/engines/glk/frotz/processor_windows.cpp b/engines/glk/frotz/processor_windows.cpp
new file mode 100644
index 0000000..2f1956f
--- /dev/null
+++ b/engines/glk/frotz/processor_windows.cpp
@@ -0,0 +1,316 @@
+/* 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/frotz/processor.h"
+
+namespace Glk {
+namespace Frotz {
+
+static struct {
+    Story story_id;
+    int pic;
+    int pic1;
+    int pic2;
+} mapper[] = {
+    { ZORK_ZERO,  5, 497, 498 },
+    { ZORK_ZERO,  6, 501, 502 },
+    { ZORK_ZERO,  7, 499, 500 },
+    { ZORK_ZERO,  8, 503, 504 },
+    {    ARTHUR, 54, 170, 171 },
+    {    SHOGUN, 50,  61,  62 },
+    {   UNKNOWN,  0,   0,   0 }
+};
+
+void Processor::z_draw_picture() {
+#ifdef TODO
+    zword pic = zargs[0];
+    zword y = zargs[1];
+    zword x = zargs[2];
+    int i;
+
+    flush_buffer ();
+
+    if (y == 0)			// use cursor line if y-coordinate is 0
+		y = cwp->y_cursor;
+    if (x == 0)    		// use cursor column if x-coordinate is 0 */
+		x = cwp->x_cursor;
+
+    y += cwp->y_pos - 1;
+    x += cwp->x_pos - 1;
+
+    /* The following is necessary to make Amiga and Macintosh story
+       files work with MCGA graphics files.  Some screen-filling
+       pictures of the original Amiga release like the borders of
+       Zork Zero were split into several MCGA pictures (left, right
+       and top borders).  We pretend this has not happened. */
+
+    for (i = 0; mapper[i].story_id != UNKNOWN; i++) {
+		if (_storyId == mapper[i].story_id && pic == mapper[i].pic) {
+			int height1, width1;
+			int height2, width2;
+
+			int delta = 0;
+
+			os_picture_data (pic, &height1, &width1);
+			os_picture_data (mapper[i].pic2, &height2, &width2);
+
+			if (_storyId == ARTHUR && pic == 54)
+			delta = h_screen_width / 160;
+
+			os_draw_picture(mapper[i].pic1, gos_lower, Point(x + delta, y + height1));
+			os_draw_picture(mapper[i].pic2, gos_lower, Point(x + width1 - width2 - delta, y + height1));
+		}
+	}
+
+    os_draw_picture(pic, gos_lower, Point(x, y));
+
+    if (_storyId == SHOGUN && pic == 3) {
+	    glui32 height, width;
+
+	    os_picture_data(59, &height, &width);
+	    os_draw_picture(59, gos_lower, Point(h_screen_width - width + 1, y));
+	}
+#endif
+}
+
+void Processor::z_picture_data() {
+#ifdef TODO
+	zword pic = zargs[0];
+	zword table = zargs[1];
+
+	int height, width;
+	int i;
+
+	bool avail = os_picture_data(pic, &height, &width);
+
+	for (i = 0; mapper[i].story_id != UNKNOWN; i++)
+
+		if (story_id == mapper[i].story_id) {
+
+			if (pic == mapper[i].pic) {
+
+				int height2, width2;
+
+				avail &= os_picture_data(mapper[i].pic1, &height2, &width2);
+				avail &= os_picture_data(mapper[i].pic2, &height2, &width2);
+
+				height += height2;
+
+			}
+			else if (pic == mapper[i].pic1 || pic == mapper[i].pic2)
+
+				avail = FALSE;
+		}
+
+	storew((zword)(table + 0), (zword)(height));
+	storew((zword)(table + 2), (zword)(width));
+
+	branch(avail);
+#endif
+}
+
+void Processor::z_erase_picture() {
+#ifdef TODO
+	int height, width;
+
+	zword y = zargs[1];
+	zword x = zargs[2];
+
+	flush_buffer();
+
+	/* Do nothing if the background is transparent */
+
+	if (hi(cwp->colour) == TRANSPARENT_COLOUR)
+		return;
+
+	if (y == 0)		/* use cursor line if y-coordinate is 0 */
+		y = cwp->y_cursor;
+	if (x == 0)    	/* use cursor column if x-coordinate is 0 */
+		x = cwp->x_cursor;
+
+	os_picture_data(zargs[0], &height, &width);
+
+	y += cwp->y_pos - 1;
+	x += cwp->x_pos - 1;
+
+	os_erase_area(y, x, y + height - 1, x + width - 1, -1);
+#endif
+}
+
+void Processor::z_set_margins() {
+#ifdef TODO
+	zword win = winarg2();
+
+	flush_buffer();
+
+	wp[win].left = zargs[0];
+	wp[win].right = zargs[1];
+
+	/* Protect the margins */
+
+	if (wp[win].x_cursor <= zargs[0] || wp[win].x_cursor > wp[win].x_size - zargs[1]) {
+
+		wp[win].x_cursor = zargs[0] + 1;
+
+		if (win == cwin)
+			update_cursor();
+
+	}
+#endif
+}
+
+void Processor::z_move_window(void) {
+#ifdef TODO
+	zword win = winarg0();
+
+	flush_buffer();
+
+	wp[win].y_pos = zargs[1];
+	wp[win].x_pos = zargs[2];
+
+	if (win == cwin)
+		update_cursor();
+#endif
+}
+
+void Processor::z_window_size() {
+#ifdef TODO
+	zword win = winarg0();
+
+	flush_buffer();
+
+	wp[win].y_size = zargs[1];
+	wp[win].x_size = zargs[2];
+
+	/* Keep the cursor within the window */
+
+	if (wp[win].y_cursor > zargs[1] || wp[win].x_cursor > zargs[2])
+		reset_cursor(win);
+
+	os_window_height(win, wp[win].y_size);
+#endif
+}
+
+void Processor::z_window_style() {
+#ifdef TODO
+	zword win = winarg0();
+	zword flags = zargs[1];
+
+	flush_buffer();
+
+	/* Supply default arguments */
+
+	if (zargc < 3)
+		zargs[2] = 0;
+
+	/* Set window style */
+
+	switch (zargs[2]) {
+	case 0: wp[win].attribute = flags; break;
+	case 1: wp[win].attribute |= flags; break;
+	case 2: wp[win].attribute &= ~flags; break;
+	case 3: wp[win].attribute ^= flags; break;
+	}
+
+	if (cwin == win)
+		update_attributes();
+#endif
+}
+
+void Processor::z_get_wind_prop() {
+#ifdef TODO
+	flush_buffer();
+
+	if (zargs[1] < 16)
+		store(((zword *)(wp + winarg0()))[zargs[1]]);
+
+	else if (zargs[1] == 16)
+		store(os_to_true_colour(lo(wp[winarg0()].colour)));
+
+	else if (zargs[1] == 17) {
+
+		zword bg = hi(wp[winarg0()].colour);
+
+		if (bg == TRANSPARENT_COLOUR)
+			store((zword)-4);
+		else
+			store(os_to_true_colour(bg));
+
+	}
+	else
+		runtime_error(ERR_ILL_WIN_PROP);
+#endif
+}
+
+void Processor::z_put_wind_prop() {
+#ifdef TODO
+	flush_buffer();
+
+	if (zargs[1] >= 16)
+		runtime_error(ERR_ILL_WIN_PROP);
+
+	((zword *)(wp + winarg0()))[zargs[1]] = zargs[2];
+#endif
+}
+
+void Processor::z_scroll_window() {
+#ifdef TODO
+	zword win = winarg0();
+	zword y, x;
+
+	flush_buffer();
+
+	/* Use the correct set of colours when scrolling the window */
+
+	if (win != cwin && !amiga_screen_model())
+		os_set_colour(lo(wp[win].colour), hi(wp[win].colour));
+
+	y = wp[win].y_pos;
+	x = wp[win].x_pos;
+
+	os_scroll_area(y,
+		x,
+		y + wp[win].y_size - 1,
+		x + wp[win].x_size - 1,
+		(short)zargs[1]);
+
+	if (win != cwin && !amiga_screen_model())
+		os_set_colour(lo(cwp->colour), hi(cwp->colour));
+#endif
+}
+
+void Processor::z_mouse_window() {
+#ifdef TODO
+	mwin = ((short)zargs[0] == -1) ? -1 : winarg0();
+#endif
+}
+
+void Processor::z_picture_table() {
+	/* This opcode is used by Shogun and Zork Zero when the player
+	 * encounters built-in games such as Peggleboz. Nowadays it is
+	 * not very helpful to hold the picture data in memory because
+	 * even a small disk cache avoids re-loading of data.
+	 */
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 003f8dc..dd0c2a4 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -39,6 +39,7 @@ MODULE_OBJS := \
 	frotz/processor_table.o \
 	frotz/processor_text.o \
 	frotz/processor_variables.o \
+	frotz/processor_windows.o \
 	frotz/quetzal.o \
 	frotz/screen.o \
 	scott/detection.o \


Commit: 7fda941302358b55d05005837e7719d33bf6ede7
    https://github.com/scummvm/scummvm/commit/7fda941302358b55d05005837e7719d33bf6ede7
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Implement some of the V6 opcodes

Changed paths:
    engines/glk/frotz/processor_windows.cpp


diff --git a/engines/glk/frotz/processor_windows.cpp b/engines/glk/frotz/processor_windows.cpp
index 2f1956f..34c3be1 100644
--- a/engines/glk/frotz/processor_windows.cpp
+++ b/engines/glk/frotz/processor_windows.cpp
@@ -41,37 +41,43 @@ static struct {
 };
 
 void Processor::z_draw_picture() {
-#ifdef TODO
-    zword pic = zargs[0];
-    zword y = zargs[1];
-    zword x = zargs[2];
-    int i;
-
-    flush_buffer ();
-
-    if (y == 0)			// use cursor line if y-coordinate is 0
-		y = cwp->y_cursor;
-    if (x == 0)    		// use cursor column if x-coordinate is 0 */
-		x = cwp->x_cursor;
+	zword pic = zargs[0];
+	zword y = zargs[1];
+	zword x = zargs[2];
+	int i;
 
-    y += cwp->y_pos - 1;
-    x += cwp->x_pos - 1;
+	flush_buffer ();
+
+	if (!x || !y) {
+		// Currently I only support getting the cursor for the text grid area
+		assert(cwin == 1);
+		Point cursPos = gos_upper->getCursor();
+		// use cursor column if x-coordinate is 0
+		if (!x)
+			x = cursPos.x;
+		// use cursor line if y-coordinate is 0
+		if (!y)
+			y = cursPos.y;
+	}
 
-    /* The following is necessary to make Amiga and Macintosh story
-       files work with MCGA graphics files.  Some screen-filling
-       pictures of the original Amiga release like the borders of
-       Zork Zero were split into several MCGA pictures (left, right
-       and top borders).  We pretend this has not happened. */
+//	y += cwp->y_pos - 1;
+//	x += cwp->x_pos - 1;
 
-    for (i = 0; mapper[i].story_id != UNKNOWN; i++) {
+	/* The following is necessary to make Amiga and Macintosh story
+	 * files work with MCGA graphics files.  Some screen-filling
+	 *  pictures of the original Amiga release like the borders of
+	 *  Zork Zero were split into several MCGA pictures (left, right
+	 *  and top borders).  We pretend this has not happened.
+	 */
+	for (i = 0; mapper[i].story_id != UNKNOWN; i++) {
 		if (_storyId == mapper[i].story_id && pic == mapper[i].pic) {
-			int height1, width1;
-			int height2, width2;
+			glui32 height1, width1;
+			glui32 height2, width2;
 
 			int delta = 0;
 
-			os_picture_data (pic, &height1, &width1);
-			os_picture_data (mapper[i].pic2, &height2, &width2);
+			os_picture_data(pic, &height1, &width1);
+			os_picture_data(mapper[i].pic2, &height2, &width2);
 
 			if (_storyId == ARTHUR && pic == 54)
 			delta = h_screen_width / 160;
@@ -81,51 +87,43 @@ void Processor::z_draw_picture() {
 		}
 	}
 
-    os_draw_picture(pic, gos_lower, Point(x, y));
+	os_draw_picture(pic, gos_lower, Point(x, y));
 
-    if (_storyId == SHOGUN && pic == 3) {
-	    glui32 height, width;
+	if (_storyId == SHOGUN && pic == 3) {
+		glui32 height, width;
 
-	    os_picture_data(59, &height, &width);
-	    os_draw_picture(59, gos_lower, Point(h_screen_width - width + 1, y));
+		os_picture_data(59, &height, &width);
+		os_draw_picture(59, gos_lower, Point(h_screen_width - width + 1, y));
 	}
-#endif
 }
 
 void Processor::z_picture_data() {
-#ifdef TODO
 	zword pic = zargs[0];
 	zword table = zargs[1];
-
-	int height, width;
+	glui32 height, width;
 	int i;
 
 	bool avail = os_picture_data(pic, &height, &width);
 
-	for (i = 0; mapper[i].story_id != UNKNOWN; i++)
-
-		if (story_id == mapper[i].story_id) {
-
+	for (i = 0; mapper[i].story_id != UNKNOWN; i++) {
+		if (_storyId == mapper[i].story_id) {
 			if (pic == mapper[i].pic) {
-
-				int height2, width2;
+				glui32 height2, width2;
 
 				avail &= os_picture_data(mapper[i].pic1, &height2, &width2);
 				avail &= os_picture_data(mapper[i].pic2, &height2, &width2);
 
 				height += height2;
-
+			} else if (pic == mapper[i].pic1 || pic == mapper[i].pic2) {
+				avail = false;
 			}
-			else if (pic == mapper[i].pic1 || pic == mapper[i].pic2)
-
-				avail = FALSE;
 		}
+	}
 
 	storew((zword)(table + 0), (zword)(height));
 	storew((zword)(table + 2), (zword)(width));
 
 	branch(avail);
-#endif
 }
 
 void Processor::z_erase_picture() {
@@ -299,9 +297,8 @@ void Processor::z_scroll_window() {
 }
 
 void Processor::z_mouse_window() {
-#ifdef TODO
-	mwin = ((short)zargs[0] == -1) ? -1 : winarg0();
-#endif
+	// No implementation - since ScummVM can run as a window, it's better
+	// not to constrain the area the mouse can move
 }
 
 void Processor::z_picture_table() {


Commit: ab8ff73453005f55acdf13ebbc39cb3acb7aa74a
    https://github.com/scummvm/scummvm/commit/ab8ff73453005f55acdf13ebbc39cb3acb7aa74a
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Fix handling of empty rect picture resources

Changed paths:
    engines/glk/frotz/pics.cpp
    engines/glk/picture.cpp


diff --git a/engines/glk/frotz/pics.cpp b/engines/glk/frotz/pics.cpp
index 4953f04..2d05fc9 100644
--- a/engines/glk/frotz/pics.cpp
+++ b/engines/glk/frotz/pics.cpp
@@ -24,6 +24,7 @@
 #include "glk/frotz/pics_decoder.h"
 #include "glk/glk.h"
 #include "common/algorithm.h"
+#include "common/memstream.h"
 
 namespace Glk {
 namespace Frotz {
@@ -68,7 +69,10 @@ Pics::Pics() : Common::Archive(), _filename(getFilename()) {
 			}
 		}
 
-		e._filename = Common::String::format("pic%u.raw", e._number);
+		if (e._dataOffset)
+			e._filename = Common::String::format("pic%u.raw", e._number);
+		else
+			e._filename = Common::String::format("pic%u.rect", e._number);
 	}
 
 	// Further processing of index to calculate data sizes
@@ -128,6 +132,8 @@ const Common::ArchiveMemberPtr Pics::getMember(const Common::String &name) const
 }
 
 Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::String &name) const {
+	PictureDecoder decoder;
+
 	for (uint idx = 0; idx < _index.size(); ++idx) {
 		const Entry &e = _index[idx];
 		if (e._filename.equalsIgnoreCase(name)) {
@@ -137,19 +143,21 @@ Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::String
 			if (!f.open(_filename))
 				error("Reading failed");
 
-			// Read in the image's palette
-			assert(e._paletteOffset);
-			f.seek(e._paletteOffset);
-			palette.resize(f.readByte() * 3);
-			f.read(&palette[0], palette.size());
-
-			PictureDecoder decoder;
 			if (e._dataSize) {
+				// Read in the image's palette
+				assert(e._paletteOffset);
+				f.seek(e._paletteOffset);
+				palette.resize(f.readByte() * 3);
+				f.read(&palette[0], palette.size());
+
 				Common::SeekableReadStream *src = f.readStream(e._dataSize);
 				dest = decoder.decode(*src, e._flags, palette, MCGA, e._width, e._height);
 				delete src;
 			} else {
-				error("TODO: Empty rect renderings");
+				byte *rect = (byte *)malloc(2 * sizeof(uint16));
+				WRITE_LE_UINT16(rect, e._width);
+				WRITE_LE_UINT16(rect + 2, e._height);
+				dest = new Common::MemoryReadStream(rect, 2 * sizeof(uint16), DisposeAfterUse::YES);
 			}
 
 			f.close();
diff --git a/engines/glk/picture.cpp b/engines/glk/picture.cpp
index 631f9be..c7c8f70 100644
--- a/engines/glk/picture.cpp
+++ b/engines/glk/picture.cpp
@@ -103,6 +103,7 @@ Picture *Pictures::retrieve(uint id, bool scaled) {
 Picture *Pictures::load(uint32 id) {
 	::Image::PNGDecoder png;
 	::Image::JPEGDecoder jpg;
+	Graphics::Surface rectImg;
 	RawDecoder raw;
 	const Graphics::Surface *img;
 	const byte *palette = nullptr;
@@ -128,6 +129,10 @@ Picture *Pictures::load(uint32 id) {
 		img = raw.getSurface();
 		palette = raw.getPalette();
 		palCount = raw.getPaletteColorCount();
+	} else if (f.open(Common::String::format("pic%u.rect", id))) {
+		rectImg.w = f.readUint16LE();
+		rectImg.h = f.readUint16LE();
+		img = &rectImg;
 	} else {
 		// No such picture
 		return nullptr;
@@ -138,7 +143,9 @@ Picture *Pictures::load(uint32 id) {
     pic->_id = id;
     pic->_scaled = false;
 
-	if (!palette) {
+	if (!img->getPixels()) {
+		// Area definition without any content
+	} else if (!palette) {
 		pic->blitFrom(*img);
 	} else {
 		uint pal[256];


Commit: 88b47cdd8bd20e54fcfdf5e98c24c8e7d0c97415
    https://github.com/scummvm/scummvm/commit/88b47cdd8bd20e54fcfdf5e98c24c8e7d0c97415
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Added winarg methods

Changed paths:
    engines/glk/frotz/processor.h
    engines/glk/frotz/processor_windows.cpp


diff --git a/engines/glk/frotz/processor.h b/engines/glk/frotz/processor.h
index dcf3986..5d6d448 100644
--- a/engines/glk/frotz/processor.h
+++ b/engines/glk/frotz/processor.h
@@ -549,7 +549,6 @@ protected:
 
 	/**
 	 * Map a Unicode character onto the ZSCII alphabet.
-	 *
 	 */
 	zbyte translate_to_zscii(zchar c);
 
@@ -668,6 +667,26 @@ protected:
 	zchar unicode_tolower(zchar c);
 
 	/**@}*/
+
+	/**
+	 * \defgroup Window/V6 Opcode methods
+	 * @{
+	 */
+
+	/**
+	 * Return the window number in zargs[0]. In V6 only, -3 refers to the
+	 * current window.
+	 */
+	zword winarg0();
+
+	/**
+	 * Return the (optional) window number in zargs[2]. -3 refers to the
+	 * current window. This optional window number was only used by some
+	 * V6 opcodes: set_cursor, set_margins, set_colour.
+	 */
+	zword winarg2();
+
+	 /**@}*/
 protected:
 	/**
 	 * \defgroup General Opcode methods
diff --git a/engines/glk/frotz/processor_windows.cpp b/engines/glk/frotz/processor_windows.cpp
index 34c3be1..5e24ac8 100644
--- a/engines/glk/frotz/processor_windows.cpp
+++ b/engines/glk/frotz/processor_windows.cpp
@@ -255,7 +255,7 @@ void Processor::z_get_wind_prop() {
 
 	}
 	else
-		runtime_error(ERR_ILL_WIN_PROP);
+		runtimeError(ERR_ILL_WIN_PROP);
 #endif
 }
 
@@ -264,7 +264,7 @@ void Processor::z_put_wind_prop() {
 	flush_buffer();
 
 	if (zargs[1] >= 16)
-		runtime_error(ERR_ILL_WIN_PROP);
+		runtimeError(ERR_ILL_WIN_PROP);
 
 	((zword *)(wp + winarg0()))[zargs[1]] = zargs[2];
 #endif
@@ -309,5 +309,25 @@ void Processor::z_picture_table() {
 	 */
 }
 
+zword Processor::winarg0() {
+	if (h_version == V6 && (short)zargs[0] == -3)
+		return cwin;
+
+	if (zargs[0] >= ((h_version == V6) ? 8 : 2))
+		runtimeError(ERR_ILL_WIN);
+
+	return zargs[0];
+}
+
+zword Processor::winarg2() {
+	if (zargc < 3 || (short)zargs[2] == -3)
+		return cwin;
+
+	if (zargs[2] >= 8)
+		runtimeError(ERR_ILL_WIN);
+
+	return zargs[2];
+}
+
 } // End of namespace Scott
 } // End of namespace Glk


Commit: 2ae0edd58ba0258b4f3278e334468b12a0b73573
    https://github.com/scummvm/scummvm/commit/2ae0edd58ba0258b4f3278e334468b12a0b73573
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Add sound playing code

Changed paths:
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/processor.cpp


diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index 7367375..f800f73 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -279,11 +279,27 @@ bool GlkInterface::os_picture_data(int picture, glui32 *height, glui32 *width) {
 }
 
 void GlkInterface::start_sample(int number, int volume, int repeats, zword eos) {
-	// TODO
+	static zbyte LURKING_REPEATS[] = {
+		0x00, 0x00, 0x00, 0x01, 0xff,
+		0x00, 0x01, 0x01, 0x01, 0x01,
+		0xff, 0x01, 0x01, 0xff, 0x00,
+		0xff, 0xff, 0xff, 0xff, 0xff
+	};
+
+	if (_storyId == LURKING_HORROR)
+		repeats = LURKING_REPEATS[number];
+
+	os_start_sample(number, volume, repeats, eos);
+
+	_soundPlaying = true;
 }
 
 void GlkInterface::start_next_sample() {
-	// TODO
+	if (next_sample != 0)
+		start_sample(next_sample, next_volume, 0, 0);
+
+	next_sample = 0;
+	next_volume = 0;
 }
 
 void GlkInterface::gos_update_width() {
diff --git a/engines/glk/frotz/processor.cpp b/engines/glk/frotz/processor.cpp
index e9f0d87..8cfff9c 100644
--- a/engines/glk/frotz/processor.cpp
+++ b/engines/glk/frotz/processor.cpp
@@ -628,12 +628,11 @@ void Processor::z_sound_effect() {
 		_soundPlaying = false;
 
 		switch (effect) {
-
 		case EFFECT_PREPARE:
-			os_prepare_sample (number);
+			os_prepare_sample(number);
 			break;
 		case EFFECT_PLAY:
-			start_sample(number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0);
+			start_sample(number, lo(volume), hi(volume), (zargc == 4) ? zargs[3] : 0);
 			break;
 		case EFFECT_STOP:
 			os_stop_sample (number);


Commit: f6abb3ea33c7b073066732f794a9d071c38a044d
    https://github.com/scummvm/scummvm/commit/f6abb3ea33c7b073066732f794a9d071c38a044d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Simplify accessing sounds for Lurking Horror & Sherlock

Changed paths:
  A engines/glk/frotz/sound_folder.cpp
  A engines/glk/frotz/sound_folder.h
    engines/glk/frotz/glk_interface.cpp
    engines/glk/frotz/glk_interface.h
    engines/glk/module.mk


diff --git a/engines/glk/frotz/glk_interface.cpp b/engines/glk/frotz/glk_interface.cpp
index f800f73..79907cb 100644
--- a/engines/glk/frotz/glk_interface.cpp
+++ b/engines/glk/frotz/glk_interface.cpp
@@ -22,8 +22,11 @@
 
 #include "glk/frotz/glk_interface.h"
 #include "glk/frotz/pics.h"
+#include "glk/frotz/sound_folder.h"
 #include "glk/conf.h"
 #include "glk/screen.h"
+#include "common/config-manager.h"
+#include "common/unzip.h"
 
 namespace Glk {
 namespace Frotz {
@@ -52,49 +55,49 @@ void GlkInterface::initialize() {
 	 * Init glk stuff
 	 */
 
-	// monor
-	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Weight, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Oblique, 0);
+	 // monor
+	glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Weight, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Preformatted, stylehint_Oblique, 0);
 
 	// monob
-	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Weight, 1);
-	glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Oblique, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Weight, 1);
+	glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Oblique, 0);
 
 	// monoi
-	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Weight, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Oblique, 1);
+	glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Weight, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Oblique, 1);
 
 	// monoz
-	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Weight, 1);
-	glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Oblique, 1);
+	glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Weight, 1);
+	glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Oblique, 1);
 
 	// propr
-	glk_stylehint_set(wintype_TextBuffer, style_Normal,       stylehint_Proportional, 1);
-	glk_stylehint_set(wintype_TextGrid,   style_Normal,       stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Normal,       stylehint_Weight, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Normal,       stylehint_Oblique, 0);
+	glk_stylehint_set(wintype_TextBuffer, style_Normal, stylehint_Proportional, 1);
+	glk_stylehint_set(wintype_TextGrid, style_Normal, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Weight, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Oblique, 0);
 
 	// propb
-	glk_stylehint_set(wintype_TextBuffer, style_Header,       stylehint_Proportional, 1);
-	glk_stylehint_set(wintype_TextGrid,   style_Header,       stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Header,       stylehint_Weight, 1);
-	glk_stylehint_set(wintype_AllTypes,   style_Header,       stylehint_Oblique, 0);
+	glk_stylehint_set(wintype_TextBuffer, style_Header, stylehint_Proportional, 1);
+	glk_stylehint_set(wintype_TextGrid, style_Header, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Weight, 1);
+	glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Oblique, 0);
 
 	// propi
-	glk_stylehint_set(wintype_TextBuffer, style_Emphasized,   stylehint_Proportional, 1);
-	glk_stylehint_set(wintype_TextGrid,   style_Emphasized,   stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Emphasized,   stylehint_Weight, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Emphasized,   stylehint_Oblique, 1);
+	glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Proportional, 1);
+	glk_stylehint_set(wintype_TextGrid, style_Emphasized, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Weight, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Oblique, 1);
 
 	// propi
-	glk_stylehint_set(wintype_TextBuffer, style_Note,         stylehint_Proportional, 1);
-	glk_stylehint_set(wintype_TextGrid,   style_Note,         stylehint_Proportional, 0);
-	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Weight, 1);
-	glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Oblique, 1);
+	glk_stylehint_set(wintype_TextBuffer, style_Note, stylehint_Proportional, 1);
+	glk_stylehint_set(wintype_TextGrid, style_Note, stylehint_Proportional, 0);
+	glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Weight, 1);
+	glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Oblique, 1);
 
 	/*
 	 * Get the screen size
@@ -123,7 +126,7 @@ void GlkInterface::initialize() {
 
 	if (h_version >= V4)
 		h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
-			CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
+		CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
 
 	if (h_version >= V5)
 		h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
@@ -134,7 +137,7 @@ void GlkInterface::initialize() {
 	if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
 		h_flags |= OLD_SOUND_FLAG;
 
-	if ((h_version == 6) && (_sound != 0)) 
+	if ((h_version == 6) && (_sound != 0))
 		h_config |= CONFIG_SOUND;
 
 	if (h_version >= V5 && (h_flags & UNDO_FLAG))
@@ -188,6 +191,15 @@ void GlkInterface::initialize() {
 
 	// Set the screen colors
 	garglk_set_zcolors(_defaultForeground, _defaultBackground);
+
+	// Add any sound folder or zip
+	addSound();
+}
+
+void GlkInterface::addSound() {
+	Common::FSNode gameDir(ConfMan.get("path"));
+	SoundSubfolder::check(gameDir);
+	SoundZip::check(gameDir, _storyId);
 }
 
 bool GlkInterface::initPictures() {
diff --git a/engines/glk/frotz/glk_interface.h b/engines/glk/frotz/glk_interface.h
index 4e531ee..ce5da8a 100644
--- a/engines/glk/frotz/glk_interface.h
+++ b/engines/glk/frotz/glk_interface.h
@@ -105,6 +105,11 @@ private:
 	 * Displays the title screen for the game Beyond Zork
 	 */
 	void showBeyondZorkTitle();
+
+	/**
+	 * Add any Sound subfolder or sound zip file for access
+	 */
+	void addSound();
 protected:
 	/**
 	 * Return the length of the character in screen units.
diff --git a/engines/glk/frotz/sound_folder.cpp b/engines/glk/frotz/sound_folder.cpp
new file mode 100644
index 0000000..20f22a8
--- /dev/null
+++ b/engines/glk/frotz/sound_folder.cpp
@@ -0,0 +1,146 @@
+/* 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/frotz/sound_folder.h"
+#include "common/file.h"
+#include "common/unzip.h"
+
+namespace Glk {
+namespace Frotz {
+
+void SoundSubfolder::check(const Common::FSNode &gameDir) {
+	Common::FSNode sound = gameDir.getChild("sound");
+	if (sound.isDirectory())
+		SearchMan.add("sound", new SoundSubfolder(sound));
+}
+
+SoundSubfolder::SoundSubfolder(const Common::FSNode &folder) : _folder(folder) {
+	Common::FSList files;
+	if (folder.getChildren(files, Common::FSNode::kListFilesOnly)) {
+		for (uint idx = 0; idx < files.size(); ++idx) {
+			Common::String filename = files[idx].getName();
+			if (filename.hasSuffixIgnoreCase(".snd")) {
+				int fileNum = atoi(filename.c_str() + filename.size() - 6);
+				Common::String newName = Common::String::format("sound%d.snd", fileNum);
+
+				_filenames[newName] = filename;
+			}
+		}
+	}
+}
+
+bool SoundSubfolder::hasFile(const Common::String &name) const {
+	return _filenames.contains(name);
+}
+
+int SoundSubfolder::listMembers(Common::ArchiveMemberList &list) const {
+	int total = 0;
+	for (Common::StringMap::iterator i = _filenames.begin(); i != _filenames.end(); ++i) {
+		list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember((*i)._key, this)));
+		++total;
+	}
+
+	return total;
+}
+
+const Common::ArchiveMemberPtr SoundSubfolder::getMember(const Common::String &name) const {
+	if (!hasFile(name))
+		return Common::ArchiveMemberPtr();
+
+	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *SoundSubfolder::createReadStreamForMember(const Common::String &name) const {
+	Common::File *f = new Common::File();
+	if (f->open(_folder.getChild(name)))
+		return f;
+
+	delete f;
+	return nullptr;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void SoundZip::check(const Common::FSNode &gameDir, Story story) {
+	if (story != LURKING_HORROR && story != SHERLOCK)
+		return;
+	Common::String zipName = (story == LURKING_HORROR) ? "lhsound.zip" : "shsound.zip";
+	
+	// Check for the existance of the zip
+	Common::FSNode zipNode = gameDir.getChild(zipName);
+	if (!zipNode.exists())
+		return;
+
+	SearchMan.add("sound", Common::makeZipArchive(zipNode));
+}
+
+SoundZip::SoundZip(Common::Archive *zip) : _zip(zip) {
+	Common::ArchiveMemberList files;
+	zip->listMembers(files);
+
+	for (Common::ArchiveMemberList::iterator i = files.begin(); i != files.end(); ++i) {
+		Common::String filename = (*i)->getName();
+		if (filename.hasSuffixIgnoreCase(".snd")) {
+			int fileNum = atoi(filename.c_str() + filename.size() - 6);
+			Common::String newName = Common::String::format("sound%d.snd", fileNum);
+
+			_filenames[newName] = filename;
+		}
+	}
+}
+
+SoundZip::~SoundZip() {
+	delete _zip;
+}
+
+bool SoundZip::hasFile(const Common::String &name) const {
+	return _filenames.contains(name);
+}
+
+int SoundZip::listMembers(Common::ArchiveMemberList &list) const {
+	int total = 0;
+
+	for (Common::StringMap::iterator i = _filenames.begin(); i != _filenames.end(); ++i) {
+		list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember((*i)._key, this)));
+		++total;
+	}
+
+	return total;
+}
+
+const Common::ArchiveMemberPtr SoundZip::getMember(const Common::String &name) const {
+	if (!hasFile(name))
+		return Common::ArchiveMemberPtr();
+
+	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+
+}
+
+Common::SeekableReadStream *SoundZip::createReadStreamForMember(const Common::String &name) const {
+	if (!_filenames.contains(name))
+		return nullptr;
+
+	return _zip->createReadStreamForMember(_filenames[name]);
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/frotz/sound_folder.h b/engines/glk/frotz/sound_folder.h
new file mode 100644
index 0000000..50eef06
--- /dev/null
+++ b/engines/glk/frotz/sound_folder.h
@@ -0,0 +1,138 @@
+/* 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_FROTZ_SOUND_FOLDER_H
+#define GLK_FROTZ_SOUND_FOLDER_H
+
+#include "glk/frotz/frotz_types.h"
+#include "common/archive.h"
+#include "common/fs.h"
+#include "common/hash-str.h"
+
+namespace Glk {
+namespace Frotz {
+
+/**
+ * Acts as an interface to an Infocom sound subfolder for the Lurking Horror or
+ * Sherlock. Any file which ends with a number and '.snd' will be accessible as
+ * 'sound<num>.snd' in the outer Glk layer
+ */
+class SoundSubfolder : public Common::Archive {
+private:
+	Common::FSNode _folder;
+	Common::StringMap _filenames;
+private:
+	/**
+	 * Constructor
+	 */
+	SoundSubfolder(const Common::FSNode &folder);
+public:
+	/**
+	 * Checks for a sound subfolder, and if so, instantiates the class for it
+	 */
+	static void check(const Common::FSNode &gameDir);
+
+	/**
+	 * Check if a member with the given name is present in the Archive.
+	 * Patterns are not allowed, as this is meant to be a quick File::exists()
+	 * replacement.
+	 */
+	virtual bool hasFile(const Common::String &name) const override;
+
+	/**
+	 * Add all members of the Archive to list.
+	 * Must only append to list, and not remove elements from it.
+	 *
+	 * @return the number of names added to list
+	 */
+	virtual int listMembers(Common::ArchiveMemberList &list) const override;
+
+	/**
+	 * Returns a ArchiveMember representation of the given file.
+	 */
+	virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const override;
+
+	/**
+	 * Create a stream bound to a member with the specified name in the
+	 * archive. If no member with this name exists, 0 is returned.
+	 * @return the newly created input stream
+	 */
+	virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override;
+};
+
+/**
+ * Acts as an interface to a Zip file from if-archive for the Lurking Horror or
+ * Sherlock. Any file which ends with a number and '.snd' will be accessible as
+ * 'sound<num>.snd' in the outer Glk layer
+ */
+class SoundZip : public Common::Archive {
+private:
+	Common::Archive *_zip;
+	Common::StringMap _filenames;
+private:
+	/**
+	 * Constructor
+	 */
+	SoundZip(Common::Archive *zip);
+public:
+	/**
+	 * Checks for a sound subfolder, and if so, instantiates the class for it
+	 */
+	static void check(const Common::FSNode &gameDir, Story story);
+
+	/**
+	 * Destructor
+	 */
+	~SoundZip();
+
+	/**
+	 * Check if a member with the given name is present in the Archive.
+	 * Patterns are not allowed, as this is meant to be a quick File::exists()
+	 * replacement.
+	 */
+	virtual bool hasFile(const Common::String &name) const override;
+
+	/**
+	 * Add all members of the Archive to list.
+	 * Must only append to list, and not remove elements from it.
+	 *
+	 * @return the number of names added to list
+	 */
+	virtual int listMembers(Common::ArchiveMemberList &list) const override;
+
+	/**
+	 * Returns a ArchiveMember representation of the given file.
+	 */
+	virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const override;
+
+	/**
+	 * Create a stream bound to a member with the specified name in the
+	 * archive. If no member with this name exists, 0 is returned.
+	 * @return the newly created input stream
+	 */
+	virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const override;
+};
+
+} // End of namespace Frotz
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index dd0c2a4..c09bfac 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -42,6 +42,7 @@ MODULE_OBJS := \
 	frotz/processor_windows.o \
 	frotz/quetzal.o \
 	frotz/screen.o \
+	frotz/sound_folder.o \
 	scott/detection.o \
 	scott/scott.o \
 	tads/detection.o \


Commit: 3661fc61ec319752a767e5981581ded026a57dd1
    https://github.com/scummvm/scummvm/commit/3661fc61ec319752a767e5981581ded026a57dd1
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Beginnings of Sounds manager

Changed paths:
  A engines/glk/sound.cpp
  A engines/glk/sound.h
    engines/glk/blorb.cpp
    engines/glk/glk.cpp
    engines/glk/glk.h
    engines/glk/glk_api.cpp
    engines/glk/glk_api.h
    engines/glk/glk_types.h
    engines/glk/module.mk


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index 038a79e..5ffd0b0 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -146,7 +146,7 @@ Common::ErrorCode Blorb::load() {
 				ce._filename += ".rect";
 
 		} else if (ce._type == ID_Snd) {
-			ce._filename = Common::String::format("snd%u", ce._number);
+			ce._filename = Common::String::format("sound%u", ce._number);
 			if (ce._id == ID_MIDI)
 				ce._filename += ".midi";
 			else if (ce._id == ID_MP3)
@@ -156,7 +156,7 @@ Common::ErrorCode Blorb::load() {
 			else if (ce._id == ID_AIFF)
 				ce._filename += ".aiff";
 			else if (ce._id == ID_OGG)
-				ce._filename += ".ogg";
+				ce._filename += ".ogg"; 
 			else if (ce._id == ID_MOD)
 				ce._filename += ".mod";
 
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index a22f9e9..c8a63c5 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -35,6 +35,7 @@
 #include "glk/picture.h"
 #include "glk/screen.h"
 #include "glk/selection.h"
+#include "glk/sound.h"
 #include "glk/streams.h"
 #include "glk/windows.h"
 
@@ -45,9 +46,9 @@ GlkEngine *g_vm;
 GlkEngine::GlkEngine(OSystem *syst, const GlkGameDescription &gameDesc) :
 		_gameDescription(gameDesc), Engine(syst), _random("Glk"), _blorb(nullptr),
 		_clipboard(nullptr), _conf(nullptr), _events(nullptr), _pictures(nullptr),
-		_screen(nullptr), _selection(nullptr), _windows(nullptr), _copySelect(false),
-		_terminated(false), gli_unregister_obj(nullptr), gli_register_arr(nullptr),
-		gli_unregister_arr(nullptr) {
+		_screen(nullptr), _selection(nullptr), _sounds(nullptr), _windows(nullptr),
+		_copySelect(false), _terminated(false), gli_unregister_obj(nullptr),
+		gli_register_arr(nullptr), gli_unregister_arr(nullptr) {
 	g_vm = this;
 }
 
@@ -59,6 +60,7 @@ GlkEngine::~GlkEngine() {
 	delete _pictures;
 	delete _screen;
 	delete _selection;
+	delete _sounds;
 	delete _streams;
 	delete _windows;
 }
@@ -79,6 +81,7 @@ void GlkEngine::initialize() {
 	_events = new Events();
 	_pictures = new Pictures();
 	_selection = new Selection();
+	_sounds = new Sounds();
 	_streams = new Streams();
 	_windows = new Windows(_screen);
 }
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 923e295..91702c5 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -41,6 +41,7 @@ class Events;
 class Pictures;
 class Screen;
 class Selection;
+class Sounds;
 class Streams;
 class Windows;
 
@@ -107,6 +108,7 @@ public:
 	Screen *_screen;
 	Selection *_selection;
 	Streams *_streams;
+	Sounds *_sounds;
 	Windows *_windows;
 	bool _copySelect;
 	bool _terminated;
diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index 32d6177..1104789 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -24,6 +24,7 @@
 #include "glk/conf.h"
 #include "glk/events.h"
 #include "glk/picture.h"
+#include "glk/sound.h"
 #include "glk/streams.h"
 #include "glk/unicode.h"
 #include "glk/windows.h"
@@ -970,22 +971,19 @@ void GlkAPI::glk_window_set_background_color(winid_t win, glui32 color) {
 }
 
 schanid_t GlkAPI::glk_schannel_create(glui32 rock) {
-	// TODO
-	return nullptr;
+	return _sounds->create(rock);
 }
 
 void GlkAPI::glk_schannel_destroy(schanid_t chan) {
-	// TODO
+	delete chan;
 }
 
 schanid_t GlkAPI::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
-	// TODO
-	return nullptr;
+	return _sounds->iterate(chan, rockptr);
 }
 
 glui32 GlkAPI::glk_schannel_get_rock(schanid_t chan) {
-	// TODO
-	return 0;
+	return chan->_rock;
 }
 
 glui32 GlkAPI::glk_schannel_play(schanid_t chan, glui32 snd) {
diff --git a/engines/glk/glk_api.h b/engines/glk/glk_api.h
index 3081ac2..2dd2501 100644
--- a/engines/glk/glk_api.h
+++ b/engines/glk/glk_api.h
@@ -26,6 +26,7 @@
 #include "glk/glk.h"
 #include "glk/glk_types.h"
 #include "glk/blorb.h"
+#include "glk/sound.h"
 #include "glk/time.h"
 #include "glk/windows.h"
 
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index 81a36d8..abae785 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -68,8 +68,6 @@ enum InterpreterType {
 #define GLK_MODULE_DATETIME
 #define GLK_MODULE_GARGLKTEXT
 
-typedef struct glk_schannel_struct *schanid_t;
-
 /**
  * Usurp C1 space for ligatures and smart typography glyphs
  */
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index c09bfac..de69645 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -11,6 +11,7 @@ MODULE_OBJS := \
 	raw_decoder.o \
 	screen.o \
 	selection.o \
+	sound.o \
 	streams.o \
 	time.o \
 	unicode.o \
diff --git a/engines/glk/sound.cpp b/engines/glk/sound.cpp
new file mode 100644
index 0000000..5c98b93
--- /dev/null
+++ b/engines/glk/sound.cpp
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 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/sound.h"
+
+namespace Glk {
+
+Sounds::~Sounds() {
+	for (int idx = (int)_sounds.size() - 1; idx >= 0; --idx)
+		delete _sounds[idx];
+}
+
+void Sounds::removeSound(schanid_t snd) {
+	for (uint idx = 0; idx < _sounds.size(); ++idx) {
+		if (_sounds[idx] == snd) {
+			_sounds.remove_at(idx);
+			break;
+		}
+	}
+}
+
+schanid_t Sounds::create(glui32 rock) {
+	schanid_t snd = new SoundChannel();
+	_sounds.push_back(snd);
+	return snd;
+}
+
+schanid_t Sounds::iterate(schanid_t chan, glui32 *rockptr) {
+	for (int idx = 0; idx < (int)_sounds.size() - 1; ++idx) {
+		if (_sounds[idx] == chan) {
+			schanid_t next = _sounds[idx + 1];
+			if (*rockptr)
+				*rockptr = next->_rock;
+
+			return next;
+		}
+	}
+
+	return nullptr;
+}
+
+/*--------------------------------------------------------------------------*/
+
+SoundChannel::~SoundChannel() {
+	_owner->removeSound(this);
+	delete _stream;
+}
+
+void SoundChannel::play(uint soundNum) {
+	// TODO
+}
+
+} // End of namespace Glk
diff --git a/engines/glk/sound.h b/engines/glk/sound.h
new file mode 100644
index 0000000..05875ea
--- /dev/null
+++ b/engines/glk/sound.h
@@ -0,0 +1,87 @@
+/* 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_SOUND_H
+#define GLK_SOUND_H
+
+#include "glk/glk_types.h"
+#include "audio/audiostream.h"
+#include "common/array.h"
+
+namespace Glk {
+
+class Sounds;
+
+/**
+ * Holds the data for a playing sound
+ */
+struct SoundChannel {
+	Sounds *_owner;
+	Audio::AudioStream *_stream;
+	glui32 _rock;
+
+	/**
+	 * Destructor
+	 */
+	SoundChannel() : _stream(nullptr), _rock(0) {}
+
+	/**
+	 * Destructor
+	 */
+	~SoundChannel();
+
+	/**
+	 * Play a sound
+	 */
+	void play(uint soundNum);
+};
+typedef SoundChannel *schanid_t;
+
+/**
+ * Sound manager
+ */
+class Sounds {
+	friend struct SoundChannel;
+private:
+	Common::Array<schanid_t> _sounds;
+private:
+	/**
+	 * Remove a sound from the sounds list
+	 */
+	void removeSound(schanid_t snd);
+public:
+	~Sounds();
+
+	/**
+	 * Create a new channel
+	 */
+	schanid_t create(glui32 rock = 0);
+
+	/**
+	 * Used to iterate over the current list of sound channels
+	 */
+	schanid_t iterate(schanid_t chan, glui32 *rockptr = nullptr);
+};
+
+} // End of namespace Glk
+
+#endif


Commit: 0cbd51641b0bc1b205f90324399eca6b08a75c32
    https://github.com/scummvm/scummvm/commit/0cbd51641b0bc1b205f90324399eca6b08a75c32
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Further sound handling

Changed paths:
    audio/decoders/mp3.h
    engines/glk/events.cpp
    engines/glk/frotz/sound_folder.cpp
    engines/glk/glk_api.cpp
    engines/glk/sound.cpp
    engines/glk/sound.h


diff --git a/audio/decoders/mp3.h b/audio/decoders/mp3.h
index 4740d52..5054cac 100644
--- a/audio/decoders/mp3.h
+++ b/audio/decoders/mp3.h
@@ -25,6 +25,7 @@
  * Sound decoder used in engines:
  *  - agos
  *  - draci
+ *  - glk
  *  - kyra
  *  - mohawk
  *  - queen
diff --git a/engines/glk/events.cpp b/engines/glk/events.cpp
index 8f3d5ef..e6f046c 100644
--- a/engines/glk/events.cpp
+++ b/engines/glk/events.cpp
@@ -25,6 +25,7 @@
 #include "glk/glk.h"
 #include "glk/screen.h"
 #include "glk/selection.h"
+#include "glk/sound.h"
 #include "glk/windows.h"
 #include "graphics/cursorman.h"
 
@@ -107,7 +108,9 @@ void Events::checkForNextFrameCounter() {
 			g_vm->_windows->redraw();
 		_redraw = false;
 		g_vm->_screen->update();
-		return;
+
+		// Poll for any finished sounds
+		g_vm->_sounds->poll();
 	}
 }
 
diff --git a/engines/glk/frotz/sound_folder.cpp b/engines/glk/frotz/sound_folder.cpp
index 20f22a8..91916c4 100644
--- a/engines/glk/frotz/sound_folder.cpp
+++ b/engines/glk/frotz/sound_folder.cpp
@@ -71,7 +71,7 @@ const Common::ArchiveMemberPtr SoundSubfolder::getMember(const Common::String &n
 
 Common::SeekableReadStream *SoundSubfolder::createReadStreamForMember(const Common::String &name) const {
 	Common::File *f = new Common::File();
-	if (f->open(_folder.getChild(name)))
+	if (_filenames.contains(name) && f->open(_folder.getChild(_filenames[name])))
 		return f;
 
 	delete f;
diff --git a/engines/glk/glk_api.cpp b/engines/glk/glk_api.cpp
index 1104789..c24d452 100644
--- a/engines/glk/glk_api.cpp
+++ b/engines/glk/glk_api.cpp
@@ -975,7 +975,11 @@ schanid_t GlkAPI::glk_schannel_create(glui32 rock) {
 }
 
 void GlkAPI::glk_schannel_destroy(schanid_t chan) {
-	delete chan;
+	if (chan) {
+		delete chan;
+	} else {
+		warning("schannel_dest roy: invalid ref");
+	}
 }
 
 schanid_t GlkAPI::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
@@ -983,21 +987,38 @@ schanid_t GlkAPI::glk_schannel_iterate(schanid_t chan, glui32 *rockptr) {
 }
 
 glui32 GlkAPI::glk_schannel_get_rock(schanid_t chan) {
-	return chan->_rock;
+	if (chan) {
+		return chan->_rock;
+	} else {
+		warning("schannel_get_rock: invalid ref");
+		return 0;
+	}
 }
 
 glui32 GlkAPI::glk_schannel_play(schanid_t chan, glui32 snd) {
-	// TODO
-	return 0;
+	if (chan) {
+		return chan->play(snd);
+	} else {
+		warning("schannel_play_ext: invalid ref");
+		return 0;
+	}
 }
 
 glui32 GlkAPI::glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify) {
-	// TODO
-	return 0;
+	if (chan) {
+		chan->play(snd, repeats, notify);
+	} else {
+		warning("schannel_play_ext: invalid ref");
+		return 0;
+	}
 }
 
 void GlkAPI::glk_schannel_stop(schanid_t chan) {
-	// TODO
+	if (chan) {
+		chan->stop();
+	} else {
+		warning("schannel_stop: invalid ref");
+	}
 }
 
 void GlkAPI::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
@@ -1005,17 +1026,17 @@ void GlkAPI::glk_schannel_set_volume(schanid_t chan, glui32 vol) {
 }
 
 void GlkAPI::glk_sound_load_hint(glui32 snd, glui32 flag) {
-	// TODO
+	// No implementation
 }
 
 schanid_t GlkAPI::glk_schannel_create_ext(glui32 rock, glui32 volume) {
-	// TODO
+	// No implementation
 	return nullptr;
 }
 
 glui32 GlkAPI::glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount,
                                     glui32 *sndarray, glui32 soundcount, glui32 notify) {
-	// TODO
+	// No implementation
 	return 0;
 }
 
diff --git a/engines/glk/sound.cpp b/engines/glk/sound.cpp
index 5c98b93..85f7e01 100644
--- a/engines/glk/sound.cpp
+++ b/engines/glk/sound.cpp
@@ -21,6 +21,13 @@
  */
 
 #include "glk/sound.h"
+#include "glk/glk.h"
+#include "glk/events.h"
+#include "common/file.h"
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "audio/decoders/mp3.h"
+#include "audio/decoders/wave.h"
 
 namespace Glk {
 
@@ -39,7 +46,7 @@ void Sounds::removeSound(schanid_t snd) {
 }
 
 schanid_t Sounds::create(glui32 rock) {
-	schanid_t snd = new SoundChannel();
+	schanid_t snd = new SoundChannel(this);
 	_sounds.push_back(snd);
 	return snd;
 }
@@ -58,15 +65,83 @@ schanid_t Sounds::iterate(schanid_t chan, glui32 *rockptr) {
 	return nullptr;
 }
 
+void Sounds::poll() {
+	for (uint idx = 0; idx < _sounds.size(); ++idx)
+		_sounds[idx]->poll();
+}
+
 /*--------------------------------------------------------------------------*/
 
+SoundChannel::SoundChannel(Sounds *owner) : _owner(owner), _soundNum(0),
+		_rock(0), _notify(0) {
+}
+
 SoundChannel::~SoundChannel() {
+	stop();
 	_owner->removeSound(this);
-	delete _stream;
 }
 
-void SoundChannel::play(uint soundNum) {
-	// TODO
+glui32 SoundChannel::play(glui32 soundNum, glui32 repeats, glui32 notify) {
+	stop();
+	if (repeats == 0)
+		return 1;
+
+	// Find a sound of the given name
+	Audio::AudioStream *stream;
+	Common::File f;
+	Common::String nameSnd = Common::String::format("sound%u.snd", soundNum);
+	Common::String nameMp3 = Common::String::format("sound%u.mp3", soundNum);
+	Common::String nameWav = Common::String::format("sound%u.wav", soundNum);
+
+	if (f.exists(nameSnd) && f.open(nameSnd)) {
+		if (f.readUint16BE() != (f.size() - 2))
+			error("Invalid sound filesize");
+		repeats = f.readByte();
+		f.skip(1);
+		uint freq = f.readUint16BE();
+		f.skip(2);
+		uint size = f.readUint16BE();
+
+		Common::SeekableReadStream *s = f.readStream(size);
+		stream = Audio::makeRawStream(s, freq, Audio::FLAG_UNSIGNED);
+
+	} else if (f.exists(nameMp3) && f.open(nameMp3)) {
+		Common::SeekableReadStream *s = f.readStream(f.size());
+		stream = Audio::makeMP3Stream(s, DisposeAfterUse::YES);
+
+	} else if (f.exists(nameWav) && f.open(nameWav)) {
+		Common::SeekableReadStream *s = f.readStream(f.size());
+		stream = Audio::makeWAVStream(s, DisposeAfterUse::YES);
+
+	} else {
+		warning("Could not find sound %u", soundNum);
+		return 1;
+	}
+
+	_soundNum = soundNum;
+	_notify = notify;
+
+	// Set up a repeat if multiple repeats are specified
+	if (repeats > 1) {
+		Audio::RewindableAudioStream *rwStream = dynamic_cast<Audio::RewindableAudioStream *>(stream);
+		assert(rwStream);
+		stream = new Audio::LoopingAudioStream(rwStream, repeats, DisposeAfterUse::YES);
+	}
+
+	// Start playing the audio
+	g_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream);
+	return 0;
+}
+
+void SoundChannel::stop() {
+	g_vm->_mixer->stopHandle(_handle);
+}
+
+void SoundChannel::poll() {
+	if (!g_vm->_mixer->isSoundHandleActive(_handle) && _notify != 0)
+		g_vm->_events->store(evtype_SoundNotify, 0,
+			_soundNum, _notify);
+
 }
 
 } // End of namespace Glk
diff --git a/engines/glk/sound.h b/engines/glk/sound.h
index 05875ea..514a5f6 100644
--- a/engines/glk/sound.h
+++ b/engines/glk/sound.h
@@ -25,6 +25,7 @@
 
 #include "glk/glk_types.h"
 #include "audio/audiostream.h"
+#include "audio/mixer.h"
 #include "common/array.h"
 
 namespace Glk {
@@ -34,15 +35,19 @@ class Sounds;
 /**
  * Holds the data for a playing sound
  */
-struct SoundChannel {
+class SoundChannel {
+private:
 	Sounds *_owner;
-	Audio::AudioStream *_stream;
+	glui32 _soundNum;
+	glui32 _notify;
+	Audio::SoundHandle _handle;
+public:
 	glui32 _rock;
-
+public:
 	/**
-	 * Destructor
+	 * Constructor
 	 */
-	SoundChannel() : _stream(nullptr), _rock(0) {}
+	SoundChannel(Sounds *owner);
 
 	/**
 	 * Destructor
@@ -52,7 +57,17 @@ struct SoundChannel {
 	/**
 	 * Play a sound
 	 */
-	void play(uint soundNum);
+	glui32 play(glui32 soundNum, glui32 repeats = 1, glui32 notify = 0);
+
+	/**
+	 * Stop playing sound
+	 */
+	void stop();
+
+	/**
+	 * Poll for whether a playing sound was finished
+	 */
+	void poll();
 };
 typedef SoundChannel *schanid_t;
 
@@ -60,7 +75,7 @@ typedef SoundChannel *schanid_t;
  * Sound manager
  */
 class Sounds {
-	friend struct SoundChannel;
+	friend class SoundChannel;
 private:
 	Common::Array<schanid_t> _sounds;
 private:
@@ -80,6 +95,11 @@ public:
 	 * Used to iterate over the current list of sound channels
 	 */
 	schanid_t iterate(schanid_t chan, glui32 *rockptr = nullptr);
+
+	/**
+	 * Poll for whether any playing sounds are finished
+	 */
+	void poll();
 };
 
 } // End of namespace Glk


Commit: 43283ff561233d5e67cc15d8f1c3da4489156c0c
    https://github.com/scummvm/scummvm/commit/43283ff561233d5e67cc15d8f1c3da4489156c0c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Playing sounds now works directly from lhsound.zip and shsound.zip

Changed paths:
    engines/glk/frotz/sound_folder.cpp


diff --git a/engines/glk/frotz/sound_folder.cpp b/engines/glk/frotz/sound_folder.cpp
index 91916c4..fd99b0b 100644
--- a/engines/glk/frotz/sound_folder.cpp
+++ b/engines/glk/frotz/sound_folder.cpp
@@ -90,7 +90,7 @@ void SoundZip::check(const Common::FSNode &gameDir, Story story) {
 	if (!zipNode.exists())
 		return;
 
-	SearchMan.add("sound", Common::makeZipArchive(zipNode));
+	SearchMan.add("sound", new SoundZip(Common::makeZipArchive(zipNode)));
 }
 
 SoundZip::SoundZip(Common::Archive *zip) : _zip(zip) {


Commit: 08db684864f0734a2d93641dc7a23ce117073eb4
    https://github.com/scummvm/scummvm/commit/08db684864f0734a2d93641dc7a23ce117073eb4
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: Add support for secondary Blorb files separate from the gamefile

It also hads further support for playing AIFF files. However, the
Blorb files for Lurking Horror & Sherlock on the if-archive website
don't seem to be valid AIFF files, so sound doesn't play using them

Changed paths:
    audio/decoders/wave.h
    engines/glk/blorb.cpp
    engines/glk/glk.cpp
    engines/glk/sound.cpp


diff --git a/audio/decoders/wave.h b/audio/decoders/wave.h
index aaf4a01..75581f1 100644
--- a/audio/decoders/wave.h
+++ b/audio/decoders/wave.h
@@ -28,6 +28,7 @@
  *  - cge
  *  - cge2
  *  - fullpipe
+ *  - glk
  *  - gob
  *  - hopkins
  *  - mohawk
diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index 5ffd0b0..4a4cf8c 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -153,7 +153,7 @@ Common::ErrorCode Blorb::load() {
 				ce._filename += ".mp3";
 			else if (ce._id == ID_WAVE)
 				ce._filename += ".wav";
-			else if (ce._id == ID_AIFF)
+			else if (ce._id == ID_AIFF || ce._id == ID_FORM)
 				ce._filename += ".aiff";
 			else if (ce._id == ID_OGG)
 				ce._filename += ".ogg"; 
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index c8a63c5..849de8d 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -122,6 +122,20 @@ Common::Error GlkEngine::run() {
 		if (!f.open("game", *_blorb))
 			return Common::kNoGameDataFoundError;
 	} else {
+		// Check for a secondary blorb file with the same filename
+		Common::String baseName = filename;
+		while (baseName.contains('.'))
+			baseName.deleteLastChar();
+
+		if (f.exists(baseName + ".blorb")) {
+			_blorb = new Blorb(baseName + ".blorb", getInterpreterType());
+			SearchMan.add("blorb", _blorb, 99, false);
+		} else if (f.exists(baseName + ".blb")) {
+			_blorb = new Blorb(baseName + ".blb", getInterpreterType());
+			SearchMan.add("blorb", _blorb, 99, false);
+		}
+
+		// Open up the game file
 		if (!f.open(filename))
 			return Common::kNoGameDataFoundError;
 	}
diff --git a/engines/glk/sound.cpp b/engines/glk/sound.cpp
index 85f7e01..02fb7f9 100644
--- a/engines/glk/sound.cpp
+++ b/engines/glk/sound.cpp
@@ -25,6 +25,7 @@
 #include "glk/events.h"
 #include "common/file.h"
 #include "audio/audiostream.h"
+#include "audio/decoders/aiff.h"
 #include "audio/decoders/raw.h"
 #include "audio/decoders/mp3.h"
 #include "audio/decoders/wave.h"
@@ -92,6 +93,7 @@ glui32 SoundChannel::play(glui32 soundNum, glui32 repeats, glui32 notify) {
 	Common::String nameSnd = Common::String::format("sound%u.snd", soundNum);
 	Common::String nameMp3 = Common::String::format("sound%u.mp3", soundNum);
 	Common::String nameWav = Common::String::format("sound%u.wav", soundNum);
+	Common::String nameAiff = Common::String::format("sound%u.aiff", soundNum);
 
 	if (f.exists(nameSnd) && f.open(nameSnd)) {
 		if (f.readUint16BE() != (f.size() - 2))
@@ -113,6 +115,10 @@ glui32 SoundChannel::play(glui32 soundNum, glui32 repeats, glui32 notify) {
 		Common::SeekableReadStream *s = f.readStream(f.size());
 		stream = Audio::makeWAVStream(s, DisposeAfterUse::YES);
 
+	} else if (f.exists(nameAiff) && f.open(nameAiff)) {
+		Common::SeekableReadStream *s = f.readStream(f.size());
+		stream = Audio::makeAIFFStream(s, DisposeAfterUse::YES);
+
 	} else {
 		warning("Could not find sound %u", soundNum);
 		return 1;


Commit: ecb0c10fdb90cf57dca0a528d3b7291979ee7ab8
    https://github.com/scummvm/scummvm/commit/ecb0c10fdb90cf57dca0a528d3b7291979ee7ab8
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Properly flag the detections of Infocom games that have sound effects

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


diff --git a/engines/glk/frotz/detection.cpp b/engines/glk/frotz/detection.cpp
index c869c92..7afeec2 100644
--- a/engines/glk/frotz/detection.cpp
+++ b/engines/glk/frotz/detection.cpp
@@ -106,6 +106,7 @@ bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &g
 		} else {
 			PlainGameDescriptor gameDesc = findGame(p->_gameId);
 			gd = DetectedGame(p->_gameId, gameDesc.description, p->_language, Common::kPlatformUnknown, p->_extra);
+			gd.setGUIOptions(p->_guiOptions);
 		}
 
 		gd.addExtraEntry("filename", filename);
diff --git a/engines/glk/frotz/detection_tables.h b/engines/glk/frotz/detection_tables.h
index 179067c..2bcb011 100644
--- a/engines/glk/frotz/detection_tables.h
+++ b/engines/glk/frotz/detection_tables.h
@@ -88,6 +88,8 @@ const PlainGameDescriptor FROTZ_GAME_LIST[] = {
 #define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
 #define ENTRY0(ID, VERSION, MD5, FILESIZE) { ID, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
 #define ENTRY1(ID, VERSION, MD5, FILESIZE, LANG) { ID, VERSION, MD5, FILESIZE, LANG, NONE }
+#define ENTRYS(ID, VERSION, MD5, FILESIZE) { ID, VERSION, MD5, FILESIZE, Common::EN_ANY, GUIO3(GUIO_NOSPEECH, GUIO_NOMUSIC, GUIO_NOSUBTITLES) }
+
 #define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
 
 const FrotzGameDescription FROTZ_GAMES[] = {
@@ -124,9 +126,9 @@ const FrotzGameDescription FROTZ_GAMES[] = {
 	ENTRY0("lgop", "R0", "69b3534570851b90d7f53ebe9d224a6a", 128998),
 	ENTRY0("lgop", "R4-880405", "6bdae7434df7c03f3589ece0bed3317d", 159928),
 	ENTRY0("lgop", "R59-860730", "e81237e220a612c5a93fbcc1fdf85a0a", 129022),
-	ENTRY0("lurkinghorror", "R203", "e2d2505510479fec0405727e3d0abc10", 128986),
-	ENTRY0("lurkinghorror", "R219", "83936d75c2cfd71fb64bf63c4696b9ac", 129704),
-	ENTRY0("lurkinghorror", "R221", "c60cd0bf3c6eda867241378c7cb5464a", 129944),
+	ENTRYS("lurkinghorror", "R203", "e2d2505510479fec0405727e3d0abc10", 128986),
+	ENTRYS("lurkinghorror", "R219", "83936d75c2cfd71fb64bf63c4696b9ac", 129704),
+	ENTRYS("lurkinghorror", "R221", "c60cd0bf3c6eda867241378c7cb5464a", 129944),
 	ENTRY0("minizork1", "R34-871124", "0d7700679e5e63dec97f712698610a46", 52216),
 	ENTRY0("moonmist", "R4-860918", "284797c3025ffaf76aecfa5c2bbffa86", 129002),
 	ENTRY0("moonmist", "R9-861022", "698475de2769c66bc5a1eca600c71561", 128866),
@@ -146,8 +148,8 @@ const FrotzGameDescription FROTZ_GAMES[] = {
 	ENTRY0("seastalker", "R16-850515", "eb39dff7beb3589c8581dd2e3569eb78", 117752),
 	ENTRY0("seastalker", "R16-850603", "bccf194b1e823e37db2431b586662773", 117762),
 	ENTRY0("seastalker", "R86-840320", "64fb27e7b9fd682ff4f0d0ec6616a468", 116456),
-	ENTRY0("sherlockriddle", "R21-871214", "69862f7f07a4e977159ea4da7f2f2ba6", 188444),
-	ENTRY0("sherlockriddle", "R26-880127", "2cb2bda2e34eb7f9494cb585720e74cd", 190180),
+	ENTRYS("sherlockriddle", "R21-871214", "69862f7f07a4e977159ea4da7f2f2ba6", 188444),
+	ENTRYS("sherlockriddle", "R26-880127", "2cb2bda2e34eb7f9494cb585720e74cd", 190180),
 	ENTRY0("shogun", "R322-890706", "62cca41feb94082442026f44f3e48e19", 344816),
 	ENTRY0("sorcerer", "R4-840131", "d4a914fdfe90f5cd055a03b9aa24addd", 109734),
 	ENTRY0("sorcerer", "R6-840508", "7ee357c10a9e049fe7c641a4817ee575", 109482),


Commit: 754819f9281d5207deaff1922a915bd9dc0437b5
    https://github.com/scummvm/scummvm/commit/754819f9281d5207deaff1922a915bd9dc0437b5
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: FROTZ: Reduce window margin to 0 to have status bar fill entire width

Changed paths:
    engines/glk/conf.cpp


diff --git a/engines/glk/conf.cpp b/engines/glk/conf.cpp
index ee2fd9b..efd1687 100644
--- a/engines/glk/conf.cpp
+++ b/engines/glk/conf.cpp
@@ -107,7 +107,7 @@ Conf::Conf(InterpreterType interpType) {
 	if (ConfMan.hasKey("maxcols"))
 		_cols = MIN(_cols, strToInt(ConfMan.get("maxcols").c_str()));
 
-	const int DEFAULT_MARGIN_X = (interpType == INTERPRETER_FROTZ) ? 2 : 15;
+	const int DEFAULT_MARGIN_X = (interpType == INTERPRETER_FROTZ) ? 0 : 15;
 	const int DEFAULT_MARGIN_Y = (interpType == INTERPRETER_FROTZ) ? 0 : 15;
 
 	get("lockrows", _lockRows);


Commit: 290196946dab0ee60498be16d8e33bb911e5a166
    https://github.com/scummvm/scummvm/commit/290196946dab0ee60498be16d8e33bb911e5a166
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: GLULXE: Skeleton sub-engine

Changed paths:
  A engines/glk/glulxe/detection.cpp
  A engines/glk/glulxe/detection.h
  A engines/glk/glulxe/detection_tables.h
  A engines/glk/glulxe/glulxe.cpp
  A engines/glk/glulxe/glulxe.h
    engines/glk/blorb.cpp
    engines/glk/detection.cpp
    engines/glk/glk.cpp
    engines/glk/glk_types.h
    engines/glk/module.mk


diff --git a/engines/glk/blorb.cpp b/engines/glk/blorb.cpp
index 4a4cf8c..1438abb 100644
--- a/engines/glk/blorb.cpp
+++ b/engines/glk/blorb.cpp
@@ -171,6 +171,7 @@ Common::ErrorCode Blorb::load() {
 
 			if (
 				(_interpType == INTERPRETER_FROTZ && type == "ZCOD") ||
+				(_interpType == INTERPRETER_GLULXE && type == "GLUL") ||
 				(_interpType == INTERPRETER_TADS2 && type == "TAD2") ||
 				(_interpType == INTERPRETER_TADS3 && type == "TAD3") ||
 				(_interpType == INTERPRETER_HUGO && type == "HUGO")
diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 491c6c8..b848178 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -23,6 +23,8 @@
 #include "glk/glk.h"
 #include "glk/frotz/detection.h"
 #include "glk/frotz/frotz.h"
+#include "glk/glulxe/detection.h"
+#include "glk/glulxe/glulxe.h"
 #include "glk/scott/detection.h"
 #include "glk/scott/scott.h"
 #include "glk/tads/detection.h"
@@ -132,9 +134,11 @@ Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
 	gameDesc._md5 = Common::computeStreamMD5AsString(f, 5000);
 	f.close();
 
-	// Correct the correct engine
+	// Create the correct engine
 	if (Glk::Frotz::FrotzMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
 		*engine = new Glk::Frotz::Frotz(syst, gameDesc);
+	} else if (Glk::Glulxe::GlulxeMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
+		*engine = new Glk::Glulxe::Glulxe(syst, gameDesc);
 	} else if (Glk::Scott::ScottMetaEngine::findGame(gameDesc._gameId.c_str()).description) {
 		*engine = new Glk::Scott::Scott(syst, gameDesc);
 	} else if ((td = Glk::TADS::TADSMetaEngine::findGame(gameDesc._gameId.c_str())).description) {
@@ -174,6 +178,7 @@ Common::String GlkMetaEngine::findFileByGameId(const Common::String &gameId) con
 PlainGameList GlkMetaEngine::getSupportedGames() const {
 	PlainGameList list;
 	Glk::Frotz::FrotzMetaEngine::getSupportedGames(list);
+	Glk::Glulxe::GlulxeMetaEngine::getSupportedGames(list);
 	Glk::Scott::ScottMetaEngine::getSupportedGames(list);
 	Glk::TADS::TADSMetaEngine::getSupportedGames(list);
 
@@ -186,6 +191,9 @@ PlainGameDescriptor GlkMetaEngine::findGame(const char *gameId) const {
 	gd = Glk::Frotz::FrotzMetaEngine::findGame(gameId);
 	if (gd.description) return gd;
 
+	gd = Glk::Glulxe::GlulxeMetaEngine::findGame(gameId);
+	if (gd.description) return gd;
+
 	gd = Glk::Scott::ScottMetaEngine::findGame(gameId);
 	if (gd.description) return gd;
 
@@ -198,6 +206,7 @@ PlainGameDescriptor GlkMetaEngine::findGame(const char *gameId) const {
 DetectedGames GlkMetaEngine::detectGames(const Common::FSList &fslist) const {
 	DetectedGames detectedGames;
 	Glk::Frotz::FrotzMetaEngine::detectGames(fslist, detectedGames);
+	Glk::Glulxe::GlulxeMetaEngine::detectGames(fslist, detectedGames);
 	Glk::Scott::ScottMetaEngine::detectGames(fslist, detectedGames);
 	Glk::TADS::TADSMetaEngine::detectGames(fslist, detectedGames);
 
diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index 849de8d..8c35cfe 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -114,7 +114,8 @@ Common::Error GlkEngine::run() {
 
 	initialize();
 
-	if (filename.hasSuffixIgnoreCase(".blorb") || filename.hasSuffixIgnoreCase(".zblorb")) {
+	if (filename.hasSuffixIgnoreCase(".blorb") || filename.hasSuffixIgnoreCase(".zblorb")
+			|| filename.hasSuffixIgnoreCase(".gblorb")) {
 		// Blorb archive
 		_blorb = new Blorb(filename, getInterpreterType());
 		SearchMan.add("blorb", _blorb, 99, false);
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index abae785..3c8f60f 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -43,15 +43,16 @@ enum InterpreterType {
 	INTERPRETER_BOCFEL = 4,
 	INTERPRETER_FROTZ = 5,
 	INTERPRETER_GEAS = 6,
-	INTERPRETER_HUGO = 7,
-	INTERPRETER_JACL = 8,
-	INTERPRETER_LEVEL9 = 9,
-	INTERPRETER_MAGNETIC = 10,
-	INTERPRETER_NITFOL = 11,
-	INTERPRETER_SCARE = 12,
-	INTERPRETER_SCOTT = 13,
-	INTERPRETER_TADS2 = 14,
-	INTERPRETER_TADS3 = 15
+	INTERPRETER_GLULXE = 7,
+	INTERPRETER_HUGO = 8,
+	INTERPRETER_JACL = 9,
+	INTERPRETER_LEVEL9 = 10,
+	INTERPRETER_MAGNETIC = 11,
+	INTERPRETER_NITFOL = 12,
+	INTERPRETER_SCARE = 13,
+	INTERPRETER_SCOTT = 14,
+	INTERPRETER_TADS2 = 15,
+	INTERPRETER_TADS3 = 16
 };
 
 /**
diff --git a/engines/glk/glulxe/detection.cpp b/engines/glk/glulxe/detection.cpp
new file mode 100644
index 0000000..0e1ec1f
--- /dev/null
+++ b/engines/glk/glulxe/detection.cpp
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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/glulxe/detection.h"
+#include "glk/glulxe/detection_tables.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "engines/game.h"
+
+namespace Glk {
+namespace Glulxe {
+
+void GlulxeMetaEngine::getSupportedGames(PlainGameList &games) {
+	for (const GlulxeDescriptor *pd = GLULXE_GAME_LIST; pd->gameId; ++pd) {
+		games.push_back(*pd);
+	}
+}
+
+GlulxeDescriptor GlulxeMetaEngine::findGame(const char *gameId) {
+	for (const GlulxeDescriptor *pd = GLULXE_GAME_LIST; pd->gameId; ++pd) {
+		if (!strcmp(gameId, pd->gameId))
+			return *pd;
+	}
+
+	return GlulxeDescriptor();
+}
+
+bool GlulxeMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+	const char *const EXTENSIONS[3] = { ".ulx", ".blb", ".gblorb" };
+
+	// 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 (int idx = 0; idx < 3 && !hasExt; ++idx)
+			hasExt = filename.hasSuffixIgnoreCase(EXTENSIONS[idx]);
+		if (!hasExt)
+			continue;
+
+		// Open up the file and calculate the md5
+		Common::File gameFile;
+		if (!gameFile.open(*file))
+			continue;
+		Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+		size_t filesize = gameFile.size();
+		gameFile.close();
+
+		// Check for known games
+		const GlulxeGameDescription *p = GLULXE_GAMES;
+		while (p->_gameId && (md5 != p->_md5 || filesize != p->_filesize))
+			++p;
+
+		DetectedGame gd;
+		if (!p->_gameId) {
+			if (filename.hasSuffixIgnoreCase(".blb"))
+				continue;
+
+			if (gDebugLevel > 0) {
+				// Print an entry suitable for putting into the detection_tables.h, using the
+				// name of the parent folder the game is in as the presumed game Id
+				Common::String folderName = file->getParent().getName();
+				if (folderName.hasSuffix("\\"))
+					folderName.deleteLastChar();
+				Common::String fname = filename;
+				const char *dot = strchr(fname.c_str(), '.');
+				if (dot)
+					fname = Common::String(fname.c_str(), dot);
+
+				debug("ENTRY0(\"%s\", \"%s\", %lu),",
+					fname.c_str(), md5.c_str(), filesize);
+			}
+			const PlainGameDescriptor &desc = GLULXE_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();
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/detection.h b/engines/glk/glulxe/detection.h
new file mode 100644
index 0000000..d6d5c6a
--- /dev/null
+++ b/engines/glk/glulxe/detection.h
@@ -0,0 +1,71 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 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_GLULXE_DETECTION
+#define GLK_GLULXE_DETECTION
+
+#include "common/fs.h"
+#include "engines/game.h"
+
+namespace Glk {
+namespace Glulxe {
+
+/**
+ * Glulxe game descriptior
+ */
+struct GlulxeDescriptor {
+	const char *gameId;
+	const char *description;
+
+	operator PlainGameDescriptor() const {
+		PlainGameDescriptor pd;
+		pd.gameId = gameId;
+		pd.description = description;
+		return pd;
+	}
+};
+
+/**
+ * Meta engine for Glulxe interpreter
+ */
+class GlulxeMetaEngine {
+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 GlulxeDescriptor findGame(const char *gameId);
+
+	/**
+	 * Detect supported games
+	 */
+	static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+};
+
+} // End of namespace Glulxe
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/glulxe/detection_tables.h b/engines/glk/glulxe/detection_tables.h
new file mode 100644
index 0000000..17c5ffd
--- /dev/null
+++ b/engines/glk/glulxe/detection_tables.h
@@ -0,0 +1,57 @@
+/* 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 Glulxe {
+
+/**
+ * Game description
+ */
+struct GlulxeGameDescription {
+	const char *const _gameId;
+	const char *const _extra;
+	const char *const _md5;
+	size_t _filesize;
+	Common::Language _language;
+};
+
+const GlulxeDescriptor GLULXE_GAME_LIST[] = {
+	{ "glulxe", "Glulxe Game" },
+	{ "cragne", "Cragne Manor" },
+
+	{ nullptr, nullptr }
+};
+
+#define ENTRY0(ID, MD5, FILESIZE) { ID, nullptr, MD5, FILESIZE, Common::EN_ANY }
+#define TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY }
+
+const GlulxeGameDescription GLULXE_GAMES[] = {
+	ENTRY0("cragne", "082f518c0120d2323ce340bef8a2d5a9", 8869096),
+	TABLE_END_MARKER
+};
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
new file mode 100644
index 0000000..25af4c2
--- /dev/null
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -0,0 +1,48 @@
+/* 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/glulxe/glulxe.h"
+#include "common/config-manager.h"
+#include "common/translation.h"
+
+namespace Glk {
+namespace Glulxe {
+
+Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+}
+
+void Glulxe::runGame(Common::SeekableReadStream *gameFile) {
+	// TODO
+}
+
+Common::Error Glulxe::loadGameData(strid_t file) {
+	// TODO
+	return Common::kNoError;
+}
+
+Common::Error Glulxe::saveGameData(strid_t file, const Common::String &desc) {
+	// TODO
+	return Common::kNoError;
+}
+
+} // End of namespace Glulxe
+} // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
new file mode 100644
index 0000000..eb4aaf3
--- /dev/null
+++ b/engines/glk/glulxe/glulxe.h
@@ -0,0 +1,66 @@
+/* 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_GLULXE
+#define GLK_GLULXE
+
+#include "common/scummsys.h"
+#include "glk/glk_api.h"
+
+namespace Glk {
+namespace Glulxe {
+
+/**
+ * Glulxe game interpreter
+ */
+class Glulxe : public GlkAPI {
+public:
+	/**
+	 * Constructor
+	 */
+	Glulxe(OSystem *syst, const GlkGameDescription &gameDesc);
+
+	/**
+	 * Run the game
+	 */
+	void runGame(Common::SeekableReadStream *gameFile);
+
+	/**
+	 * Returns the running interpreter type
+	 */
+	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_GLULXE; }
+
+	/**
+	 * 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 Glulxe
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index de69645..7164481 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -44,6 +44,8 @@ MODULE_OBJS := \
 	frotz/quetzal.o \
 	frotz/screen.o \
 	frotz/sound_folder.o \
+	glulxe/detection.o \
+	glulxe/glulxe.o \
 	scott/detection.o \
 	scott/scott.o \
 	tads/detection.o \


Commit: ac9830614d07d80f0c02d9d7c75d709f9d534466
    https://github.com/scummvm/scummvm/commit/ac9830614d07d80f0c02d9d7c75d709f9d534466
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2018-12-08T19:05:59-08:00

Commit Message:
GLK: GLULXE: Validate game file version

Changed paths:
    engines/glk/POTFILES
    engines/glk/glulxe/glulxe.cpp
    engines/glk/glulxe/glulxe.h


diff --git a/engines/glk/POTFILES b/engines/glk/POTFILES
index d672609..935c263 100644
--- a/engines/glk/POTFILES
+++ b/engines/glk/POTFILES
@@ -1,3 +1,4 @@
 engines/glk/streams.cpp
 engines/glk/scott/scott.cpp
 engines/glk/frotz/detection.cpp
+engines/glk/glulxe/glulxe.cpp
diff --git a/engines/glk/glulxe/glulxe.cpp b/engines/glk/glulxe/glulxe.cpp
index 25af4c2..e2e1a47 100644
--- a/engines/glk/glulxe/glulxe.cpp
+++ b/engines/glk/glulxe/glulxe.cpp
@@ -27,10 +27,16 @@
 namespace Glk {
 namespace Glulxe {
 
-Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+Glulxe::Glulxe(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
+		vm_exited_cleanly(false) {
 }
 
 void Glulxe::runGame(Common::SeekableReadStream *gameFile) {
+	_gameFile = gameFile;
+
+	if (!is_gamefile_valid())
+		return;
+
 	// TODO
 }
 
@@ -44,5 +50,30 @@ Common::Error Glulxe::saveGameData(strid_t file, const Common::String &desc) {
 	return Common::kNoError;
 }
 
+bool Glulxe::is_gamefile_valid() {
+	if (_gameFile->size() < 8) {
+		GUIError(_("This is too short to be a valid Glulx file."));
+		return false;
+	}
+
+	if (_gameFile->readUint32BE() != MKTAG('G', 'l', 'u', 'l')) {
+		GUIError(_("This is not a valid Glulx file."));
+		return false;
+	}
+
+	// We support version 2.0 through 3.1.*
+	uint version = _gameFile->readUint32BE();
+	if (version < 0x20000) {
+		GUIError(_("This Glulx file is too old a version to execute."));
+		return false;
+	}
+	if (version >= 0x30200) {
+		GUIError(_("This Glulx file is too new a version to execute."));
+		return false;
+	}
+
+	return true;
+}
+
 } // End of namespace Glulxe
 } // End of namespace Glk
diff --git a/engines/glk/glulxe/glulxe.h b/engines/glk/glulxe/glulxe.h
index eb4aaf3..3764ca4 100644
--- a/engines/glk/glulxe/glulxe.h
+++ b/engines/glk/glulxe/glulxe.h
@@ -34,6 +34,14 @@ namespace Glulxe {
  */
 class Glulxe : public GlkAPI {
 public:
+	Common::SeekableReadStream *_gameFile;
+	bool vm_exited_cleanly;
+private:
+	/**
+	 * Validates the game file, and if it's invalid, displays an error dialog
+	 */
+	bool is_gamefile_valid();
+public:
 	/**
 	 * Constructor
 	 */





More information about the Scummvm-git-logs mailing list