[Scummvm-git-logs] scummvm master -> 96ebd81e5f29e9cde3d83c4af0b9166cd1f40b26

dreammaster paulfgilbert at gmail.com
Sat May 11 08:15:38 CEST 2019


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

Summary:
1bbfcca229 GLK: HUGO: Add heparse
34122d2f47 GLK: HUGO: Added herun
96ebd81e5f GLK: HUGO: NULL to nullptr, Amiga compilation fix


Commit: 1bbfcca229a3aa39854d495b0ce497d958d37b2e
    https://github.com/scummvm/scummvm/commit/1bbfcca229a3aa39854d495b0ce497d958d37b2e
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-11T13:59:44+10:00

Commit Message:
GLK: HUGO: Add heparse

Changed paths:
  A engines/glk/hugo/heparse.cpp
    engines/glk/hugo/hemisc.cpp
    engines/glk/hugo/hugo.cpp
    engines/glk/hugo/hugo.h
    engines/glk/hugo/hugo_defines.h
    engines/glk/module.mk


diff --git a/engines/glk/hugo/hemisc.cpp b/engines/glk/hugo/hemisc.cpp
index 9e56057..4a2ea25 100644
--- a/engines/glk/hugo/hemisc.cpp
+++ b/engines/glk/hugo/hemisc.cpp
@@ -977,13 +977,14 @@ char *Hugo::GetText(long textaddr) {
 	return g;
 }
 
-const char *Hugo::GetWord(unsigned int w) {
-	static const char *b;
+char *Hugo::GetWord(unsigned int w) {
+	char *b;
 	unsigned short a;
+	static char *EMPTY = "";
 
 	a = w;
 
-	if (a==0) return "";
+	if (a==0) return EMPTY;
 
 	if (a==PARSE_STRING_VAL) return parseerr;
 	if (a==SERIAL_STRING_VAL) return serial;
@@ -991,8 +992,7 @@ const char *Hugo::GetWord(unsigned int w) {
 	/* bounds-checking to avoid some sort of memory arena error */
 	if ((long)(a+dicttable*16L) > codeend)
 	{
-		b = "";
-		return b;
+		return EMPTY;
 	}
 
 	defseg = dicttable;
diff --git a/engines/glk/hugo/heparse.cpp b/engines/glk/hugo/heparse.cpp
new file mode 100644
index 0000000..2a70fed
--- /dev/null
+++ b/engines/glk/hugo/heparse.cpp
@@ -0,0 +1,2579 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/hugo.h"
+
+namespace Glk {
+namespace Hugo {
+
+#define STARTS_AS_NUMBER(a) (((a[0]>='0' && a[0]<='9') || a[0]=='-')?1:0)
+
+void Hugo::AddAllObjects(int loc) {
+	int i;
+
+	if (loc==var[player] && domain!=loc)
+		return;
+
+	/* Try to add everything in the specified domain
+	   to objlist[]
+	*/
+	for (i=Child(loc); i!=0; i=Sibling(i))
+	{
+		if (i==var[xobject]) continue;
+
+		TryObj(i);
+		if (domain==0)
+		{
+			if (Child(i)) AddAllObjects(i);
+		}
+	}
+}
+
+void Hugo::AddObj(int obj) {
+	int i;
+
+	for (i=0; i<objcount; i++)
+	{
+		if (objlist[i]==obj)
+			return;
+	}
+
+	objlist[(int)objcount] = obj;
+	if (++objcount> MAXOBJLIST) objcount = MAXOBJLIST;
+}
+
+void Hugo::AddPossibleObject(int obj, char type, unsigned int w) {
+	int i;
+
+	if (pobjcount==MAXPOBJECTS)
+		return;
+
+	for (i=0; i<pobjcount; i++)
+	{
+		/* If it is already in the list */
+		if (pobjlist[i].obj==obj)
+		{
+			/* Being referred to with a noun outweighs being
+			   referred to previously with an adjective
+			*/
+			if (type==(char)noun || ObjWordType(obj, w, noun))
+				pobjlist[i].type = (char)noun;
+
+			return;
+		}
+	}
+
+	/* Getting this to point is presuming that we're adding an object
+	   referred to with an adjective, but check just to be sure it isn't
+	   also a noun for that same object
+	*/
+	if (ObjWordType(obj, w, noun)) type = (char)noun;
+	
+	pobjlist[pobjcount].obj = obj;
+	pobjlist[pobjcount].type = type;
+
+	pobjcount++;
+#ifdef DEBUG_PARSER
+{
+	char buf[100];
+	sprintf(buf, "AddPossibleObject(%d:\"%s\")", obj, Name(obj));
+	Printout(buf);
+}
+#endif
+}
+
+void Hugo::AdvanceGrammar() {
+	int a;
+
+	defseg = gameseg;
+
+	switch (a = Peek(grammaraddr))
+	{
+		case FORWARD_SLASH_T:
+		case HELD_T:
+		case MULTI_T:
+		case MULTIHELD_T:
+		case ANYTHING_T:
+		case NUMBER_T:
+		case PARENT_T:
+		case NOTHELD_T:
+		case MULTINOTHELD_T:
+		case WORD_T:
+		case OBJECT_T:
+		case XOBJECT_T:
+		case STRING_T:
+			grammaraddr++;
+			break;
+
+		case ASTERISK_T:
+		case ATTR_T:
+			grammaraddr += 2;
+			break;
+
+		case DICTENTRY_T:
+		case ROUTINE_T:
+		case OBJECTNUM_T:
+			grammaraddr += 3;
+			break;
+
+		case OPEN_BRACKET_T:
+			grammaraddr +=5;
+			break;
+	}
+}
+
+int Hugo::AnyObjWord(int wn) {
+	int i;
+	
+	if (objword_cache[wn])
+		return objword_cache[wn];
+
+	for (i=0; i<objects; i++)
+	{
+		if (ObjWord(i, wd[wn]))
+		{
+			return (objword_cache[wn] = 1);
+		}
+	}
+
+	return (objword_cache[wn] = -1);
+}
+
+int Hugo::Available(int obj, char non_grammar) {
+	int temp_stack_depth;
+
+	if (findobjectaddr)
+	{
+		passlocal[0] = obj;
+
+		/* if anything or (Routine) grammar */
+		if ((Peek(grammaraddr)==ANYTHING_T
+			|| (Peek(grammaraddr)==OPEN_BRACKET_T && Peek(grammaraddr+1)==ROUTINE_T))
+			&& non_grammar==0)
+		{
+			passlocal[1] = 0;
+		}
+		else
+		{
+			if (domain > 0)
+				passlocal[1] = domain;
+			else if (speaking && non_grammar==0)
+				passlocal[1] = GrandParent(speaking);
+			/* domain of -1 is an explicit 'parent' */
+/*
+			else if (domain==-1)
+				passlocal[1] = parse_location;
+*/
+			else
+				passlocal[1] = parse_location;
+		}
+
+		ret = 0;
+
+		PassLocals(2);
+		temp_stack_depth = stack_depth;
+
+		SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+#if defined (DEBUGGER)
+		DebugRunRoutine((long)findobjectaddr*address_scale);
+#else
+		RunRoutine((long)findobjectaddr*address_scale);
+#endif
+		retflag = 0;
+		stack_depth = temp_stack_depth;
+		return ret;
+	}
+	else
+		return 1;
+}
+
+void Hugo::CallLibraryParse() {
+	if (parseaddr)
+	{
+#ifdef DEBUG_PARSER
+		Printout("CallLibraryParse()");
+#endif
+		parse_called_twice = false;
+
+		SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+		ret = 0;
+		PassLocals(0);
+#if defined (DEBUGGER)
+		DebugRunRoutine((long)parseaddr*address_scale);
+#else
+		RunRoutine((long)parseaddr*address_scale);
+#endif
+		retflag = 0;
+
+		/* Returning non-zero return calls the
+		   engine's Parse routine again.
+		*/
+		if (ret)
+		{
+			parse_called_twice = true;
+			Parse();
+		}
+#ifdef DEBUG_PARSER
+		if (ret)
+			Printout("CallLibraryParse() returned true");
+		else
+			Printout("CallLibraryParse() returned false");
+#endif
+	}
+}
+
+int Hugo::DomainObj(int obj) {
+	int yes = false;
+
+	if (obj != var[actor])
+	{
+		switch (domain)
+		{
+			case 0:
+			case -1:
+			{
+				if (Parent(obj)==parse_location)
+					yes = true;
+				else if ((parse_allflag) && GrandParent(obj)==parse_location)
+					yes = true;
+				else
+				{
+					if (Parent(obj)==parse_location && !InList(Parent(obj)))
+						yes = true;
+				}
+
+				if (Peek(grammaraddr)==MULTINOTHELD_T)
+				{
+					if (Parent(obj)==var[actor])
+						yes = false;
+				}
+				break;
+			}
+
+			default:
+			{
+				if (Parent(obj)==domain)
+					yes = true;
+			}
+		}
+	}
+
+	return yes;
+}
+
+unsigned int Hugo::FindWord(char *a) {
+	unsigned int ptr = 0;
+	int i, p, alen;
+
+	if (a[0]=='\0')
+		return 0;
+
+	alen = strlen(a);
+
+	defseg = dicttable;
+
+	for (i=1; i<=dictcount; i++)
+	{
+		if (alen==(p = Peek(ptr+2)) && (unsigned char)(MEM(dicttable*16L+ptr+3)-CHAR_TRANSLATION)==(unsigned char)a[0])
+		{
+			if (!strcmp(GetString(ptr + 2), a))
+			{
+				defseg = gameseg;
+				return ptr;
+			}
+		}
+
+		ptr += (p + 1);
+	}
+	
+	/* As a last resort, see if the first 6 characters of the word (if it
+	   has at least six characters) match a dictionary word:
+	*/
+	if (alen >= 6)
+	{
+		unsigned int possible = 0;
+		int posscount = 0;
+
+		ptr = 0;
+	
+		for (i=1; i<=dictcount; i++)
+		{
+			if (alen<=(p = Peek(ptr+2)) && (unsigned char)MEM(dicttable*16L+ptr+3)-CHAR_TRANSLATION==a[0])
+			{
+				if (!strncmp(GetString(ptr + 2), a, alen))
+				{
+					/* As long as the dictionary word 
+					   doesn't contain a space */
+					if (!strrchr(GetString(ptr+2), ' '))
+					{
+						possible = ptr;
+						posscount++;
+					}
+				}
+			}
+			ptr += (p + 1);
+		}
+		
+		if (posscount==1)
+			return possible;
+	}
+
+	defseg = gameseg;
+
+	return UNKNOWN_WORD;                    /* not found */
+}
+
+int Hugo::InList(int obj) {
+	int i;
+
+	for (i=0; i<objcount; i++)
+	{
+		if (objlist[i]==obj)
+			return true;
+	}
+	return false;
+}
+
+void Hugo::KillWord(int a) {
+	int i;
+
+	if (a>words)
+		return;
+
+	for (i=a; i<words; i++)
+		word[i] = word[i+1];
+	word[words] = "";
+
+	RemoveWord(a);
+	words--;
+}
+
+int Hugo::MatchCommand() {
+	int i, j, flag, a, mw = 0, gotspeaker = 0;
+	int wordnum;
+	int numverbs = 0;
+	bool nextverb = false;
+	unsigned int ptr, verbptr, nextgrammar;
+	unsigned int obj, propaddr;
+
+#ifdef DEBUG_PARSER
+	Printout("Entering MatchCommand()");
+#endif
+
+	odomain = 0;
+	
+	/* Reset these for command-matching */
+	if (!speaking)
+	{
+		var[actor] = var[player];
+		parse_location = var[location];
+	}
+	else
+	{
+		var[actor] = speaking;
+		parse_location = GrandParent(speaking);
+	}
+
+	if (!strcmp(word[1], "~oops"))
+	{
+		strcpy(parseerr, "");
+
+		/* "oops" on its own */
+		if (words==1 || !strcmp(oops, ""))
+		{
+			ParseError(16, 0);       /* "You'll have to make a mistake..." */
+			return 0;
+		}
+
+		/* trying to correct more than one word */
+		if (words > 2 || oopscount)
+		{
+			ParseError(17, 0);       /* "...one word at a time..." */
+			return 0;
+		}
+
+		/* trying to correct a correction */
+		if (!strcmp(Left(errbuf, 5), "~oops"))
+		{
+			ParseError(13, 0);
+			return 0;
+		}
+
+		/* Rebuild the corrected buffer */
+		oopscount = 1;
+		strcpy(line, word[2]);
+		for (i=1; i<=(int)strlen(errbuf); i++)
+		{
+			if (!strcmp(Mid(errbuf, i, strlen(oops)), oops))
+				break;
+		}
+
+		strcpy(buffer, errbuf);
+		buffer[i-1] = '\0';
+		strcat(buffer, line);
+
+		strcat(buffer, Right(errbuf, strlen(errbuf) - i - strlen(oops) + 1));
+
+		SeparateWords();
+		if (!Parse())
+			return 0;
+
+		CallLibraryParse();
+	}
+
+	if (word[1][0]=='.') KillWord(1);
+
+
+	/*
+	 * STEP 1:  Match verb
+	 *
+	 */
+
+	ptr = 64;
+
+MatchVerb:
+
+#ifdef DEBUG_PARSER
+	Printout("MatchCommand(): Step 1");
+#endif
+	if (words==0)
+		return 0;
+
+	defseg = gameseg;
+
+	grammaraddr = 0;
+	domain = 0;
+	obj_match_state = 0;
+	xverb = 0;
+	starts_with_verb = 0;
+	objcount = 0;
+	parse_allflag = false;
+	objstart = 0;
+	object_is_number = false;
+
+	for (i=1; i<MAXWORDS; i++)
+		objword_cache[i] = 0;
+
+	var[object] = 0;
+	var[xobject] = 0;
+	var[self] = 0;
+	var[verbroutine] = 0;
+
+	while ((a = Peek(ptr)) != 255)
+	{
+		defseg = gameseg;
+
+		/* verb or xverb header */
+		if (a==VERB_T || a==XVERB_T)
+		{
+			/* Skim through 1 or more verb words */
+			numverbs = Peek(ptr + 1);
+			verbptr = ptr + 2;
+			for (i=1; i<=numverbs; i++)
+			{
+				/* 0xffff signals something other than a 
+				   dictionary word--see BuildVerb() in hcbuild.c.
+				   This will be the case, like, 1% of the time.
+				*/
+
+				/* If it is a dictionary word... */
+				if (PeekWord(verbptr)!=0xffff)
+				{
+					/* If one of the verb words matches the first
+					   word in the input line
+					*/
+					if (wd[1]==PeekWord(verbptr))
+					{
+						grammaraddr = ptr;
+						goto GotVerb;
+					}
+
+					verbptr += 2;
+				}
+
+				/* ...otherwise assume it's an object (value) */
+				else
+				{
+					codeptr = verbptr + 1; /* skip 0xffff */
+
+					/* GetVal(), not GetValue(), since it's
+					   always a simple value
+					*/
+					obj = GetVal();
+
+					/* codeptr can't be >65535 on a 16-bit
+					   compiler
+					*/
+					verbptr = (unsigned int)codeptr;
+					propaddr = PropAddr(obj, noun, 0);
+					if (propaddr)
+					{
+						defseg = proptable;
+						a = Peek(propaddr+1);	/* obj.#prop */
+						defseg = gameseg;
+
+						for (j=1; j<=a; j++)
+						{
+							if (wd[1]==(unsigned)GetProp(obj, noun, j, 0))
+							{
+								grammaraddr = ptr;
+								goto GotVerb;
+							}
+						}
+					}
+				}
+			}
+
+			/* Otherwise skip over this verb header */
+			ptr += 2 + numverbs * 2;
+		}
+
+		/* anything else */
+		else
+			ptr += Peek(ptr + 1) + 1;
+	}
+
+
+	/*
+	 * STEP 2:  Match object/character (if no verb match)
+	 *
+	 */
+#ifdef DEBUG_PARSER
+	Printout("MatchCommand(): Step 2");
+#endif
+	/* If we hit the end of the grammar without finding a verb match: */
+	if (Peek(ptr)==255)
+	{
+		/* If we already tried this once */
+		if (gotspeaker)
+		{
+			if (!starts_with_verb)
+				/* "Better start with a verb..." */
+				ParseError(2, 0);
+			else
+				/* "That doesn't make any sense..." */
+				ParseError(6, 0);
+			return 0;
+		}
+
+		/* See if the command begins with an object
+		   (character) name:
+		*/
+		flag = 0;
+		if (AnyObjWord(1)==1)
+			flag = 1;
+
+		/* No match, ergo an invalid command */
+		if (flag==0 && nextverb==true)
+		{
+			strcpy(parseerr, "");
+			ParseError(6, 0);        /* "...doesn't make any sense..." */
+			return 0;
+		}
+
+		/* No provision made for addressing objects (characters) */
+		if (flag==0 || speaktoaddr==0)
+		{
+			strcpy(parseerr, "");
+			ParseError(2, 0);        /* "Better start with a verb..." */
+			return 0;
+		}
+
+		/* Count how many object words there are */
+		for (i=2; i<=words; i++)
+		{
+			if (AnyObjWord(i)!=1)
+				break;
+		}
+
+		/* Try to match the first word to a valid object */
+		objfinish = i - 1;
+		obj_match_state = 5;
+		i = 1;
+		recursive_call = 0;
+		
+		if (MatchObject(&i) != true)
+			return 0;  /* unsuccessful */
+		
+		speaking = pobj;                        /* successful */
+		gotspeaker = true;
+
+		/* So erase the object name from the start of the line */
+		for (i=1; i<=objfinish; i++)
+			KillWord(1);
+		if (word[1][0]=='~') KillWord(1);
+		
+		/* If it's a name and that's all...*/
+		if (words==0)
+			return true;
+
+		/* ...or else proceed as usual */
+		ptr = 64;
+		goto MatchVerb;
+	}
+	else if (!gotspeaker)
+		speaking = 0;
+
+GotVerb:
+
+	if (!speaking)
+	{
+		var[actor] = var[player];
+		parse_location = var[location];
+	}
+	else
+	{
+		var[actor] = speaking;
+		parse_location = GrandParent(speaking);
+	}
+
+	obj_match_state = 0;
+	starts_with_verb = 1;
+	strcpy(parseerr, word[1]);
+
+	if (Peek(grammaraddr)==XVERB_T) xverb = true;
+	grammaraddr += 2 + numverbs * 2;
+
+	/*
+	 * STEP 3:  Match proper grammar structure (syntax)
+	 *
+	 */
+#ifdef DEBUG_PARSER
+	Printout("MatchCommand(): Step 3");
+#endif
+
+	/*
+	 * (STEP 4:  We'll be matching xobject, if any, before object)
+	 *
+	 */
+#ifdef DEBUG_PARSER
+	Printout("MatchCommand(): Step 4");
+#endif
+
+	/* Loop until end of grammar table, or next verb:
+	 */
+	while (Peek(grammaraddr)!=255 &&
+		Peek(grammaraddr)!=VERB_T && Peek(grammaraddr)!=XVERB_T)
+	{
+		wordnum = 1;
+
+		nextgrammar = grammaraddr + Peek(grammaraddr + 1) + 1;
+
+		/* Loop until end of table or next verb: */
+		while (Peek(grammaraddr) != 255 && Peek(grammaraddr) != VERB_T && Peek(grammaraddr) != XVERB_T)
+		{
+			mw = MatchWord(&wordnum);
+
+			if (mw==1)
+			{
+				/* end of both input and grammar */
+				if (wd[wordnum]==0 && Peek(grammaraddr)==ROUTINE_T)
+				{
+					full_buffer = (char)wordnum;
+					break;
+				}
+
+				/* end of grammar, not input */
+				if (Peek(grammaraddr)==ROUTINE_T)
+				{
+					mw = false;
+					goto NextStructure;
+				}
+			}
+			else
+			{
+				/* If error already signalled */
+				if (mw==2)
+					return 0;
+
+				/* No match, so try next structure */
+				else
+				{
+NextStructure:
+					grammaraddr = nextgrammar;
+					var[object] = 0;
+					var[xobject] = 0;
+					var[verbroutine] = 0;
+					domain = 0;
+					odomain = 0;
+					obj_match_state = 0;
+					xverb = 0;
+					objcount = 0;
+					objstart = 0;
+					break;
+				}
+			}
+		}
+
+		/* Matched the complete syntax of a verb */
+		if (mw==1)
+		{
+			var[verbroutine] = PeekWord(grammaraddr + 1);
+			break;
+		}
+	}
+
+	if (mw != 1)
+	{
+		if (mw==0)	/* mw = 2 if error already printed */
+		{
+			/* If there's more grammar to check... */
+			if (Peek(grammaraddr) != 255)
+			{
+				ptr = grammaraddr;
+				nextverb = true;
+				goto MatchVerb;
+			}
+
+			/* ...or if we reached the end without a sensible
+			   syntax matched:
+			*/
+			strcpy(parseerr, "");
+
+			/* "...doesn't make any sense..." */
+			ParseError(6, 0);
+		}
+		return 0;
+	}
+
+
+	/*
+	 * STEP 5:  Match remaining object(s), if any
+	 *
+	 */
+#ifdef DEBUG_PARSER
+	Printout("MatchCommand(): Step 5");
+#endif
+
+	/* If there are objects waiting to be loaded into objlist[] */
+	if (objstart)
+	{
+		ResetFindObject();
+
+		obj_match_state = 2;
+		recursive_call = false;
+		if (odomain) domain = odomain;
+
+		grammaraddr = objgrammar;
+		i = objstart;
+
+		/* If there was a problem matching them */
+		if (MatchObject(&i)==false)
+			return 0;
+	}
+
+	/* Got a successfully matched verb with all the requisite
+	   parameters (if any).
+	*/
+
+	if (words < wordnum)
+		remaining = 0;
+	else
+		remaining = (char)(words - wordnum);
+
+#ifdef DEBUG_PARSER
+	Printout("MatchCommand(): Leaving");
+#endif
+	return true;
+}
+
+bool Hugo::MatchObject(int *wordnum) {
+	char found_noun = false;
+	int i, j, k, m, flag;
+	int mobjs; unsigned int mobj[MAX_MOBJ];
+	bool allmatch;
+	int roomloc;
+	int bestobj = 0;
+	char bestavail = 0;
+
+	/* If this is a recursive call, we're not adding new objects */
+	if (!recursive_call) addflag = true;
+
+	stack_depth = 0;
+
+	pobj = 0;                          /* possible object */
+	objcount = 0;                      /* of objlist[] */
+	pobjcount = 0;                     /* of pobjlist[] */
+	mobjs = 0;                         /* # of previous words in phrase */
+	bestavail = 0;                     /* adjective or noun */
+
+#ifdef DEBUG_PARSER
+	Printout("MatchObject(): Entering");
+#endif
+	strcpy(parseerr, "");
+
+	do                                 /* starting at word #a */
+	{
+		/* Check first to make sure it's not a housekeeping word
+		   such as "~and" or "~all".
+		*/
+		if (word[*wordnum][0]!='~' && word[*wordnum][0]!='\0')
+		{
+			if (parseerr[0]!='\0') strcat(parseerr, " ");
+			strcat(parseerr, word[*wordnum]);
+
+			flag = 0;
+			for (i=0; i<objects; i++)
+			{
+				if (wd[*wordnum]==0)
+					break;
+
+				/* Might be this object if wd[*wordnum] is an
+				   adjective or noun of object i
+				*/
+				m = ObjWord(i, wd[*wordnum]);
+
+				if (m)
+				{
+					flag = true;
+					allmatch = true;
+
+					/* check previously matched words */
+					for (j=1; j<=mobjs; j++)
+					{
+						if (!ObjWord(i, mobj[j])) /* || wd[*wordnum]==mobj[j]) */
+							allmatch = false;
+					}
+
+					/* matches all previous words */
+					if (allmatch==true)
+					{
+						AddPossibleObject(i, (char)m, wd[*wordnum]);
+						pobj = i;
+						if (bestavail==0 || bestavail>=(char)m)
+						{
+							if (!bestobj)
+								bestobj = i;
+							bestavail = (char)m;
+						}
+					}
+
+					/* doesn't match previous words */
+					else
+						SubtractPossibleObject(i);
+				}
+
+				/* definitely not this object */
+				else
+					SubtractPossibleObject(i);
+			}
+
+
+			/* If checking the start of an input line, i.e. for
+			   a command addressed to an object (character):
+			*/
+			if (obj_match_state==5 && !flag) goto Clarify;
+		}
+
+		else if (!strcmp(word[*wordnum], "~any"))
+			goto NextLoop;
+
+		/* "~and", "~all",... */
+		else
+			goto Clarify;
+
+		/* Didn't get any suspects */
+		if (pobjcount==0)
+		{
+			/* If "~and", "~all",... */
+			if (word[*wordnum][0]=='~')
+			{
+				/* If checking the xobject */
+				if (obj_match_state==1)
+				{
+					strcpy(parseerr, word[1]);
+					/* "...can't use multiple objects..."
+					   (as indirect objects) */
+					ParseError(7, 0);
+					return false;
+				}
+				goto Clarify;
+			}
+
+			/* Got an unmatchable sequence of words */
+			else
+			{
+				if (obj_match_state==5)
+				{
+					if (!starts_with_verb)
+						/* "Better start with a verb..." */
+						ParseError(2, 0);
+					else
+						/* "That doesn't make any sense..." */
+						ParseError(6, 0);
+				}
+				else
+					/* "(no such thing)..." */
+					ParseError(5, 0);
+				return false;
+			}
+		}
+
+		if (word[*wordnum][0]!='~')
+		{
+			/* Go back for next word in this object phrase */
+
+			mobjs++;
+			if (mobjs==MAX_MOBJ)
+			{
+				/* "(no such thing)..." */
+				ParseError(5, 0);
+				return false;
+			}
+			mobj[mobjs] = wd[*wordnum];
+		}
+		else
+		{
+			/* Since hitting "~and" or "~all", we've obviously
+			   finished an object phrase
+			*/
+			(*wordnum)++;
+			goto Clarify;
+		}
+
+NextLoop:
+		(*wordnum)++;                   /* next word */
+		if ((*wordnum > words || word[*wordnum][0]=='\0')
+			|| *wordnum > objfinish)
+			goto Clarify;
+	}
+	while (true);                           /* endless loop */
+
+
+Clarify:
+
+#ifdef DEBUG_PARSER
+	Printout("MatchObject(): Clarify");
+#endif
+	/* If "~and", "~all",... */
+	if (word[*wordnum][0]=='~')
+	{
+		/* If checking the xobject or addressing a command */
+		if (obj_match_state==1 || speaking)
+		{
+			strcpy(parseerr, word[1]);
+			/* "...can't use multiple objects..."
+			   (as indirect objects) */
+			ParseError(7, 0);
+			return false;
+		}
+	}
+
+	if (!strcmp(word[*wordnum], "~all"))    /* if "~all" is specified */
+	{
+		parse_allflag = true;
+		
+		/* If one or more words were already matched, however... */
+
+		if (mobjs > 0)
+		{
+			ParseError(6, 0);       /* "...doesn't make any sense..." */
+			return false;
+		}
+
+		if (!domain)    /* no particular domain object specified */
+			roomloc = parse_location;
+		else
+			roomloc = domain;
+
+		AddAllObjects(roomloc);
+
+		(*wordnum)++;
+
+
+		/* Done processing the object phrase yet? */
+
+		/* only >GET ALL EXCEPT... if we're not done the object phrase */
+		if (*wordnum<=objfinish && strcmp(word[*wordnum], "~except"))
+		{
+			ParseError(6, 0);   /* "Doesn't make any sense..." */
+			return false;
+		}
+
+		if ((*wordnum > words || word[*wordnum][0]=='\0')
+			|| (obj_match_state != 1 && *wordnum >= objfinish))
+		{
+			if (!objcount && !speaking)
+			{
+				strcpy(parseerr, word[1]);
+				ParseError(9, 0);   /* "Nothing to (verb)..." */
+				return false;
+			}
+			return true;
+		}
+
+		/* Go back for the next piece of the phrase */
+		pobjcount = 0;
+		(*wordnum)--;
+		goto NextLoop;
+	}
+
+
+	/* If we have a possible object or set of objects, go through the
+	   disqualification process, either to sort out any confusion, or
+	   even if there's only one possible object, to make sure it's
+	   available
+	*/
+	if (pobjcount >= 1)
+	{
+		bestavail = 0;
+
+		for (k=0; k<pobjcount; k++)	/* disqualify if unavailable */
+		{
+			i = pobjlist[k].obj;
+
+			/* held or multiheld */
+			if ((domain) && domain==var[actor])
+			{
+				if (Parent(i) != var[actor])
+				{
+					SubtractPossibleObject(i);
+					k--;
+				}
+				else
+					pobj = i;
+			}
+
+			else if ((Peek(grammaraddr)==NOTHELD_T || Peek(grammaraddr)==MULTINOTHELD_T) 
+				&& Parent(i)==var[actor])
+			{
+				SubtractPossibleObject(i);
+				k--;
+
+				/* if this was the last suspect */
+				if (pobjcount<1)  /* i.e., 0 */
+				{
+					ParseError(11, i);  /* "You don't see that..." */
+					return false;
+				}
+			}
+
+			/* otherwise */
+			else if (Available(i, 0)==false) /* and obj_match_state!=5) */
+			{
+				SubtractPossibleObject(i);
+				k--;
+			}
+			else
+				pobj = i;
+
+			/* Try to determine the best available object */
+			if ((!bestavail) || (bestavail==(char)adjective && pobjlist[k].type==(char)noun))
+			{
+				m = domain;
+				/* Temporary parent domain */
+				domain = -1;
+				if (Available(i, 0))
+				{
+					bestavail = pobjlist[k].type;
+					bestobj = i;
+				}
+				domain = m;
+			}
+
+			/* Pick a default (poor) best match */
+			if (!bestobj)
+				bestobj = i;
+		}
+
+
+		/* Disqualify if an object is less exact--i.e., if the
+		   word is a noun for one object but only an adjective for
+		   another, disqualify the one for which it is an
+		   adjective.
+		*/
+		if (pobjcount > 1)
+		{
+			for (i=0; i<pobjcount; i++)
+			{
+				if (pobjlist[i].type==(char)noun)
+					found_noun = true;
+			}
+
+			if (found_noun)
+			{
+				for (i=0; i<pobjcount; i++)
+				{
+					/* Use ObjWord()'s test to see if this word
+					   should get upgraded from an adjective to a noun
+					*/
+					if (pobjlist[i].type==(char)adjective)
+					{
+						if (ObjWordType(pobjlist[i].obj, wd[*wordnum-1], noun))
+							pobjlist[i].type = (char)noun;
+					}
+
+					if (pobjlist[i].type==(char)adjective)
+					{
+						SubtractPossibleObject(pobjlist[i].obj);
+						i--;
+					}
+
+					if (pobjcount==1) break;
+				}
+			}
+		}
+
+
+		/* If we're dealing with an 'anything' object, prefer an
+		   object that's currently available to one that isn't
+		*/
+		if (pobjcount > 1 && Peek(grammaraddr)==ANYTHING_T)
+		{
+			for (i=0; i<pobjcount; i++)
+			{
+				if (Available(pobjlist[i].obj, true))
+				{
+					for (j=0; j<pobjcount; j++)
+					{
+						if (j!=i)
+						{
+							if (!Available(pobjlist[j].obj, true))
+							{
+								SubtractPossibleObject(pobjlist[j].obj);
+								j--;	/* don't skip one */
+								if (pobjcount<=1) break;
+							}
+						}
+					}
+				}
+				if (pobjcount<=1) break;
+			}
+		}
+
+
+		/* Use whatever disambiguation the FindObject routine might provide */
+		if (pobjcount > 1 && findobjectaddr)
+		{
+			for (k=0; k<pobjcount; k++)
+			{
+				i = pobjlist[k].obj;
+				ret = 0;
+				passlocal[0] = i;
+				passlocal[1] = 0;
+				PassLocals(2);
+
+				SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+#if defined (DEBUGGER)
+				DebugRunRoutine((long)findobjectaddr*address_scale);
+#else
+				RunRoutine((long)findobjectaddr*address_scale);
+#endif
+				retflag = 0;
+
+				if (ret==0)     /* returned false */
+				{
+					SubtractPossibleObject(i);
+					k--;	/* so we don't skip one */
+					if (pobjcount<=1) break;
+				}
+				else
+				{
+					bestobj = i;
+					pobj = i;
+				}
+
+				if (pobjcount==1) break;
+			}
+		}
+
+
+		/* If this is a clarification-required call to MatchObject(),
+		   return true if more than one possible object is found.
+		*/
+		if (pobjcount > 1 && recursive_call)
+			return true;
+
+
+		/* On the other hand, if we've managed to disqualify
+		   everything:
+		*/
+		if (pobjcount==0 && !speaking)
+		{
+			if (obj_match_state==5)
+			{
+				if (!starts_with_verb)
+					/* "Better start with a verb..." */
+					ParseError(2, 0);
+				else
+					/* "That doesn't make any sense..." */
+					ParseError(6, 0);
+			}
+			else
+			{
+				if ((!domain) || domain != var[actor])
+				{
+					if (Peek(grammaraddr)==ANYTHING_T)
+						/* "...haven't seen..." */
+						ParseError(10, bestobj);
+					else if (domain)
+						/* "...don't see that there..." */
+						ParseError(14, bestobj);
+					else
+						/* "...don't see any..." */
+						ParseError(11, bestobj);
+				}
+				else
+					ParseError(15, bestobj);  /* "...don't have any..." */
+			}
+			return false;
+		}
+		else if (pobjcount==0)
+			pobj = bestobj;
+
+
+		/* If, after all this, there still exists some confusion
+		   about what object is meant:
+		*/
+		if (pobjcount > 1)
+		{
+			int wtemp;
+			unsigned int wdtemp[MAXWORDS+1];
+			int tempobjlist[MAXWORDS+1], tempobjcount;
+			struct pobject_structure temppobjlist[MAXPOBJECTS];
+			int tempobjfinish;
+			int temppobjcount;
+			bool tempaddflag;
+
+			char tempxverb = xverb;
+			ParseError(8, 0);      /* "Which...do you mean..." */
+			xverb = tempxverb;
+
+			for (i=1; i<=words; i++)      /* save word arrays */
+				wdtemp[i] = wd[i];
+			wtemp = words;
+			tempobjfinish = objfinish;
+
+			for (i=0; i<=objcount; i++)   /* save object arrays */
+				tempobjlist[i] = objlist[i];
+			for (i=0; i<=pobjcount; i++)
+				temppobjlist[i] = pobjlist[i];
+
+			tempobjcount = objcount;
+			temppobjcount = pobjcount;
+			tempaddflag = addflag;
+
+
+			/* Get a new input and properly parse it; after
+			   all, it may be returned to MatchCommand() as
+			   a potentially valid command.
+			*/
+			GetCommand();
+			SeparateWords();
+			if (words==0)
+			{
+				ParseError(0, 1);
+				return false;
+			}
+			if (!Parse())
+				return false;
+
+			objfinish = words;
+
+			/* Do we not care?  i.e. is "any", "either", etc. given? */
+			if (!strcmp(word[1], "~any") && words==1)
+			{
+				for (k=0; k<pobjcount; k++)
+				{
+					i = pobjlist[k].obj;
+
+					if (strcmp(Name(i), ""))
+					{
+						pobj = i;
+						sprintf(line, "(%s)", Name(i));
+						AP(line);
+						goto RestoreTempArrays;
+					}
+				}
+			}
+
+			/* Check to see if any object is disqualified by
+			   any one of the words.
+			*/
+			for (i=1; i<=words; i++)
+			{
+				if (word[i][0]!='~')
+				{
+					for (j=0; j<pobjcount; j++)
+					{
+						if (!ObjWord(pobjlist[j].obj, wd[i]))
+						{
+							SubtractPossibleObject(pobjlist[j].obj);
+							j--;
+						}
+					}
+				}
+			}
+			if (pobjcount==1)
+			{
+				pobj = pobjlist[0].obj;
+				full_buffer = false;
+				goto RestoreTempArrays;
+			}
+
+			/* Check to see if the first word is a noun
+			   or adjective for any of the possible suspect objects.
+			*/
+			flag = 0;
+			for (i=0; i<pobjcount; i++)
+			{
+				if (ObjWord(pobjlist[i].obj, wd[1]))
+				{
+					flag = 1;
+					break;
+				}
+			}
+			if (!strcmp(word[1], "~all"))
+				flag = 1;
+
+			/* If not, tell MatchCommand() that there's a 
+			   new command coming down the hopper.
+			*/
+			if (flag==0)
+			{
+				full_buffer = true;
+				return false;
+			}
+
+			/* Here, tell MatchObject()--this function--
+			   that this isn't a virgin call.
+			*/
+			recursive_call = true;
+			addflag = true;
+			i = 1;
+			j = MatchObject(&i);
+			if (j==false)	/* parsing error */
+			{
+				full_buffer = false;
+				return false;
+			}
+			else if (j==-1)	/* multiple matches found */
+			{
+				/* See if a single entered word matches
+				   exactly the name of only one object
+				*/
+				if (words==1)
+				{
+					flag = 0;
+					for (j=0; j<temppobjcount; j++)
+					{
+						if (wd[1]==(unsigned int)GetProp(temppobjlist[j].obj, 0, 1, 0))
+						{
+							pobj = temppobjlist[j].obj;
+							flag++;
+						}
+					}
+					if (flag==1)
+					{
+						full_buffer = false;
+						goto RestoreTempArrays;
+					}
+				}
+
+				/* Now, even if we weren't able to find a
+				   match, check to see if the last word
+				   belongs to only one of the possible
+				   suspects, especially if it's a noun.
+				*/
+				flag = 0;
+				bestobj = 0;
+				i = words;
+				while (wd[i]==0) i--;
+				for (j=0; j<temppobjcount; j++)
+				{
+					if ((m = ObjWord(temppobjlist[j].obj, wd[i])) != 0)
+					{
+						if (!bestobj || m<=bestobj)
+						{
+							flag++;
+							bestobj = m;
+							pobj = temppobjlist[j].obj;
+						}
+					}
+				}
+				if (flag != 1)
+				{
+					/* "You'll have to be more specific..." */
+					ParseError(13, 0);
+					full_buffer = false;
+					return false;
+				}
+				full_buffer = false;
+			}
+
+RestoreTempArrays:
+		/* Rebuild <buffer> and word[] array */
+			strcpy(buffer, "");
+			for (i=1; i<=wtemp; i++)
+			{
+				strcat(buffer, GetWord(wdtemp[i]));
+				strcat(buffer, " ");
+			}
+			SeparateWords();
+
+		/* Restore wd[] array after SeparateWords() */
+			for (i=1; i<=wtemp; i++)
+				wd[i] = wdtemp[i];
+			words = wtemp;
+			objfinish = tempobjfinish;
+
+		/* Restore object lists */
+			for (i=0; i<temppobjcount; i++)
+				pobjlist[i] = temppobjlist[i];
+			pobjcount = temppobjcount;
+
+			/* Multiple disambig. results for a non-multi verb? */
+			i = Peek(grammaraddr);
+			if (objcount>1 && i!=MULTI_T && i!=MULTIHELD_T && i!=MULTINOTHELD_T)
+			{
+				strcpy(parseerr, word[1]);
+				/* "You can't...multiple objects." */
+				ParseError(3, 0);
+				return false;
+			}
+
+			/* Check objlist against original pobjlist */
+			for (i=0; i<objcount; i++)
+			{
+				flag = 0;
+				for (j=0; j<pobjcount; j++)
+				{
+					if (pobjlist[j].obj==objlist[i])
+						flag = true;
+				}
+				if (!flag)
+				{
+					/* "You'll have to be more specific..." */
+					ParseError(13, 0);
+					full_buffer = false;
+					return false;
+				}
+			}
+
+			/* Check pobjlist against disambiguation objlist */
+			for (i=0; i<pobjcount; i++)
+			{
+				flag = 0;
+				for (j=0; j<objcount; j++)
+				{
+					if (pobjlist[i].obj==objlist[j])
+						flag = true;
+				}
+				if (!flag)
+				{
+					SubtractPossibleObject(pobjlist[i].obj);
+					i--;
+				}
+			}
+
+			for (i=0; i<=tempobjcount; i++)
+				objlist[i] = tempobjlist[i];
+			objcount = (char)tempobjcount;
+			addflag = (char)tempaddflag;
+
+			if (word[*wordnum][0]=='~') (*wordnum)--;
+		}
+	}
+
+	/* Rule out "noun noun" and "noun adjective" combinations */
+
+	i = 0;                        /* count nouns */
+	k = 10;                       /* best (i.e., lowest) match */
+	for (j=1; j<=mobjs; j++)
+	{
+		if ((m = ObjWord(pobj, mobj[j]))==noun)
+		{
+			k = noun;
+			i++;
+		}
+		else if (k==noun) i = 2;
+	}
+
+	/* Can't use more than one noun for a given object */
+	if (i > 1)
+	{
+		/* "...no such thing" */
+		ParseError(5, 0);
+		return false;
+	}
+
+	/* Check finally to make sure it's valid */
+	ResetFindObject();
+	if (!ValidObj(pobj))
+		return false;
+
+	/* Finally--now add it or subtract it from the list, as
+	   appropriate:
+	*/
+	if (addflag==true)
+	{
+		if (pobjcount)
+		{
+			for (i=0; i<pobjcount; i++)
+				AddObj(pobjlist[i].obj);
+		}
+
+		/* backup */
+		else if (pobj != 0)
+			AddObj(pobj);
+	}
+	else
+		SubtractObj(pobj);
+
+	/* If the object wasn't where it was specifically claimed to be,
+	   applies to second pass through object phrase(s) after xobject
+	   is found (since <obj_match_state> is 2):
+	*/
+	if (obj_match_state==2 && domain != 0 &&
+		Parent(pobj) != domain && pobj != 0 && !speaking)
+	{
+		if (domain==var[player])
+			ParseError(15, pobj);    /* "You don't have any..." */
+		else
+			ParseError(14, pobj);    /* "You don't see any...there..." */
+		return false;
+	}
+
+	if (!strcmp(word[*wordnum], "~except"))
+	{
+		if (obj_match_state==1)
+		{
+			ParseError(7, 0);	/* can't have multiple xobjects */
+			return false;
+		}
+		addflag = false;
+	}
+
+	if ((strcmp(word[*wordnum], "~and") && strcmp(word[*wordnum], "~except"))
+		|| obj_match_state==5)
+	{
+		/* At the end yet? */
+		if ((*wordnum > words || word[*wordnum][0]=='\0')
+			|| *wordnum > objfinish)
+		{
+			if ((objcount > 0 && pobj != 0) || recursive_call || speaking)
+				return true;
+			else
+			{
+				/* No objects found */
+				strcpy(parseerr, word[1]);
+				ParseError(9, 0);   /* "Nothing to (verb)..." */
+				return false;
+			}
+		}
+	}
+
+	/* Go back for the next object phrase */
+	pobjcount = 0;
+	mobjs = 0;
+	strcpy(parseerr, "");
+
+	goto NextLoop;
+}
+
+int Hugo::MatchWord(int *wordnum) {
+	char num[18];
+	int i, p, t, flag, finish;
+	unsigned int thissyntax, nextsyntax;
+
+	if (wd[*wordnum]==0)
+		return 0;
+
+	switch ((t = Peek(grammaraddr)))
+	{
+		/* the verb ("*") */
+		case ASTERISK_T:
+			(*wordnum)++;
+			AdvanceGrammar();
+			return 1;
+
+		/* a non-specific dictionary word */
+		case WORD_T:
+			if (obj_match_state==1)
+				var[xobject] = wd[*wordnum];
+			else
+			{
+				var[object] = wd[*wordnum];
+				obj_match_state = 1;
+			}
+			object_is_number = true;
+			(*wordnum)++;
+			AdvanceGrammar();
+			return 1;
+
+		/* a specific dictionary entry */
+		case DICTENTRY_T:
+CheckWord:
+			/* matches word */
+			if (wd[*wordnum]==PeekWord(grammaraddr + 1))
+			{
+				(*wordnum)++;
+				AdvanceGrammar();
+				while (Peek(grammaraddr)==9)
+					grammaraddr += 4;
+				return 1;
+			}
+			else
+			{
+				/* if next word is a "/" */
+				if (Peek(grammaraddr + 3)==FORWARD_SLASH_T)
+				{
+					AdvanceGrammar();       /* this word */
+					AdvanceGrammar();       /* "/" */
+					goto CheckWord;
+				}
+
+				return 0;
+			}
+
+		/* alternative dictionary words */
+		case FORWARD_SLASH_T:
+			grammaraddr++;
+			return 1;
+
+		/* a number */
+		case NUMBER_T:
+			if ((STARTS_AS_NUMBER(word[*wordnum])) &&
+				!strcmp(itoa(atoi(word[*wordnum]), num, 10), word[*wordnum]))
+			{
+				if (obj_match_state==1)
+					var[xobject] = atoi(word[*wordnum]);
+				else
+				{
+					var[object] = atoi(word[*wordnum]);
+					obj_match_state = 1;
+				}
+				object_is_number = true;
+				AdvanceGrammar();
+				(*wordnum)++;
+				return 1;
+			}
+			break;
+
+		/* a string enclosed in quotes */
+		case STRING_T:
+			if (parsestr[0]=='\"')
+			{
+				AdvanceGrammar();
+				(*wordnum)++;
+				return 1;
+			}
+			else
+				return 0;
+
+		default:
+		{
+		
+		/* Some manifestation of an object (or objects) before domain is
+		   found, since <obj_match_state> is initially set to 0:
+		*/
+			if (obj_match_state==0)
+			{
+				if (Peek(grammaraddr)==HELD_T || Peek(grammaraddr)==MULTIHELD_T)
+					odomain = var[actor];
+
+				obj_match_state = 1;    /* since next set of object words
+							   must be the xobject */
+				objstart = *wordnum;
+				objgrammar = grammaraddr;
+
+				while (wd[*wordnum] != 0)
+				{
+					finish = *wordnum;
+
+					/* Check what's coming up in case it's a dictionary
+					   word--which would override an object phrase.
+					*/
+					thissyntax = grammaraddr;
+					AdvanceGrammar();
+					nextsyntax = grammaraddr;
+					grammaraddr = thissyntax;
+
+					/* dictionary word or string */
+CheckWordorString:
+					p = Peek(nextsyntax);
+					if (p==DICTENTRY_T || p==STRING_T)
+					{
+						if ((PeekWord(nextsyntax + 1)==wd[*wordnum]) ||
+							(p==STRING_T && wd[*wordnum]==UNKNOWN_WORD))
+						{
+							grammaraddr = nextsyntax;
+							if (*wordnum != objstart)
+								return 1;
+							else
+								return 0;
+						}
+						else if (Peek(nextsyntax + 3)==FORWARD_SLASH_T)
+						{
+							thissyntax = grammaraddr;
+							grammaraddr = nextsyntax + 3;
+							AdvanceGrammar();
+							nextsyntax = grammaraddr;
+							grammaraddr = thissyntax;
+							
+							goto CheckWordorString;
+						}
+					}
+
+					/* or a number */
+					else if ((p==NUMBER_T && STARTS_AS_NUMBER(word[*wordnum])) &&
+						!strcmp(word[*wordnum], itoa(atoi(word[*wordnum]), num, 10)))
+					{
+						grammaraddr = nextsyntax;
+						if (*wordnum != objstart)
+							return 1;
+						else
+							return 0;
+					}
+
+					/* Pass over any object words--they'll be matched
+					   specifically later in MatchCommand().
+					*/
+					flag = 0;
+					if (AnyObjWord(*wordnum)==1)
+					{
+						(*wordnum)++;
+						flag = 1;
+					}
+
+					/* if "~and", "~all",... */
+					if (word[*wordnum][0]=='~')
+					{
+						int multicheck = Peek(grammaraddr);
+						
+						(*wordnum)++;
+						flag = 1;
+
+						/* multi or multi(something) */
+						if (multicheck != MULTI_T &&
+							multicheck != MULTIHELD_T &&
+							multicheck != MULTINOTHELD_T)
+						{
+							strcpy(parseerr, word[1]);
+							/* "You can't...multiple objects." */
+							ParseError(3, 0);
+							return 2;
+						}
+					}
+
+					objfinish = finish;
+
+					if (flag==0)
+						return 0;
+				}
+
+				AdvanceGrammar();
+				return 1;
+			}
+
+		/* hitting xobject */
+
+			else if (obj_match_state==1)
+			{
+				int temp_objfinish = objfinish;
+
+				/* If we don't know the verbroutine, try to figure it out */
+				if (var[verbroutine]==0)
+				{
+					thissyntax = grammaraddr;
+					AdvanceGrammar();
+					nextsyntax = grammaraddr;
+					grammaraddr = thissyntax;
+					if (Peek(nextsyntax)==ROUTINE_T)
+					{
+						var[verbroutine] = PeekWord(nextsyntax+1);
+					}
+				}
+
+				p = Peek(grammaraddr);
+				
+				/* If the xobject is specifically a parent of the
+				   object(s) to be matched later:
+				*/
+				if (p==PARENT_T) domain = -1;
+
+				/* Also deal with held xobjects */
+				t = domain;
+				if (p==HELD_T || p==MULTIHELD_T)
+					domain = var[actor];
+
+				/* Figure out where this xobject must end, as per grammar */
+				objfinish = -1;
+				if (*wordnum < words)
+				{
+					thissyntax = grammaraddr;
+					AdvanceGrammar();
+					nextsyntax = grammaraddr;
+					grammaraddr = thissyntax;
+
+CheckXobjectFinish:
+					p = Peek(nextsyntax);
+					for (i=*wordnum+1; i<=words; i++)
+					{
+						if (p==DICTENTRY_T || p==STRING_T)
+						{
+							if ((PeekWord(nextsyntax + 1)==wd[i]) ||
+								(p==STRING_T && wd[i]==UNKNOWN_WORD))
+							{
+								objfinish = i-1;
+								break;
+							}
+						}
+						else if (Peek(nextsyntax + 3)==FORWARD_SLASH_T)
+						{
+							thissyntax = grammaraddr;
+							grammaraddr = nextsyntax + 3;
+							AdvanceGrammar();
+							nextsyntax = grammaraddr;
+							grammaraddr = thissyntax;
+						
+							goto CheckXobjectFinish;
+						}
+						else if ((p==NUMBER_T && STARTS_AS_NUMBER(word[*wordnum])) &&
+							!strcmp(word[*wordnum], itoa(atoi(word[*wordnum]), num, 10)))
+						{
+							objfinish = i;
+							break;
+						}
+						else
+							break;
+					}
+
+				}
+
+				if (objfinish==-1) objfinish = words;
+
+				/* Regardless, try to match the xobject */
+				recursive_call = false;
+				if (MatchObject(&(*wordnum))==0)
+				{
+					objfinish = temp_objfinish;
+
+					return 2;
+				}
+				else
+				{
+					objfinish = temp_objfinish;
+
+					domain = 0;
+					if (ValidObj(pobj)==false)
+						return 2;
+					domain = t;
+
+					if (objcount==1)
+						var[xobject] = objlist[0];
+					else
+						var[xobject] = pobj;
+					if (domain==-1) domain = var[xobject]; /* parent */
+					obj_match_state = 2;
+
+					AdvanceGrammar();
+
+					/* Can't have multiple xobjects */
+					if (objcount > 1)
+					{
+						ParseError(7, 0);
+						return 2;
+					}
+					objcount = 0;
+					return 1;
+				}
+			}
+		}
+	}
+	
+	return 0;
+}
+
+int Hugo::ObjWordType(int obj, unsigned int w, int type) {
+	int j, num;
+	unsigned int pa;
+	
+	pa = PropAddr(obj, type, 0);
+	if (pa)
+	{
+		defseg = proptable;
+		num = Peek(pa + 1);
+		
+		if (num==PROP_ROUTINE)
+		{
+			if ((unsigned int)GetProp(obj, type, 1, false)==w)
+			{
+				defseg = gameseg;
+				return true;
+			}
+		}
+		else
+		{
+			for (j=1; j<=num; j++)
+			{
+				if (PeekWord(pa + j * 2)==w)
+				{
+					defseg = gameseg;
+					return true;
+				}
+			}
+		}
+	}
+
+	defseg = gameseg;
+
+	return false;
+}
+
+int Hugo::ObjWord(int obj, unsigned int w) {
+	if ((obj_parselist) && !(obj_parselist[obj/8]&1<<(obj%8)))
+		return 0;
+
+	if (ObjWordType(obj, w, adjective))
+		return adjective;
+	
+	if (ObjWordType(obj, w, noun))
+		return noun;
+		
+	return 0;
+}
+
+int Hugo::Parse() {
+	char foundstring = 0;           /* allow one unknown word/phrase */
+	int notfound_word = 0;
+	int i, j, k, m;
+	char num[33];
+	char tempword[81];
+	unsigned int period, comma;
+	unsigned int synptr;
+
+	period = FindWord(".");
+	comma = FindWord(",");
+
+	strcpy(parsestr, "");           /* for storing any unknown string */
+	parsed_number = 0;              /*  "     "     "  parsed number  */
+
+	for (i=1; i<=words; i++)        /* find dictionary addresses */
+	{
+		if (word[i][0]=='\"' && foundstring==0)
+		{
+			strcpy(parsestr, word[i]);
+			foundstring = 1;
+			wd[i] = UNKNOWN_WORD;
+		}
+		else
+		{
+			wd[i] = FindWord(word[i]);
+
+			/* Numbers -32768 to 32767 are valid...*/
+			if (!strcmp(word[i], itoa(atoi(word[i]), num, 10)))
+			{
+#if !defined (MATH_16BIT)
+				if (atoi(word[i]) > 32767 || atoi(word[i]) < -32768)
+					goto NotinDictionary;
+#endif
+				parsed_number = atoi(word[i]);
+				if (parseerr[0]=='\0')
+					strcpy(parseerr, word[i]);
+			}
+
+			/* Otherwise it must be a dictionary entry */
+			else
+			{
+				/* If it's not in the dictionary */
+				if (wd[i]==UNKNOWN_WORD)
+				{
+NotinDictionary:
+					if (!notfound_word)
+					{
+						strcpy(parseerr, word[i]);
+						strcpy(oops, word[i]);
+
+						notfound_word = i;
+					}
+				}
+			}
+		}
+	}
+
+	/* Return here instead of immediately, so that we have a chance
+	   to load the rest of the recognized words into the word array
+	*/
+	if (notfound_word)
+	{
+		i = notfound_word;
+
+		/* "...can't use the word..." */
+		ParseError(1, 0);
+		strcpy(errbuf, "");
+		for (i=1; i<=words; i++)
+		{
+			strcat(errbuf, word[i]);
+			if (i != words) strcat(errbuf, " ");
+		}
+
+		return 0;
+	}
+
+	wd[words+1] = 0;
+	oopscount = 0;
+
+
+	/* Do synonyms, removals, compounds, punctuation */
+
+	for (i=1; i<=words; i++)                /* Look through words... */
+	{
+		synptr = 2;
+
+		for (j=1; j<=syncount; j++)  /* ...and alterations */
+		{
+			defseg = syntable;
+			if (wd[i]==PeekWord(synptr + 1))
+			{
+				switch (Peek(synptr))
+				{
+					case 0:        /* synonym */
+					{
+						defseg = syntable;
+						wd[i] = PeekWord(synptr + 3);
+						m = strlen(GetWord(wd[i])) - strlen(word[i]);
+						if (m)
+						{
+							if (m + (int)strlen(buffer) > 81)
+								{strcpy(buffer, "");
+								words = 0;
+								ParseError(0, 0);
+								return 0;}
+
+							for (k=words; k>i; k--)
+							{
+								strcpy(tempword, word[k]);
+								word[k] += m;
+								strcpy(word[k], tempword);
+							}
+						}
+						strcpy(word[i], GetWord(wd[i]));
+						i--;
+						break;
+					}
+
+					case 1:        /* removal */
+					{
+						KillWord(i);
+						i--;
+						break;
+					}
+
+					case 2:        /* compound */
+					{
+						if (wd[i+1]==PeekWord(synptr+3))
+						{
+							strcat(word[i], word[i+1]);
+							wd[i] = FindWord(word[i]);
+							KillWord(i+1);
+						}
+						break;
+					}
+				}
+				goto NextSyn;
+			}
+NextSyn:
+			synptr += 5;
+		}
+
+		if (wd[i]==comma)
+		{
+			if (strcmp(word[i+1], "~and"))
+			{
+				word[i] = "~and";
+				wd[i] = FindWord("~and");
+			}
+			else
+				KillWord(i);
+		}
+
+		if (wd[i]==period)
+		{
+			wd[i] = 0;
+			word[i] = "";
+		}
+	}
+
+	defseg = gameseg;
+
+	if (strcmp(word[1], "~oops")) strcpy(oops, "");
+
+	if (words==0)
+	{
+		ParseError(0,0);        /* What? */
+		return false;
+	}
+		
+	return true;
+}
+
+void Hugo::ParseError(int e, int a) {
+	int i, k, count;
+
+	remaining = 0;
+	xverb = true;
+
+	if (e==5 && !strcmp(parseerr, "")) e = 6;
+
+	if (parseerroraddr)
+	{
+		ret = 0;
+		passlocal[0] = e;
+		passlocal[1] = a;
+		PassLocals(2);
+
+		SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+#if defined (DEBUGGER)
+		DebugRunRoutine((long)parseerroraddr*address_scale);
+#else
+		RunRoutine((long)parseerroraddr*address_scale);
+#endif
+		stack_depth = 0;
+		retflag = 0;
+		if (ret)
+		{
+			if (ret==2) reparse_everything = true;
+			return;
+		}
+	}
+
+	switch (e)
+	{
+		case 0:
+			AP("What?");
+			break;
+
+		case 1:
+			sprintf(line, "You can't use the word \"%s\".", parseerr);
+			AP(line);
+			break;
+
+		case 2:
+			AP("Better start with a verb.");
+			break;
+
+		case 3:
+			sprintf(line, "You can't %s multiple objects.", parseerr);
+			AP(line);
+			break;
+
+		case 4:
+			AP("Can't do that.");
+			break;
+
+		case 5:
+			sprintf(line, "You haven't seen any \"%s\", nor are you likely to in the near future even if such a thing exists.", parseerr);
+			AP(line);
+			break;
+
+		case 6:
+			AP("That doesn't make any sense.");
+			break;
+
+		case 7:
+			AP("You can't use multiple objects like that.");
+			break;
+
+		case 8:
+		{
+			sprintf(line, "Which %s do you mean, ", !parse_called_twice?parseerr:"exactly");
+			count = 1;
+			for (k=0; k<pobjcount; k++)
+			{
+				i = pobjlist[k].obj;
+
+				if (strcmp(Name(i), ""))
+				{
+					if (count==pobjcount)
+					{
+						if (count > 2) strcat(line, ",");
+						strcat(line, " or ");
+					}
+					else
+					{
+						if (count != 1)
+							strcat(line, ", ");
+					}
+					if (GetProp(i, article, 1, 0))
+					{
+						const char *w = GetWord(GetProp(i, article, 1, 0));
+						/* Don't use "a" or "an" in listing */
+						/*
+						if (!strcmp(w, "a") || !strcmp(w, "an"))
+							strcat(line, "the ");
+						else
+							sprintf(line+strlen(line), "%s ", w);
+						*/
+						/* We'll just use "the" */
+						if (w) strcat(line, "the ");
+					}
+					strcat(line, Name(i));
+					count++;
+				}
+			}
+			strcat(line, "?");
+			AP(line);
+			break;
+		}
+
+		case 9:
+			sprintf(line, "Nothing to %s.", parseerr);
+			AP(line);
+			break;
+
+		case 10:
+			AP("You haven't seen anything like that.");
+			break;
+
+		case 11:
+			AP("You don't see that.");
+			break;
+
+		case 12:
+			sprintf(line, "You can't do that with the %s.", Name(a));
+			AP(line);
+			break;
+
+		case 13:
+			AP("You'll have to be a little more specific.");
+			break;
+
+		case 14:
+			AP("You don't see that there.");
+			break;
+
+		case 15:
+			AP("You don't have that.");
+			break;
+
+		case 16:
+			AP("You'll have to make a mistake first.");
+			break;
+
+		case 17:
+			AP("You can only correct one word at a time.");
+			break;
+	}
+}
+
+void Hugo::RemoveWord(int a) {
+	if (a > words)
+		return;
+
+	for (; a<words; a++)
+	{
+		wd[a] = wd[a + 1];
+		objword_cache[a] = objword_cache[a + 1];
+	}
+	wd[words] = 0;
+	objword_cache[words] = 0;
+}
+
+void Hugo::ResetFindObject() {
+	if (findobjectaddr)
+	{
+		SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+		PassLocals(0);
+#if defined (DEBUGGER)
+		DebugRunRoutine((long)findobjectaddr*address_scale);
+#else
+		RunRoutine((long)findobjectaddr*address_scale);
+#endif
+		retflag = 0;
+	}
+}
+
+void Hugo::SeparateWords() {
+	char inquote = 0;
+	char a[1025];
+	char b[2];
+	char w1[17], w2[17];            /* for time conversions */
+	char temp[17];
+	short n1, n2;                   /* must be 16 bits */
+	int bloc = 0;                   /* buffer location */
+	int i;
+
+
+	/* First filter the line of any user-specified punctuation */
+	do
+	{
+		i = strcspn(buffer, punc_string);
+		if (buffer[i]) buffer[i] = ' ';
+	} while (buffer[i]);
+
+
+	/* Begin the word-splitting proper: */
+	
+	words = 1;                      /* Setup a blank string */
+
+	for (i=0; i<MAXWORDS+1; i++)
+	{
+		word[i] = "";
+		wd[i] = 0;
+	}
+	word[1] = buffer;
+
+	strcpy(a, buffer);
+	strcpy(buffer, "");
+	
+	for (i=1; i<=(int)strlen(a); i++)
+	{
+		if (inquote!=1 && isascii(a[i-1]))
+			b[0] = (char)tolower(a[i-1]);
+		else b[0] = a[i-1];
+		b[1] = '\0';
+
+		if (b[0]=='\"' && inquote==1)
+		{
+			strcpy(buffer+bloc, b);
+			bloc++;
+			inquote++;
+		}
+
+		if (b[0]=='\"' || ((b[0]==' ' || b[0]=='!' || b[0]=='?') && inquote!=1))
+		{
+			if (word[words][0]!='\0')
+			{
+				bloc++;
+				if (++words > MAXWORDS) words = MAXWORDS;
+				word[words] = buffer + bloc;
+				strcpy(word[words], "");
+			}
+
+			if (b[0]=='\"' && inquote==0)
+			{
+				strcpy(buffer+bloc, b);
+				bloc++;
+				inquote = 1;
+			}
+		}
+		else
+		{
+			if ((b[0]=='.' || b[0]==',') && inquote!=1)
+			{
+				if (word[words][0]!='\0')
+				{
+					bloc++;
+					if (++words > MAXWORDS) words = MAXWORDS;
+				}
+				word[words] = buffer + bloc;
+				strcpy(word[words], b);
+				bloc += strlen(b) + 1;
+				if (++words > MAXWORDS) words = MAXWORDS;
+				word[words] = buffer + bloc;
+				strcpy(word[words], "");
+			}
+			else
+			{
+				strcpy(buffer+bloc, b);
+				bloc++;
+			}
+		}
+	}
+
+	if (!strcmp(word[words], "")) words--;
+
+	for (i=1; i<=words; i++)
+	{
+		/* Convert hours:minutes time to minutes only */
+		if (strcspn(word[i], ":")!=strlen(word[i]) && strlen(word[i])<=5)
+		{
+			strcpy(w1, Left(word[i], strcspn(word[i], ":")));
+			strcpy(w2, Right(word[i], strlen(word[i]) - strcspn(word[i], ":") - 1));
+			n1 = (short)atoi(w1);
+			n2 = (short)atoi(w2);
+
+			if (!strcmp(Left(w2, 1), "0"))
+				strcpy(w2, Right(w2, strlen(w2) - 1));
+
+			/* If this is indeed a hh:mm time, write it back
+			   as the modified word, storing the original hh:mm
+			   in parse$:
+			*/
+			if (!strcmp(w1, itoa((int)n1, temp, 10)) && !strcmp(w2, itoa((int)n2, temp, 10)) && (n1 > 0 && n1 < 25) && (n2 >= 0 && n2 < 60))
+			{
+				strcpy(parseerr, word[i]);
+				itoa(n1 * 60 + n2, word[i], 10);
+			}
+		}
+	}
+}
+
+void Hugo::SubtractObj(int obj) {
+	int i, j;
+
+	for (i=0; i<objcount; i++)
+	{
+		if (objlist[i]==obj)
+		{
+			for (j=i; j<objcount; j++)
+				objlist[j] = objlist[j+1];
+			objcount--;
+			return;
+		}
+	}
+}
+
+void Hugo::SubtractPossibleObject(int obj) {
+	int i, j, last = 0;
+
+	for (i=0; i<pobjcount; i++)
+	{
+		if (pobjlist[i].obj==obj)
+		{
+			if (pobjlist[i].obj==pobj && last!=0) pobj = last;
+
+			for (j=i; j+1<pobjcount; j++)
+			{
+				pobjlist[j] = pobjlist[j+1];
+			}
+			pobjcount--;
+
+#ifdef DEBUG_PARSER
+{
+	char buf[100];
+	sprintf(buf, "SubtractPossibleObject(%d:\"%s\")", obj, Name(obj));
+	Printout(buf);
+}
+#endif
+			return;
+		}
+		else last = pobjlist[i].obj;
+	}
+}
+
+void Hugo::TryObj(int obj) {
+	unsigned int tempdomain;
+
+	if ((obj_parselist) && !(obj_parselist[obj/8]&1<<(obj%8)))
+		return;
+
+	if (DomainObj(obj))
+	{
+		tempdomain = domain;
+		domain = 0;
+
+		if (Available(obj, 0) && !InList(Parent(obj)))
+		{
+			AddObj(obj);
+		}
+		else
+		{
+			SubtractObj(obj);
+		}
+
+		domain = tempdomain;
+	}
+}
+
+int Hugo::ValidObj(int obj) {
+	int attr, nattr = 0;
+	unsigned int addr;
+
+	defseg = gameseg;
+
+	if (!Available(obj, 0) && !speaking &&
+		(Peek(grammaraddr)!=OPEN_BRACKET_T ||
+		Peek(grammaraddr+1)!=ROUTINE_T))
+	{
+		if (Peek(grammaraddr)==ANYTHING_T)
+			ParseError(10, obj);    /* "...haven't seen..." */
+		else
+			ParseError(11, obj);    /* "...don't see any..." */
+		return 0;
+	}
+
+	switch (Peek(grammaraddr))
+	{
+		case OPEN_BRACKET_T:
+		{
+			if (Peek(grammaraddr+1)==ROUTINE_T)
+			{
+				addr = PeekWord(grammaraddr+2);
+				ret = 0;
+				passlocal[0] = obj;
+				PassLocals(1);
+
+				SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+#if defined (DEBUGGER)
+				DebugRunRoutine((long)addr*address_scale);
+#else
+				RunRoutine((long)addr*address_scale);
+#endif
+				retflag = 0;
+
+				/* If the routine doesn't return true,
+				   the object doesn't qualify.
+				*/
+				if (!ret)
+					return (0);
+			}
+			else if (Peek(grammaraddr+1)==OBJECTNUM_T)
+			{
+				if (obj != (int)PeekWord(grammaraddr+2))
+				{
+					strcpy(parseerr, "");
+					if (GetProp(obj, article, 1, 0))
+						strcpy(parseerr, "the ");
+					strcat(parseerr, Name(obj));
+
+					/* "...can't do that with..." */
+					ParseError(12, obj);
+					return 0;
+				}
+			}
+			break;
+		}
+
+		case ATTR_T:
+		case NOT_T:
+		{
+			if (Peek(grammaraddr)==NOT_T) nattr = 1;
+			attr = Peek(grammaraddr + 1 + nattr);
+
+			/* If the attribute match is not made,
+			   the object doesn't qualify.
+			*/
+			if (!TestAttribute(obj, attr, nattr))
+			{
+				strcpy(parseerr, "");
+				if (GetProp(obj, article, 1, 0))
+					strcpy(parseerr, "the ");
+				strcat(parseerr, Name(obj));
+
+				/* "...can't do that with..." */
+				ParseError(12, obj);
+				return 0;
+			}
+			break;
+		}
+	}
+	return true;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index 60d27ae..f7d39a7 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -53,7 +53,7 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 		words(0), parsed_number(0), remaining(0), xverb(0), starts_with_verb(0),
 		grammaraddr(0), obj_parselist(nullptr), domain(0), odomain(0), objcount(0),
 		parse_allflag(false), pobjcount(0), pobj(0), obj_match_state(0), object_is_number(0),
-		objgrammar(0), objstart(0), objfinish(0), addflag(0), speaking(0), oopscount(0),
+		objgrammar(0), objstart(0), objfinish(0), addflag(false), speaking(0), oopscount(0),
 		parse_called_twice(0), reparse_everything(0), full_buffer(false), recursive_call(false),
 		parse_location(0),
 		// herun
@@ -86,7 +86,7 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 	Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
 	Common::fill(&errbuf[0], &errbuf[MAXBUFFER + 1], 0);
 	Common::fill(&line[0], &line[1025], 0);
-	Common::fill(&word[0], &word[MAXWORDS + 1], (const char *)nullptr);
+	Common::fill(&word[0], &word[MAXWORDS + 1], (char *)nullptr);
 	Common::fill(&wd[0], &wd[MAXWORDS + 1], 0);
 	Common::fill(&parseerr[0], &parseerr[MAXBUFFER + 1], '\0');
 	Common::fill(&parsestr[0], &parsestr[MAXBUFFER + 1], '\0');
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index 76983ad..aa4deee 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -144,7 +144,7 @@ private:
 	char line[1025];						///< line buffer
 
 	int words;								///< parsed word count
-	const char *word[MAXWORDS + 1];			///< breakdown into words
+	char *word[MAXWORDS + 1];				///< breakdown into words
 	unsigned int wd[MAXWORDS + 1];			///<     "      "   dict. entries
 	unsigned int parsed_number;				///< needed for numbers in input
 
@@ -168,7 +168,7 @@ private:
 	unsigned int objgrammar;				///< for 2nd pass
 	int objstart;							///<  "   "   "
 	int objfinish;							///<  "   "   "
-	char addflag;							///< true if adding to objlist[]
+	bool addflag;							///< true if adding to objlist[]
 	int speaking;							///< if command is addressed to obj.
 
 	char oops[MAXBUFFER + 1];				///< illegal word
@@ -496,7 +496,7 @@ private:
 	/**
 	 * From the dictionary table.
 	 */
-	const char *GetWord(unsigned int w);
+	char *GetWord(unsigned int w);
 
 	void HandleTailRecursion(long addr);
 
@@ -604,38 +604,6 @@ private:
 	 * \defgroup heobject - Object/property/attribute management functions
 	 * @{
 	 */
-
-#if defined (DEBUGGER)
-	int CheckinRange(uint v1, uint v2, const char *v3) {
-		// TODO: Where the heck is this actualy implemented in Gargoyle
-		return 1;
-	}
-
-	 /**
-	  * Shorthand since many of these object functions may call CheckinRange() if the debugger
-	  * is running and runtime_warnings is set.
-	 */
-	int CheckObjectRange(int obj);
-
-	void DebugRunRoutine(long addr) {}
-
-	void RuntimeWarning(const char *msg) {}
-
-	void DebugMessageBox(const char *title, const char *msg) {}
-
-	bool IsBreakpoint(long loc) const { return false; }
-
-	const char *RoutineName(long loc) { return "Routine"; }
-
-	void AddStringtoCodeWindow(const char *str) {}
-
-	void SwitchtoDebugger() {}
-
-	void DebuggerFatal(DEBUGGER_ERROR err) { error("Debugger error"); }
-
-	void RecoverLastGood() {}
-#endif
-
 	int Child(int obj);
 
 	int Children(int obj);
@@ -690,6 +658,170 @@ private:
 	/**@}*/
 
 	/**
+	 * \defgroup heparse
+	 * @{
+	 */
+
+	void AddAllObjects(int loc);
+
+	/**
+	 * Adds the object <obj> to objlist[], making all related adjustments.
+	 */
+	void AddObj(int obj);
+
+	/**
+	 * Adds <obj> as a contender to the possible object list, noting that it was referred
+	 * to as either a noun or an adjective.
+	 */
+	void AddPossibleObject(int obj, char type, unsigned int w);
+
+	/**
+	 * Move the address in the grammar table past the current token.
+	 */
+	void AdvanceGrammar();
+
+	/**
+	 * For when it's only necessary to know if word[wn] is an object word for any object,
+	 * not a particular object.  Returns 1 for an object word or -1 for a non-object word.
+	 */
+	int AnyObjWord(int wn);
+
+	/**
+	 * The non_grammar argument is true when called from a non-grammar function such as RunEvents().
+	 */
+	int Available(int obj, char non_grammar);
+
+	void CallLibraryParse();
+
+	/**
+	 * Takes into account the preset domain for checking an object's presence;
+	 * <domain> is 0, -1, or an object number..
+	 */
+	int DomainObj(int obj);
+
+	/**
+	 * Returns the dictionary address of <a>.
+	 */
+	unsigned int FindWord(char *a);
+
+	/**
+	 * Checks to see if <obj> is in objlist[].
+	 */
+	int InList(int obj);
+
+	/**
+	 * Deletes word[a].
+	 */
+	void KillWord(int a);
+
+	/**
+	 * Here, briefly, is how MatchCommand() works:
+	 *
+	 * 1.  Match the verb.
+	 *
+	 * 2.  If no match, check to see if the line begins with an object (character)
+	 * and try to match it.
+	 *
+	 * 3.  If found, try to match a syntax for that verb, including objects, dictionary words,
+	 * numbers, attributes, and routines.  If any objects are specified, skip over them for now,
+	 * marking the start and finish.  This is done mostly in MatchWord().
+	 *
+	 * 4.  Match the xobject, if there is one--via MatchObject().
+	 *
+	 * 5.  If all is well, return to match the objects that were previously skipped over,
+	 * loading them into objlist[]. Once again, this is done by MatchObject().
+	 *
+	 * (The reason the objects are initially skipped is because it may be necessary to know 
+	 * where to look for them--this may require knowing what the xobject is, if the syntax
+	 * is something like:
+	 *
+	 * "get" <object> "from" <xobject>)
+	 *
+	 * The variable <obj_match_state> is the indicator of what stage object-matching is at:
+	 *
+	 * obj_match_state = 0  - haven't matched anything yet
+	 *
+	 * obj_match_state = 1  - xobject has been matched
+	 *
+	 * obj_match_state = 2  - matching object(s), loading objlist[]
+	 *
+	 * obj_match_state = 5  - matching first word/name, i.e., "Bob, <do something>"
+	 */
+	int MatchCommand();
+
+	/**
+	 * The argument is the word number we're starting matching on.
+	 *
+	 * NOTE:  recusive_call is set to 0 if this is the first call. MatchObject() sets it to 1
+	 * when calling itself when asking for clarification as to which object is meant.
+	 *
+	 * Return true on a recursive call to allow parsing to continue.
+	 */
+	bool MatchObject(int *wordnum);
+
+	int MatchWord(int *wordnum);
+
+	/**
+	 * Returns true if the specified object has the specified word as an adjective or noun
+	 * (as specified by type).
+	 */
+	int ObjWordType(int obj, unsigned int w, int type);
+
+	/**
+	 * Returns <adjective> if the word at dictionary address <w> is an adjective of <obj>,
+	 * or <noun> if it is a noun.
+	 */
+	int ObjWord(int obj, unsigned int w);
+
+	/**
+	 * Turns word[] into dictionary addresses stored in wd[].  Takes care of fingering illegal
+	 * (unknown) words and doing alterations such as compounds, removals, and synonyms.
+	 */
+	int Parse();
+
+	void ParseError(int e, int a);
+
+	/**
+	 * Deletes wd[a].
+	 */
+	void RemoveWord(int a);
+
+	/**
+	 * Call FindObject(0, 0) to reset library's disambiguation mechanism.
+	 */
+	void ResetFindObject();
+
+	/**
+	 * Splits <buffer> into the word[] array.  Also does nifty things such as turning time
+	 * values such as hh:mm into a single number (representing minutes from midnight).
+	 */
+	void SeparateWords();
+
+	/**
+	 * Removes object <obj> from objlist[], making all related adjustments.
+	 */
+	void SubtractObj(int obj);
+
+	/**
+	 * Removes <obj> as a possible contender for object disambiguation.
+	 */
+	void SubtractPossibleObject(int obj);
+
+	/**
+	 * Called by MatchObject() to see if <obj> is available, and add it to or subtract
+	 * it from objlist[] accordingly.
+	 */
+	void TryObj(int obj);
+
+	/**
+	 * Checks first of all to see if an object is available, then checks if it meets
+	 * all the qualifications demanded by the grammar syntax.
+	 */
+	int ValidObj(int obj);
+
+	/**@}*/
+
+	/**
 	* \defgroup Miscellaneous
 	* @{
 	*/
@@ -735,6 +867,37 @@ private:
 	void *hugo_blockalloc(size_t num) { return malloc(num); }
 
 	void hugo_blockfree(void *block) { free(block); }
+
+#if defined (DEBUGGER)
+	int CheckinRange(uint v1, uint v2, const char *v3) {
+		// TODO: Where the heck is this actualy implemented in Gargoyle
+		return 1;
+	}
+
+	/**
+	* Shorthand since many of these object functions may call CheckinRange() if the debugger
+	* is running and runtime_warnings is set.
+	*/
+	int CheckObjectRange(int obj);
+
+	void DebugRunRoutine(long addr) {}
+
+	void RuntimeWarning(const char *msg) {}
+
+	void DebugMessageBox(const char *title, const char *msg) {}
+
+	bool IsBreakpoint(long loc) const { return false; }
+
+	const char *RoutineName(long loc) { return "Routine"; }
+
+	void AddStringtoCodeWindow(const char *str) {}
+
+	void SwitchtoDebugger() {}
+
+	void DebuggerFatal(DEBUGGER_ERROR err) { error("Debugger error"); }
+
+	void RecoverLastGood() {}
+#endif
 public:
 	/**
 	 * Constructor
diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h
index 5609601..5d69c84 100644
--- a/engines/glk/hugo/hugo_defines.h
+++ b/engines/glk/hugo/hugo_defines.h
@@ -41,8 +41,10 @@ namespace Hugo {
 #define MAX_DEBUG_LINE 256
 #define MAX_OBJECT 999
 #define MAX_PROPERTY 999
+#define MAX_MOBJ 16     /* maximum number of matchable object words */
 #define MAXBUFFER 255
 #define MAXUNDO 1024
+
 #define CHARWIDTH 1
 #define STAT_UNAVAILABLE (-1)
 
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 62237b6..00f5e35 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -76,6 +76,7 @@ MODULE_OBJS := \
 	hugo/heglk.o \
 	hugo/hemisc.o \
 	hugo/heobject.o \
+	hugo/heparse.o \
 	hugo/htokens.o \
 	hugo/hugo.o \
 	hugo/stringfn.o \


Commit: 34122d2f47b4a80a26ea4361a35cba50b5ab5cc0
    https://github.com/scummvm/scummvm/commit/34122d2f47b4a80a26ea4361a35cba50b5ab5cc0
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-11T16:03:14+10:00

Commit Message:
GLK: HUGO: Added herun

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


diff --git a/engines/glk/hugo/herun.cpp b/engines/glk/hugo/herun.cpp
new file mode 100644
index 0000000..4383797
--- /dev/null
+++ b/engines/glk/hugo/herun.cpp
@@ -0,0 +1,2810 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/hugo/hugo.h"
+
+namespace Glk {
+namespace Hugo {
+
+void Hugo::RunDo() {
+	long skip, enterptr;
+
+	enterptr = ++codeptr;
+	skip = PeekWord(codeptr);       /* remember the skip distance */
+	codeptr+=2;
+
+	SetStackFrame(stack_depth, DOWHILE_BLOCK, skip+enterptr, codeptr);
+
+#if defined (DEBUGGER)
+	dbnest++;
+#endif
+}
+
+void Hugo::RunEvents() {
+	int i, tempundo, flag, temp_ret;
+	int eventin, tempself;
+	int templocals[MAXLOCALS];
+	int temp_stack_depth;
+	int temp_parse_location;
+	long tempptr, eventaddr;
+#if defined (DEBUGGER)
+	int tempdbnest;
+#endif
+
+	tempundo = undorecord;
+	undorecord = true;
+
+	tempptr = codeptr;
+	tempself = var[self];
+	temp_ret = ret;
+	temp_parse_location = parse_location;
+
+	parse_location = var[location];	/* for Available() */
+
+	temp_stack_depth = stack_depth;
+
+	for (i=0; i<MAXLOCALS; i++)
+		templocals[i] = var[MAXGLOBALS+i];
+
+	for (i=0; i<events; i++)
+	{
+		defseg = eventtable;
+
+		eventin = PeekWord(2 + i * 4);
+		eventaddr = (long)PeekWord(2 + i * 4 + 2)*address_scale;
+		var[self] = eventin;
+
+		domain = 0, flag = 0;
+
+#if defined (DEBUGGER)
+		/* Prevent premature stopping */
+		if (debugger_step_over && !debugger_finish)
+			debugger_run = true;
+#endif
+		if (eventin==0 || GrandParent(eventin)==GrandParent(var[player]))
+			flag = 1;
+
+		/* true is to signal a non-grammar call */
+		else if (Available(eventin, true))
+			flag = 1;
+
+		if (flag)
+		{
+			PassLocals(0);
+
+			SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+#if defined (DEBUGGER)
+			tempdbnest = dbnest;
+			DebugRunRoutine(eventaddr);
+			dbnest = tempdbnest;
+#else
+			RunRoutine(eventaddr);
+#endif
+			stack_depth = temp_stack_depth;
+
+			retflag = 0;
+			if (var[endflag]) break;
+		}
+	}
+
+	for (i=0; i<MAXLOCALS; i++)
+		var[MAXGLOBALS+i] = templocals[i];
+
+	codeptr = tempptr;
+	parse_location = temp_parse_location;
+	var[self] = tempself;
+	undorecord = (char)tempundo;
+	ret = temp_ret;
+}
+
+void Hugo::playGame() {
+	char jw = 0;                    /* just wrote undo info */
+	char wasxverb = 0, newinput;
+	int i, flag, mc, lastspeaking = 0, startlocation;
+	
+#ifdef USE_TEXTBUFFER
+	TB_Init();
+#endif
+
+#ifdef PALMOS
+	if (AutoResume())
+	{
+		goto FreshInput;
+	}
+#endif
+
+	/* Set up initial screen position */
+	hugo_settextpos(1, physical_windowheight/lineheight);
+	display_needs_repaint = false;
+	full = 0;
+
+	/* Load globals */
+	defseg = arraytable;
+	for (i=0; i<MAXGLOBALS; i++)
+		var[i] = PeekWord(i*2);
+
+	/* Reset the speaking-to variable */
+	speaking = 0;
+
+	if (game_version < 22)
+	{
+		passlocal[0] = objects;
+#if defined (ACTUAL_LINELENGTH)
+		passlocal[1] = ACTUAL_LINELENGTH();
+#else
+		passlocal[1] = physical_windowwidth/FIXEDCHARWIDTH;
+#endif
+	}
+
+#if defined (DEBUGGER)
+RestartDebugger:
+
+	dictcount = original_dictcount;		/* see hd.c */
+	
+	/* If no gamefile is loaded, jump immediately to the debugger
+	   interrupt function.
+	*/
+	if (game==NULL) Debugger();
+#endif
+
+	stack_depth = RESET_STACK_DEPTH;
+
+	InitGame();
+
+	undoptr = 0;
+	undoturn = 0;
+	undoinvalid = 1;
+	undorecord = 0;
+
+Start:
+	stack_depth = 0;
+
+	strcpy(errbuf, "");
+	strcpy(oops, "");
+
+	do
+	{
+		if (xverb==0)
+		{
+			undorecord = true;
+
+			SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+			PassLocals(0);
+
+#if defined (DEBUGGER)
+			currentroutine = mainaddr;
+			window[VIEW_CALLS].count = 0;
+
+			/* If there is no Main routine */
+			if (currentroutine==0) goto NormalTermination;
+
+			DebugRunRoutine((long)mainaddr*address_scale);
+#else
+			RunRoutine((long)mainaddr*address_scale);
+#endif
+			undorecord = false;
+			if (retflag)
+				break;
+		}
+
+		if (!undoinvalid && !wasxverb)
+		{
+			undorecord = true;
+			SaveUndo(0, undoturn, 0, 0, 0);
+			undorecord = false;
+			undoturn = 0;
+		}
+		else if (undoinvalid)
+		{
+			undoptr = 0;
+			undoinvalid = 0;
+			undoturn = 0;
+			undorecord = true;
+			SaveUndo(0, 0, 0, 0, 0);
+			SaveUndo(0, 0, 0, 0, 0);
+			undorecord = false;
+		}
+
+		xverb = true;
+
+		jw = 1;
+		if (var[endflag])
+			break;
+		jw = 0;
+
+		full_buffer = 0;
+
+	/* Endless game loop begins here */
+
+		do
+		{
+			PassLocals(0);
+
+			/* If there's nothing waiting to be finished in the
+			   input buffer:
+			*/
+			if (!remaining)
+			{
+				newinput = true;
+				speaking = 0;
+				do
+				{
+FreshInput:
+					if (full_buffer != true)
+					{
+						newinput = true;
+						speaking = 0;
+						var[actor] = var[player];
+#if defined (DEBUGGER)
+						AddStringtoCodeWindow("[Waiting for input]");
+						buffered_code_lines = FORCE_REDRAW;
+						debugger_has_stepped_back = false;
+						window[VIEW_LOCALS].changed = true;
+#endif
+						if (!playback)
+						{
+							GetCommand();
+						}
+						else
+						{
+							if (!hugo_fgets(buffer, MAXBUFFER, *playback))
+							{
+								if (hugo_fclose(playback))
+									FatalError(READ_E);
+								playback = NULL;
+								GetCommand();
+							}
+							else
+							{
+								/* Remove CR/LF */
+/*
+								buffer[strlen(buffer)-1] = '\0';
+								if (buffer[strlen(buffer)-1]==0x0d)
+									buffer[strlen(buffer)-1] = '\0';
+*/
+								while (buffer[strlen(buffer)-1]==0x0d || buffer[strlen(buffer)-1]==0x0a)
+									buffer[strlen(buffer)-1] = '\0';
+								sprintf(line, "\n%s%s", GetWord(var[prompt]), buffer);
+								if (script)
+									/* fprintf() this way for Glk */
+									script->putBuffer("\n", 1);
+#if defined (SCROLLBACK_DEFINED)
+								hugo_sendtoscrollback("\n");
+#endif
+								AP(line);
+							}
+						}
+#if defined (DEBUGGER)
+						if (debugger_collapsing)
+							goto NormalTermination;
+						runaway_counter = 0;
+#endif
+
+						SeparateWords();
+
+						if (record)
+						{
+							for (i=1; i<=words; i++)
+							{
+								if (!strcmp(word[i], "."))
+								{
+									/* fprintf() this way for Glk */
+									if (hugo_fprintf(record, "%s", "\n")<0)
+										FatalError(WRITE_E);
+									if (i==words) goto RecordedNewline;
+								}
+								else if (hugo_fputs(word[i], record)<0
+									|| hugo_fprintf(record, "%s", " ")<0)
+								{
+									FatalError(WRITE_E);
+								}
+							}
+							if (hugo_fprintf(record, "%s", "\n")<0) FatalError(WRITE_E);
+RecordedNewline:;
+						}
+					}
+					else full_buffer = false;
+
+					if (!strcmp(buffer, "") || buffer[0]=='.')
+					{
+						strcpy(parseerr, "");
+
+						/* "What?" */
+						ParseError(0, 0);
+						goto FreshInput;
+					}
+				}
+
+				/* Loop until valid input */
+				while (Parse()==false && strcmp(buffer, ""));
+			}
+
+
+			/* Else if there's something left in the input buffer */
+			else
+			{
+				newinput = false;
+
+				/* Erase the just-parsed command, and check to
+				   to see if what's left is just blanks
+				*/
+				while (words > remaining)
+					KillWord(1);
+				flag = false;
+				for (i=1; i<=words; i++)
+					if (wd[i]!=0) flag = true;
+				if (!flag)
+					goto FreshInput;
+
+				if (words) AP("");
+
+				if (Parse()==false)
+				{
+					mc = false;
+					goto Skipmc;
+				}
+			}
+
+			/* Run the user Parse routine if one exists */
+			CallLibraryParse();
+
+			reparse_everything = false;
+			do
+			{
+				mc = MatchCommand();
+				if (mc==false)
+				{
+					remaining = 0;
+				}
+			} while (reparse_everything && !mc);
+Skipmc:;
+		}
+		while (!mc);
+
+		if (!xverb) undorecord = true;
+
+		wasxverb = xverb;
+
+		/* If there's an unknown string to be put in parse$ */
+		if (parsestr[0]!='\0')
+		{
+			if (parsestr[0]=='\"')
+			{
+				strcpy(parseerr, Right(parsestr, strlen(parsestr)-1));
+				if (parseerr[strlen(parseerr)-1]=='\"')
+					parseerr[strlen(parseerr)-1] = '\0';
+			}
+		}
+		else
+			strcpy(parseerr, "");
+
+		/* default actor */
+		var[actor] = var[player];
+
+		if (!newinput && lastspeaking) speaking = lastspeaking;
+
+		if (var[verbroutine]!=0 || (speaking))
+		{
+			/* If command addresses an object/char. */
+			if (speaking)
+			{
+				lastspeaking = speaking;
+				var[actor] = speaking;
+
+				/* If user Speakto routine exists */
+				if (speaktoaddr)
+				{
+					if (objcount) var[object] = objlist[0];
+					ret = 0;
+
+					SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+					passlocal[0] = speaking;
+					PassLocals(1);
+#if defined (DEBUGGER)
+					DebugRunRoutine((long)speaktoaddr*address_scale);
+#else
+					RunRoutine((long)speaktoaddr*address_scale);
+#endif
+					if (ret==0)
+					{
+						remaining = 0;
+						xverb = true;
+					}
+					retflag = 0;
+				}
+
+				else
+				{
+					/* "...start with a verb..." */
+					ParseError(2, 0);
+					xverb = true;
+				}
+				
+				/* reset actor */
+				var[actor] = var[player];
+			}
+
+			/* Regular old vanilla command: */
+			else
+			{
+				speaking = 0;
+				lastspeaking = 0;
+
+	/* As of v2.5, the Perform junction routine takes care of calling the
+	   before routines, verbroutine, etc.
+	*/
+				if (game_version>=25 && performaddr!=0)
+				{
+					i = 0;
+NextPerform:
+					if (objcount) var[object] = objlist[i];
+
+					/* Have to do this before passing locals, in case
+					   Name() ends up calling a routine (which would
+					   trash passlocal[])
+					*/
+					if (parseerr[0]=='\0' && parsestr[0]=='\0')
+						strcpy(parseerr, Name(objlist[i]));
+
+					/* Set up arguments for Perform */
+					passlocal[0] = var[verbroutine];
+					passlocal[1] = var[object];
+					passlocal[2] = var[xobject];
+
+					/* 'queue' argument, >1 if objcount > 1, or if
+					   "all" has been used to refer to object(s) */
+					passlocal[3] = (objcount>1)?(i+1):(parse_allflag?1:0);
+					/* -1 if object is a digit */
+					if (object_is_number) passlocal[3] = (short)-1;
+
+					/* 'isxverb' argument */
+					if (game_version>=31 && xverb)
+						passlocal[4] = 1;
+
+					obj_match_state = -1;
+					startlocation = var[location];
+					ret = 0;
+
+					SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+					PassLocals(4);
+#if defined (DEBUGGER)
+					DebugRunRoutine((long)performaddr*address_scale);
+#else
+					RunRoutine((long)performaddr*address_scale);
+#endif
+					if (ret==0)
+					{
+						remaining = 0;
+						xverb = true;
+					}
+					retflag = 0;
+
+					/* Break if endflag is set or if the location has
+					   changed from the first call to Perform
+					*/
+					if (var[endflag] || startlocation!=var[location])
+						goto EndofCommand;
+
+					if (objcount>1 && ++i<objcount)
+						goto NextPerform;
+			    	}
+
+	/* v2.4 or earlier had to call the verb loop via the engine 
+	   (as does v2.5 with no Perform routine */
+
+				/* One or more objects specified */
+				else if (objcount > 0)  /* "if (objcount > 0" for pre-v2.5 */
+				{
+					obj_match_state = 1;
+					startlocation = var[location];
+					for (i=0; i<objcount; i++)
+					{
+						if (parseerr[0]=='\0' && parsestr[0]=='\0')
+							strcpy(parseerr, Name(objlist[i]));
+
+						if (ValidObj(objlist[i]) &&
+							((objcount>1 && objlist[i]!=var[xobject]) || objcount==1))
+						{
+							var[object] = objlist[i];
+							if (GetProp(var[player], before, 1, 0)==0)
+							if (GetProp(var[location], before, 1, 0)==0)
+							if (GetProp(var[xobject], before, 1, 0)==0)
+							{
+								/* If multiple objects are specified, print
+								"name:  " for each:
+								*/
+								if (objcount > 1)
+								{
+									sprintf(line, "%s:  \\;", Name(var[object]));
+									AP(line);
+								}
+
+								obj_match_state = 0;
+
+								if ((object_is_number) || GetProp(var[object], before, 1, 0)==0)
+								{
+									obj_match_state = -1;
+									ret = 0;
+
+									SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+									PassLocals(0);
+#if defined (DEBUGGER)
+									DebugRunRoutine((long)var[verbroutine]*address_scale);
+#else
+									RunRoutine((long)var[verbroutine]*address_scale);
+#endif
+									if (ret==0)
+									{
+										remaining = 0;
+										xverb = true;
+									}
+									retflag = 0;
+
+									GetProp(var[player], after, 1, 0);
+									GetProp(var[location], after, 1, 0);
+								}
+							}
+						}
+						if (var[endflag] || var[location]!=startlocation)
+						break;
+					}
+				}
+
+				/* No object(s) specified */
+				else
+				{
+					if (GetProp(var[player], before, 1, 0)==0)
+					{
+						if (GetProp(var[location], before, 1, 0)==0)
+						{
+							ret = 0;
+
+							SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+							PassLocals(0);
+#if defined (DEBUGGER)
+							DebugRunRoutine((long)var[verbroutine]*address_scale);
+#else
+							RunRoutine((long)var[verbroutine]*address_scale);
+#endif
+							if (ret==0)
+							{
+								remaining = 0;
+								xverb = true;
+							}
+							retflag = 0;
+
+							GetProp(var[player], after, 1, 0);
+							GetProp(var[location], after, 1, 0);
+						}
+		        		}
+				}
+
+	/* (end of pre-v2.5 verbroutine-calling) */
+
+			}
+		}
+EndofCommand:
+		if (var[endflag])
+			break;
+
+		undorecord = false;
+	}
+	while (true);   /* endless loop back to start */
+
+	undorecord = false;
+
+	if (var[endflag]==-1)
+#if defined (DEBUGGER)
+		goto NormalTermination;
+#else
+		return;
+#endif
+
+	if (!jw)
+	{
+		undorecord = true;
+		SaveUndo(0, undoturn, 0, 0, 0);
+		undorecord = false;
+		undoturn = 0;
+	}
+
+	if (playback)
+	{
+		if (hugo_fclose(playback)) FatalError(READ_E);
+		playback = NULL;
+	}
+
+	Flushpbuffer();
+
+
+	/* Run the user Endgame routine if one exists */
+
+#if defined (DEBUGGER)
+	if (endgameaddr && !debugger_collapsing)
+#else
+	if (endgameaddr)
+#endif
+	{
+		passlocal[0] = var[endflag];
+
+		SetStackFrame(RESET_STACK_DEPTH, RUNROUTINE_BLOCK, 0, 0);
+
+		ret = 0;
+		var[endflag] = 0;
+		PassLocals(0);
+#if defined (DEBUGGER)
+		DebugRunRoutine((long)endgameaddr*address_scale);
+#else
+		RunRoutine((long)endgameaddr*address_scale);
+#endif
+		retflag = false;
+	}
+	else
+		ret = 0;
+
+	xverb = true;
+	wasxverb = true;
+	if (ret) goto Start;
+
+	/* Stop all audio after running EndGame */
+	hugo_stopmusic();
+	hugo_stopsample();
+	
+	/* The debugger will reset endflag anyway, but we need it to signal ports
+	   (like Palm) that the game loop has exited.
+	*/
+	var[endflag] = -1;
+
+#if defined (DEBUGGER)
+
+NormalTermination:
+
+	/* Normal program termination doesn't exit the debugger. */
+
+	debugger_interrupt = true;
+	debugger_run = false;
+	var[endflag] = false;
+
+	xverb = false;
+
+	SwitchtoDebugger();
+	UpdateDebugScreen();
+
+	if (debugger_collapsing!=2)
+	{
+		DebugMessageBox("Program Exiting", "Normal program termination");
+
+	}
+
+	debugger_collapsing = false;
+
+	if ((game!=NULL) && !RunRestart())
+		DebugMessageBox("Restart Error", "Unable to restart");
+
+	SwitchtoGame();
+
+	history_count = 0;
+	window[VIEW_CALLS].count = 0;
+
+	for (i=0; i<(int)window[CODE_WINDOW].count; i++)
+		free(codeline[i]);
+	window[CODE_WINDOW].count = 0;
+
+	/* Force Code window redraw */
+	buffered_code_lines = FORCE_REDRAW;
+
+	goto RestartDebugger;
+#endif
+}
+
+void Hugo::RunIf(char override) {
+	char t, tempinexpr;
+	long enterptr, skip;
+
+	switch (t = MEM(codeptr))
+	{
+		case CASE_T:
+		case IF_T:
+		case ELSEIF_T:
+		case WHILE_T:
+		case FOR_T:
+		{
+			codeptr++;
+			enterptr = codeptr;
+
+			/* Remember the skip distance */
+			skip = PeekWord(codeptr);
+			codeptr += 2;
+
+			/* Check if we've already done an elseif */
+			if (override && t==ELSEIF_T)
+			{
+				codeptr = skip+enterptr;
+				return;
+			}
+
+			/* Read the expression */
+			tempinexpr = inexpr;
+			inexpr = 1;
+			SetupExpr();
+			inexpr = tempinexpr;
+
+			/* If the expression is false, skip the
+			   conditional block
+			*/
+			if (EvalExpr(0)==0)
+			{
+				codeptr = skip+enterptr;
+				return;
+			}
+
+			/* Protect the stack if jumping backward */
+			if (MEM(codeptr)==JUMP_T)
+			{
+				if ((long)(PeekWord(codeptr+1)*address_scale) < codeptr)
+					if (--stack_depth < 0) stack_depth = 0;
+			}
+
+			/* Continue on into the conditional block if
+			   the expression evaluated to non-zero
+			*/
+PasstoBlock:
+			if (t==WHILE_T || t==FOR_T)
+				SetStackFrame(stack_depth, CONDITIONAL_BLOCK, skip+enterptr, 0);
+
+			else	/* no 'break' parameter */
+				SetStackFrame(stack_depth, CONDITIONAL_BLOCK, 0, 0);
+#if defined (DEBUGGER)
+			dbnest++;
+#endif
+			return;
+		}
+		case ELSE_T:
+		{
+			skip = PeekWord(++codeptr);
+			enterptr = codeptr;
+			codeptr += 2;
+
+			if (override)
+			{
+				codeptr = skip+enterptr;
+				return;
+			}
+
+			if (MEM(codeptr)==JUMP_T)
+			{
+				if ((long)(PeekWord(codeptr+1)*address_scale) < codeptr)
+					if (--stack_depth < 0) stack_depth = 0;
+			}
+
+			goto PasstoBlock;
+		}
+	}
+}
+
+void Hugo::RunInput() {
+	int i;
+
+	strcpy(parseerr, "");
+
+	Flushpbuffer();
+
+	if (icolor==-1) icolor = fcolor;	/* check unset input color */
+
+	hugo_getline("");
+
+#if defined (DEBUGGER)
+	if (debugger_collapsing) return;
+#endif
+
+	strcpy(buffer, Rtrim(strlwr(buffer)));
+
+	SeparateWords();
+
+	for (i=1; i<=words; i++)
+	{
+		wd[i] = FindWord(word[i]);
+
+		/* If a word isn't in the dictionary */
+		if (wd[i]==UNKNOWN_WORD)
+		{
+			wd[i] = 0;
+			strcpy(parseerr, word[i]);
+			if (parseerr[0]=='\"')
+			{
+				strcpy(parseerr, Right(parseerr, strlen(parseerr)-1));
+				if (parseerr[strlen(parseerr)-1]=='\"')
+					parseerr[strlen(parseerr)-1] = '\0';
+			}
+		}
+	}
+	currentpos = 0;	/* left margin */
+	remaining = 0;
+}
+
+void Hugo::RunMove() {
+	int obj, p;
+#if defined (DEBUGGER)
+	char out_of_range = 0;
+#endif
+
+	switch (MEM(codeptr))
+	{
+		case MOVE_T:
+		{
+			codeptr++;
+			obj = GetValue();
+
+#if defined (DEBUGGER)
+			if (!CheckinRange(obj, objects, "object"))
+				out_of_range = true;
+			else
+#endif
+			SaveUndo(MOVE_T, obj, Parent(obj), 0, 0);
+
+			codeptr++;              /* skip "to" */
+			p = GetValue();
+
+#if defined (DEBUGGER)
+			if (!CheckinRange(p, objects, "object"))
+				out_of_range = true;
+
+			if (!out_of_range)
+#endif
+			MoveObj(obj, p);
+			break;
+		}
+
+		case REMOVE_T:
+		{
+			codeptr++;
+			obj = GetValue();
+
+#if defined (DEBUGGER)
+			if (!CheckinRange(obj, objects, "object"))
+				out_of_range = true;
+
+			else
+#endif
+			SaveUndo(MOVE_T, obj, Parent(obj), 0, 0);
+
+#if defined (DEBUGGER)
+			if (!out_of_range)
+#endif
+			MoveObj(obj, 0);        /* move to parent 0 */
+			break;
+		}
+	}
+
+	if (game_version>=23) codeptr++; /* eol */
+}
+
+void Hugo::RunPrint() {
+	char number = 0, hexnumber = 0;
+	int a;
+	int i, l;
+
+	codeptr++;
+
+	while (MEM(codeptr) != EOL_T)
+	{
+		strcpy(line, "");
+
+		switch (MEM(codeptr))
+		{
+			case NEWLINE_T:
+			{
+				codeptr++;
+				if (currentpos+hugo_textwidth(pbuffer)!=0)
+					AP("");
+				if (MEM(codeptr)==SEMICOLON_T) codeptr++;
+				continue;
+			}
+
+			case TO_T:
+			{
+				codeptr++;
+
+#if !defined (ACTUAL_LINELENGTH)
+				if ((a = GetValue()) > physical_windowwidth/FIXEDCHARWIDTH)
+					a = physical_windowwidth/FIXEDCHARWIDTH;
+#else
+				if ((a = GetValue()) > ACTUAL_LINELENGTH())
+				{
+					double ratio;
+
+					ratio = (physical_windowwidth/FIXEDCHARWIDTH) / a;
+					a = (int)(ACTUAL_LINELENGTH() / ratio);
+				}
+#endif
+				strcpy(line, "");
+				l = 0;
+				if (a*FIXEDCHARWIDTH >
+					hugo_textwidth(pbuffer)+currentpos-hugo_charwidth(' '))
+				{
+					for (i=hugo_textwidth(pbuffer)+currentpos;
+#ifdef NO_TERMINAL_LINEFEED
+				        	i<a*FIXEDCHARWIDTH;
+#else
+				        	i<a*FIXEDCHARWIDTH && i<physical_windowright;
+#endif
+						i+=hugo_charwidth(' '))
+					{
+						line[l++] = FORCED_SPACE;
+						line[l] = '\0';
+					}
+
+				}
+				break;
+			}
+
+			case CAPITAL_T:
+			{
+				codeptr++;
+				capital = 1;
+				continue;
+			}
+
+			case NUMBER_T:
+			{
+				codeptr++;
+				number = 1;
+				continue;
+			}
+
+			case HEX_T:
+			{
+				codeptr++;
+				number = 1;
+				hexnumber = 1;
+				continue;
+			}
+
+			case STRINGDATA_T:
+			{
+				codeptr++;
+				if (game_version >= 24)
+					l = PeekWord(codeptr++);
+				else
+					l = Peek(codeptr);
+				for (i=0; i<l; i++)
+					line[i] = (char)(MEM(++codeptr) - CHAR_TRANSLATION);
+				line[i] = '\0';
+				codeptr++;
+				break;
+			}
+
+			/* Anything else is treated as a value */
+			default:
+			{
+				a = GetValue();
+				if (!number)
+				{
+					strcpy(line, GetWord(a));
+				}
+				else
+				{
+					if (!hexnumber)
+					{
+						if (capital)
+							itoa((unsigned int)a, line, 10);
+						else
+							itoa(a, line, 10);
+						capital = 0;
+					}
+					else
+						sprintf(line, "%X", a);
+
+					number = 0;
+					hexnumber = 0;
+				}
+				break;
+			}
+		}
+
+		if (MEM(codeptr)==SEMICOLON_T)
+		{
+			codeptr++;
+			strcat(line, "\\;");
+		}
+		if (capital)
+		{
+			capital = 0;
+			if ((unsigned)line[0]<128)
+				line[0] = (char)toupper((int)line[0]);
+			else
+			{
+				/* Special conversion for non-Latin1
+				   (>127) lowercase characters
+				*/
+				char diff;
+				diff = 'a'-'A';
+				if ((unsigned)line[0]+diff<=255 && (unsigned)line[0]-diff>127)
+					line[0] -= diff;
+			}
+		}
+
+		AP(line);
+	}
+
+	codeptr++;
+}
+
+int Hugo::RunRestart() {
+	unsigned int a;
+	long i = 0;
+	Common::SeekableReadStream *file;
+
+#ifndef LOADGAMEDATA_REPLACED
+
+	remaining = 0;
+
+#if !defined (GLK)	/* with Glk, game is never closed */
+	/* Use file instead of game, just in case the call fails */
+	if (!(file = HUGO_FOPEN(gamefile, "rb"))) goto RestartError;
+#else
+	file = game;
+#endif
+
+	if (hugo_fseek(file, (objtable-gameseg)*16, SEEK_SET)) goto RestartError;
+
+	i = (objtable-gameseg)*16L;
+	do
+	{
+		int val;
+
+		val = hugo_fgetc(file);
+		SETMEM(i++, (unsigned char)val);
+		if (val==EOF || hugo_ferror(file)) goto RestartError;
+	}
+	while (i < codeend);
+
+#if !defined (GLK)
+	if (fclose(file)) FatalError(READ_E);
+#endif
+
+#else
+	if (!(file = HUGO_FOPEN(gamefile, "rb"))) goto RestartError;
+	LoadGameData(true);
+	fclose(file);
+#endif	/* LOADGAMEDATA_REPLACED */
+
+	defseg = arraytable;
+	for (a=0; a<MAXGLOBALS; a++)
+		var[a] = PeekWord(a*2);
+
+	i = codeptr;
+
+	if (game_version < 22)
+	{
+		passlocal[0] = objects;
+#if defined (ACTUAL_LINELENGTH)
+		passlocal[1] = ACTUAL_LINELENGTH();
+#else
+		passlocal[1] = physical_windowwidth/FIXEDCHARWIDTH;
+#endif
+	}
+
+#if defined (DEBUGGER)
+	/* A restart can happen mid-playback from the debugger */
+	if (playback)
+	{
+		if (hugo_fclose(playback))
+			FatalError(READ_E);
+		playback = NULL;
+	}
+
+	if (active_screen!=DEBUGGER)
+#endif
+	{
+		InitGame();
+
+		SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+		PassLocals(0);
+#if defined (DEBUGGER)
+		DebugRunRoutine((long)mainaddr*address_scale);
+#else
+		RunRoutine((long)mainaddr*address_scale);
+#endif
+		retflag = 0;
+
+		codeptr = i;
+
+		undoptr = 0;
+		undoturn = 0;
+		undoinvalid = 1;
+	}
+
+	return 1;
+
+RestartError:
+#if !defined (GLK)
+	if (fclose(file)) FatalError(READ_E);
+#endif
+
+	return 0;
+}
+
+#ifndef RESTOREGAMEDATA_REPLACED
+
+int Hugo::RestoreGameData() {
+	char testid[3], testserial[9];
+	int lbyte, hbyte;
+	int j;
+	unsigned int k, undosize;
+	long i;
+
+	/* Check ID */
+	testid[0] = (char)hugo_fgetc(save);
+	testid[1] = (char)hugo_fgetc(save);
+	testid[2] = '\0';
+	if (hugo_ferror(save)) goto RestoreError;
+
+	if (strcmp(testid, id))
+	{
+		AP("Incorrect save file.");
+		if (hugo_fclose(save)) FatalError(READ_E);
+		save = NULL;
+		return 0;
+	}
+
+	/* Check serial number */
+	if (!hugo_fgets(testserial, 9, save)) goto RestoreError;
+	if (strcmp(testserial, serial))
+	{
+		AP("Save file created by different version.");
+		if (hugo_fclose(save)) FatalError(READ_E);
+		save = NULL;
+		return 0;
+	}
+
+	/* Restore variables */
+	for (k=0; k<MAXGLOBALS+MAXLOCALS; k++)
+	{
+		if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF)
+			goto RestoreError;
+		var[k] = lbyte + hbyte * 256;
+	}
+
+	/* Restore objtable and above */
+
+	if (hugo_fseek(game, objtable*16L, SEEK_SET)) goto RestoreError;
+	i = 0;
+
+	while (i<codeend-(long)(objtable*16L))
+	{
+		if ((hbyte = hugo_fgetc(save))==EOF && hugo_ferror(save)) goto RestoreError;
+
+		if (hbyte==0)
+		{
+			if ((lbyte = hugo_fgetc(save))==EOF && hugo_ferror(save)) goto RestoreError;
+			SETMEM(objtable*16L+i, (unsigned char)lbyte);
+			i++;
+
+			/* Skip byte in game file */
+			if (hugo_fgetc(game)==EOF) goto RestoreError;
+		}
+		else
+		{
+			while (hbyte--)
+			{
+				/* Get unchanged game file byte */
+				if ((lbyte = hugo_fgetc(game))==EOF) goto RestoreError;
+				SETMEM(objtable*16L+i, (unsigned char)lbyte);
+				i++;
+			}
+		}
+	}
+
+	/* Restore undo data */
+	if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF)
+		goto RestoreError;
+	undosize = lbyte + hbyte*256;
+
+	/* We can only restore undo data if it was saved by a port with
+	   the same MAXUNDO as us */
+	if (undosize==MAXUNDO)
+	{
+		for (k=0; k<MAXUNDO; k++)
+		{
+			for (j=0; j<5; j++)
+			{
+				if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF)
+					goto RestoreError;
+				undostack[k][j] = lbyte + hbyte*256;
+			}
+		}
+		if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF) goto RestoreError;
+		undoptr = lbyte + hbyte*256;
+		if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF) goto RestoreError;
+		undoturn = lbyte + hbyte*256;
+		if ((lbyte = hugo_fgetc(save))==EOF || (hbyte = hugo_fgetc(save))==EOF) goto RestoreError;
+		undoinvalid = (unsigned char)lbyte, undorecord = (unsigned char)hbyte;
+	}
+	else undoinvalid = true;
+
+	return true;
+	
+RestoreError:
+	return false;
+}
+
+#endif	// RESTOREGAMEDATA_REPLACED
+
+int Hugo::RunRestore() {
+#if !defined (GLK)
+	save = NULL;
+
+	/* stdio implementation */
+	hugo_getfilename("to restore", savefile);
+
+#if defined (DEBUGGER)
+	if (debugger_collapsing) return 1;
+#endif
+	if (!strcmp(line, "")) return 0;
+	if (!(save = HUGO_FOPEN(line, "r+b"))) return 0;
+
+#else
+	/* Glk implementation */
+	frefid_t savefile;
+
+	save = NULL;
+
+	savefile = glk_fileref_create_by_prompt(fileusage_SavedGame | fileusage_BinaryMode,
+		filemode_Read, 0);
+	if (!savefile) return 0;
+	if (glk_fileref_does_file_exist(savefile))
+		save = glk_stream_open_file(savefile, filemode_Read, 0);
+	else
+		save = NULL;
+	glk_fileref_destroy(savefile);
+	if (!save) return 0;
+
+#endif	/* GLK */
+
+	if (!RestoreGameData()) goto RestoreError;
+
+	if (hugo_fclose(save)) FatalError(READ_E);
+	save = NULL;
+
+#if !defined (GLK)
+	strcpy(savefile, line);
+#endif
+
+	game_reset = true;
+
+	return (1);
+
+RestoreError:
+	if ((save) && hugo_fclose(save)) FatalError(READ_E);
+	save = NULL;
+	game_reset = false;
+	return 0;
+}
+
+void Hugo::RunRoutine(long addr) {
+	int null_count;        /* for reading to next address boundary     */
+	char tempinexpr;
+	int i, t, len, xpos, ypos;
+	int initial_stack_depth, tempret;
+	unsigned int routineptr = 0;
+	long textaddr;
+#if defined (DEBUGGER)
+	char wascalled = 0;     /* distinguish routine calls from 'window'
+				   blocks, etc. */
+	const char *called_from;
+	char trace_comp_prop = 0;
+	unsigned char param_type;
+	long param_start;
+	unsigned int old_currentroutine = 0;
+
+	/* Because <debugdata#> and <label#> in-code data is
+	   not decompiled
+	*/
+	int broke_on_nonstatement = 0;
+#endif
+
+	/* If routine doesn't exist */
+	if (addr==0L) return;
+	
+	initial_stack_depth = stack_depth;
+	inexpr = 0;
+
+#if !defined (DEBUGGER)
+#if defined (DEBUG_CODE)
+/*
+	if (trce)
+	{
+		if (codeptr != addr)
+		{
+			sprintf(line, "[ROUTINE:  $%6s]", PrintHex(addr));
+			AP(line);
+			wascalled = 1;
+		}
+	}
+*/
+#endif
+#endif
+
+#if defined (DEBUGGER)
+
+/*
+ *      First see what debugger information has to be set up upon calling
+ *      this block of code
+ */
+
+	/* If this is a routine call vs. other block, codeptr will be
+	   different from addr.
+	*/
+	if (codeptr != addr)
+	{
+		wascalled = 1;
+
+		/* Checking to see if currentroutine is 0 is a way of seeing
+		   if the debugger has started properly.  If, for example, a
+		   property routine runs while LoadGame() is searching for
+		   the display object, it may corrupt the uninitialized
+		   debugger arrays.
+		*/
+		if ((old_currentroutine = currentroutine)==0) return;
+
+		currentroutine = (unsigned int)(addr/address_scale);
+
+		if (debugger_step_over)
+		{
+			step_nest++;
+			debugger_interrupt = false;
+		}
+		else
+		{
+			/* Add a blank line if one hasn't been added
+			   already:
+			*/
+			if ((window[CODE_WINDOW].count) && (codeline[window[CODE_WINDOW].count-1][0]&0x0FF)!='\0')
+				AddStringtoCodeWindow("");
+
+			/* If this is a property routine, the debug_line array
+			   already holds the calling information
+			*/
+			if (!trace_complex_prop_routine)
+				sprintf(debug_line, "Calling:  %s", RoutineName(currentroutine));
+			else
+				trace_comp_prop = true;
+			trace_complex_prop_routine = false;
+
+			call[window[VIEW_CALLS].count].addr = currentroutine;
+			call[window[VIEW_CALLS].count++].param = (char)arguments_passed;
+			window[VIEW_CALLS].changed = true;
+
+			/* Revise call history */
+			if (window[VIEW_CALLS].count==MAXCALLS)
+			{
+				for (i=0; i<MAXCALLS-1; i++)
+				{
+					call[i].addr = call[i+1].addr;
+					call[i].param = call[i+1].param;
+				}
+				call[0].addr = 0;
+			}
+
+			/* If not object.property or an event */
+			if (strchr(debug_line, '.')==NULL && strstr(debug_line, "vent ")==NULL)
+			{
+				strcat(debug_line, "(");
+				for (i=0; i<arguments_passed; i++)
+				{
+					sprintf(debug_line+strlen(debug_line), "%d", var[MAXGLOBALS+i]);
+					if (i<arguments_passed-1)
+						strcat(debug_line, ", ");
+				}
+				strcat(debug_line, ")");
+			}
+			AddStringtoCodeWindow(debug_line);
+		}
+	}
+
+#endif  /* defined (DEBUGGER) */
+
+	defseg = gameseg;
+	codeptr = addr;
+
+/*
+ *      Get the next token, so long as it isn't a CLOSE_BRACE_T ('}')
+ *      marking the end of this block of code
+ */
+
+ContinueRunning:
+
+	while (MEM(codeptr) != CLOSE_BRACE_T)   /* until "}" */
+	{
+
+#if defined (DEBUGGER)
+		/* Check if we're stepping over, and if we've returned to
+		   the original level of nesting:
+		*/
+		if (debugger_step_over && step_nest==0)
+		{
+			debugger_step_over = false;
+			debugger_interrupt = true;
+		}
+#endif
+		if (var[endflag]) return;
+
+		null_count = 0;
+
+		/* Read the next token */
+		while ((t = MEM(codeptr))==0)
+		{
+			codeptr++;
+
+			/* Allow for padding zeroes.  If address_scale
+			   zeroes are processed, we can't simply be
+			   eating up the null space before an address
+			   boundary.
+			*/
+			if (++null_count > address_scale)
+				FatalError(UNKNOWN_OP_E);
+		}
+		
+#if !defined (DEBUGGER)
+#if defined (DEBUG_CODE)
+		if (!inwindow)
+		{
+			sprintf(line, "[%6s:  %s]", PrintHex(codeptr), token[t]);
+			AP(line);
+		}
+#endif
+#endif
+		if (game_version < 22) if (t==TEXT_T) t = TEXTDATA_T;
+
+#if defined (DEBUGGER)
+		if (++runaway_counter>=65535 && runtime_warnings)
+		{
+			sprintf(debug_line, "Possible runaway loop (65535 unchecked steps)");
+			RuntimeWarning(debug_line);
+			buffered_code_lines = FORCE_REDRAW;
+			runaway_counter = 0;
+		}
+
+		if (t!=DEBUGDATA_T && t!=LABEL_T && !debugger_step_over)
+			AddLinetoCodeWindow(codeptr);
+
+		if ((i = IsBreakpoint(codeptr)))
+		{
+			if (t==DEBUGDATA_T || t==LABEL_T)
+				broke_on_nonstatement = i;
+
+			debugger_interrupt = true;
+
+			/* '<' for "<Unknown>" */
+			if (breakpoint[--i].in[0]=='<')
+			{
+				breakpoint[i].in = RoutineName(currentroutine);
+				window[VIEW_BREAKPOINTS].changed = true;
+			}
+		}
+
+		/* Don't add in-code data to the code window */
+		if (t==DEBUGDATA_T || t==LABEL_T) goto ProcessToken;
+
+		/* Evaluate (only) any watch expressions set to break
+		   when true:
+		*/
+		for (i=0; i<(int)window[VIEW_WATCH].count; i++)
+		{
+			if (watch[i].isbreak)
+			{
+				SetupWatchEval(i);
+				if (EvalWatch())
+				{
+					debugger_interrupt = true;
+					break;
+				}
+			}
+		}
+
+		/* Always update the watch window */
+		window[VIEW_WATCH].changed = true;
+
+/*
+ *      Immediately following is the main Debugger interrupt point.
+ *      No matter where the engine is while running, if debugger_interrupt
+ *      is ever set during the execution of a command, execution is
+ *      paused and control passed to the Debugger.
+ */
+
+		if (debugger_interrupt)
+		{
+			if (broke_on_nonstatement)
+			{
+				broke_on_nonstatement--;
+				breakpoint[broke_on_nonstatement].addr = codeptr;
+				window[VIEW_BREAKPOINTS].changed = true;
+				broke_on_nonstatement = false;
+			}
+
+			if (debugger_step_over)
+			{
+				AddStringtoCodeWindow("...");
+				if (t!=DEBUGDATA_T && t!=LABEL_T)
+					AddLinetoCodeWindow(codeptr);
+			}
+			Debugger();
+		}
+
+
+		/* Now, additional processing for flags that may have been
+		   set while execution was suspended:
+		*/
+
+		/* Collapsing the RunRoutine() call stack */
+		if (debugger_collapsing) return;
+
+
+		/* May be necessary to reset this if, for some
+		   reason, the line array was altered (see above)
+		*/
+		if (!trace_complex_prop_routine)
+			sprintf(debug_line, "Calling:  %s", RoutineName(currentroutine));
+		trace_complex_prop_routine = false;
+
+
+		/* Add this statement to the code history */
+		if (!debugger_step_back && !(debugger_step_over && step_nest>0))
+		{
+			if (++history_count>=MAX_CODE_HISTORY)
+				 history_count = MAX_CODE_HISTORY;
+			code_history[history_last] = codeptr;
+			dbnest_history[history_last] = dbnest;
+			if (++history_last >= MAX_CODE_HISTORY)
+				history_last = 0;
+		}
+
+		/* If skipping next or stepping back */
+		if (debugger_skip || debugger_step_back)
+		{
+			/* Debugger() has reset codeptr to next_codeptr */
+			debugger_skip = false;
+			continue;
+		}
+
+#endif  /* defined (DEBUGGER) */
+
+
+/*
+ *      This is the heart of RunRoutine():  the switch statement
+ *      that executes the next engine operation based on what token
+ *      has been read
+ */
+#if defined (DEBUGGER)
+ProcessToken:
+#endif
+		switch (t)
+		{
+			/* First process any encoded, non-executable data: */
+
+			/* If this is v2.5 or later, the compiler will have
+			   noted the nesting level of this label to
+			   reconcile stack_depth
+			*/
+			case LABEL_T:
+				stack_depth = initial_stack_depth + MEM(++codeptr);
+				codeptr++;
+				break;
+
+			case DEBUGDATA_T:
+			{
+			  switch (MEM(++codeptr))
+			  {
+				case VAR_T:     /* local variable name */
+				{
+					len = MEM(++codeptr);
+#if defined (DEBUGGER)
+					if (!debugger_has_stepped_back)
+					{
+						/* Read the local variable name */
+						for (i=0; i<len; i++)
+							line[i] = MEM(codeptr+i+1);
+						line[len] = '\0';
+
+						/* Check to make sure it doesn't already exist,
+						   for instance, if we've looped back to it
+						*/
+						for (i=0; i<current_locals; i++)
+							if (!strcmp(line, localname[i])) break;
+						
+						/* If it doesn't exist, add it */
+						if (i==current_locals)
+						{
+							strcpy(localname[current_locals], line);
+							if (++current_locals==MAXLOCALS)
+								current_locals--;
+							window[VIEW_LOCALS].count = current_locals;
+						}
+					}
+#endif
+					codeptr+=(len+1);
+					break;
+				}
+			  }
+			  break;
+			}
+
+
+			/* Then the executable statements: */
+
+			case TEXTDATA_T:        /* printed text from file */
+			{
+				textaddr = Peek(codeptr+1)*65536L+(long)PeekWord(codeptr+2);
+				strcpy(line, GetText(textaddr));
+				codeptr += 4;
+				if (Peek(codeptr)==SEMICOLON_T)
+					{strcat(line, "\\;");
+					codeptr++;}
+				if (capital)
+					{line[0] = (char)toupper((int)line[0]);
+					capital = 0;}
+				AP(line);
+				break;
+			}
+
+			case TEXT_T:
+			{
+				if (MEM(++codeptr)==TO_T)
+				{
+					codeptr++;
+#if defined (DEBUGGER)
+					param_type = MEM(codeptr);
+					param_start = codeptr;
+#endif
+					textto = GetValue();
+					if (game_version>=23) codeptr++; /* eol */
+#if defined (DEBUGGER)
+					/* Check if textto is 0 but was not
+					   really "text to 0", but rather
+					   something that evaluated to 0
+					*/
+					if (textto==0 && runtime_warnings)
+					{
+					  if (param_type!=VALUE_T || param_start!=codeptr-4)
+					    RuntimeWarning("Text array address evaluates to zero");
+					}
+#endif
+				}
+				else
+				{
+					SetupDisplay();
+				}
+				break;
+			}
+
+			case MINUS_T:           /* "--" */
+			case PLUS_T:            /* "++" */
+				GetValue();
+				codeptr++; /* eol */
+				break;
+
+			case PRINT_T:
+				RunPrint();
+				break;
+
+			case PRINTCHAR_T:
+			{
+Printcharloop:
+				codeptr++;
+				i = GetValue();
+				if (capital) sprintf(line, "%c\\;", toupper(i));
+				else sprintf(line, "%c\\;", i);
+				capital = 0;
+				AP(line);
+				if (Peek(codeptr)==COMMA_T)
+					goto Printcharloop;
+				if (game_version>=23) codeptr++; /* eol */
+				break;
+			}
+
+			case STRING_T:
+				RunString();
+				break;
+
+			case WINDOW_T:
+				RunWindow();
+				break;
+
+			case LOCATE_T:
+			{
+				char adhere_to_bottom = false;
+
+				codeptr++;
+
+				Flushpbuffer();
+
+				xpos = GetValue();
+				if (xpos > physical_windowwidth/FIXEDCHARWIDTH)
+					xpos = physical_windowwidth/FIXEDCHARWIDTH;
+
+				if (Peek(codeptr)==COMMA_T)
+				{
+					codeptr++;
+					ypos = GetValue();
+				}
+				else
+					ypos = currentline;
+
+				full = ypos - 1;
+
+
+				if (ypos >= physical_windowheight/lineheight)
+					full = 0;
+					
+				if (ypos > physical_windowheight/lineheight)
+				{
+					ypos = physical_windowheight/lineheight;
+
+					if (!inwindow && current_text_y && (currentfont & PROP_FONT))
+						adhere_to_bottom = true;
+				}
+
+				hugo_settextpos(xpos, ypos);
+
+				/* An adjustment for non-fixed-width font lineheight */
+				if (adhere_to_bottom)
+					current_text_y = physical_windowbottom - lineheight;
+
+				currentpos = (xpos-1)*FIXEDCHARWIDTH;
+				currentline = ypos;
+
+				codeptr++;      /* skip EOL */
+				break;
+			}
+
+			case SELECT_T:
+				codeptr++;
+				break;
+
+			case CASE_T:
+			case IF_T:
+			case ELSEIF_T:
+			case ELSE_T:
+			case WHILE_T:
+			case FOR_T:
+				RunIf(0);
+				break;
+
+			case DO_T:
+				RunDo();
+				break;
+
+			case RUN_T:
+				codeptr++;
+				tempret = ret;
+				GetValue();     /* object.property to run */
+				ret = tempret;
+				if (game_version>=23) codeptr++; /* eol */
+				break;
+
+			case BREAK_T:
+			{
+				for (; stack_depth>0; stack_depth--)
+				{
+					if (code_block[stack_depth].brk)
+					{
+						codeptr = code_block[stack_depth].brk;
+#if defined (DEBUGGER)
+						dbnest = code_block[stack_depth].dbnest;
+#endif
+						--stack_depth;
+						goto LeaveBreak;
+					}
+				}
+				codeptr++;
+LeaveBreak:
+				break;
+			}
+
+			case RETURN_T:
+			{
+				codeptr++;
+				i = inexpr;	/* don't reuse tempinexpr */
+				inexpr = 1;
+				
+				/* Let 'return Routine()' or 'return obj.prop'
+				   set tail_recursion
+				*/
+				tail_recursion = 0;
+				tail_recursion_addr = 0;
+				
+				SetupExpr();
+				inexpr = (char)i;
+				
+				/* If either a routine or property routine call has
+				   determined it's valid, we can use tail-recursion
+				   (with tail_recursion_addr having been set up)
+				*/
+				if (tail_recursion)
+				{
+					HandleTailRecursion(tail_recursion_addr);
+					break;
+				}
+				else
+				{
+					/* Clear these to be safe */
+					tail_recursion = 0;
+					tail_recursion_addr = 0;
+
+					ret = EvalExpr(0);
+					retflag = true;
+					goto LeaveRunRoutine;
+				}
+			}
+
+			case JUMP_T:
+			{
+				codeptr = (long)PeekWord(codeptr + 1)*address_scale;
+#if defined (DEBUGGER)
+				if (MEM(codeptr)==LABEL_T)
+					dbnest = 0;	/* prevent "false" nesting */
+#endif
+				break;
+			}
+
+			case PARENT_T:
+			case SIBLING_T:
+			case CHILD_T:
+			case YOUNGEST_T:
+			case ELDEST_T:
+			case YOUNGER_T:
+			case ELDER_T:
+			{
+				inobj = true;
+
+				/* Note:  GetValue() would actually get
+				   the property/attribute to be set
+				*/
+				RunSet(GetVal());
+
+				inobj = false;
+				break;
+			}
+
+			case VAR_T:
+			case OBJECTNUM_T:
+			case VALUE_T:
+			case WORD_T:
+			case ARRAYDATA_T:
+			case ARRAY_T:
+				RunSet(-1);
+				break;
+
+			case ROUTINE_T:
+			case CALL_T:
+			{
+				switch (t)
+				{
+					case ROUTINE_T:
+					{
+						codeptr++;
+						routineptr = PeekWord(codeptr);
+						codeptr += 2;
+						break;
+					}
+					case CALL_T:
+					{
+						codeptr++;
+						routineptr = GetValue();
+					}
+				}
+
+				tempret = ret;
+				CallRoutine(routineptr);
+
+				if (MEM(codeptr)==DECIMAL_T || MEM(codeptr)==IS_T)
+					RunSet(ret);
+				else if ((t==CALL_T) && game_version>=23)
+					codeptr++; /* eol */
+
+				ret = tempret;
+
+				break;
+			}
+
+			case MOVE_T:
+			case REMOVE_T:
+				RunMove();
+				break;
+
+			case COLOR_T:
+			case COLOUR_T:
+			{
+				codeptr++;
+
+				/* Get foreground color */
+				fcolor = (char)GetValue();
+				/* If background color is given */
+				if (Peek(codeptr)==COMMA_T)
+				{
+					codeptr++;
+					bgcolor = (char)GetValue();
+
+					/* If input color is given */
+					if (Peek(codeptr)==COMMA_T)
+					{
+						codeptr++;
+						icolor = (char)GetValue();
+					}
+					else
+						icolor = fcolor;
+				}
+				else
+					icolor = fcolor;
+
+				/* Only set the actual pen color now if
+				   there is no text buffered
+				*/
+				if (pbuffer[0]=='\0')
+				{
+					hugo_settextcolor(fcolor);
+					hugo_setbackcolor(bgcolor);
+				}
+				
+				if (inwindow)
+					default_bgcolor = bgcolor;
+
+				codeptr++;      /* skip EOL */
+				break;
+			}
+
+			case PAUSE_T:
+			{
+				full = 0;
+				override_full = true;
+				codeptr++;
+				Flushpbuffer();
+				/* Flush the key buffer first */
+				while (hugo_iskeywaiting()) hugo_getkey();
+				wd[0] = (unsigned int)hugo_waitforkey();
+#if defined (DEBUGGER)
+				runaway_counter = 0;
+#endif
+				break;
+			}
+
+			case RUNEVENTS_T:
+				codeptr++;
+				RunEvents();
+				break;
+
+			case QUIT_T:
+				var[endflag] = -1;
+				break;
+
+			case INPUT_T:
+				RunInput();
+				full = 1;
+				override_full = true;
+				codeptr++;
+				break;
+
+			case SYSTEM_T:
+				RunSystem();
+				if (game_version>=23) codeptr++; /* eol */
+				break;
+
+			case CLS_T:
+			{
+				hugo_settextcolor(fcolor);
+				hugo_setbackcolor(bgcolor);
+				hugo_clearwindow();
+				hugo_settextpos(1, physical_windowheight/lineheight); /*+1);*/
+
+				if (!inwindow)
+				{
+					full = 0;
+				}
+				default_bgcolor = bgcolor;
+
+				codeptr++;
+				pbuffer[0] = '\0';
+				break;
+			}
+
+			case WRITEFILE_T:
+			case READFILE_T:
+				FileIO();
+				break;
+
+			case WRITEVAL_T:
+			{
+Writevalloop:
+				codeptr++;
+				i = GetValue();
+				if (ioblock)
+				{
+					if ((ioblock==2)
+						|| hugo_fputc(i%256, io)==EOF
+						|| hugo_fputc(i/256, io)==EOF)
+					{
+						ioerror = true;
+						retflag = true;
+						break;
+					}
+				}
+
+				if (Peek(codeptr)==COMMA_T)
+					goto Writevalloop;
+
+				if (game_version>=23) codeptr++; /* eol */
+				break;
+			}
+
+			case PICTURE_T:
+				DisplayPicture();
+				break;
+
+			case MUSIC_T:
+				PlayMusic();
+				break;
+
+			case SOUND_T:
+				PlaySample();
+				break;
+
+			case VIDEO_T:
+				PlayVideo();
+				break;
+
+			case ADDCONTEXT_T:
+				ContextCommand();
+				break;
+
+			/* Didn't match a command token, so throw up an
+			   "Unknown operation" error.
+			*/
+			default:
+				FatalError(UNKNOWN_OP_E);
+		}
+
+		defseg = gameseg;
+
+		if (retflag) goto LeaveRunRoutine;
+	}
+
+
+	/* Process the closing '}': */
+
+	codeptr++;
+
+#if defined (DEBUGGER)
+	if (--dbnest < 0) dbnest = 0;
+#endif
+	/* Continue executing this iteration of RunRoutine() if the
+	   '}' marks the end of a conditional block, i.e., one that
+	   didn't call RunRoutine() the way, e.g., 'window' does.
+	   Otherwise, get out of RunRoutine().
+	*/
+	if (code_block[stack_depth--].type > RUNROUTINE_BLOCK)
+	{
+		/* Skip a following 'elseif', 'else', or 'case'  */
+		t = MEM(codeptr);
+		while (t==ELSEIF_T || t==ELSE_T || t==CASE_T)
+		{
+			RunIf(1);
+			t = MEM(codeptr);
+		}
+
+		if (t==WHILE_T && code_block[stack_depth+1].type==DOWHILE_BLOCK)
+		{
+			codeptr+=3;
+			tempinexpr = inexpr;
+			inexpr = 1;
+			SetupExpr();
+			inexpr = tempinexpr;
+
+			if (EvalExpr(0))
+				codeptr = code_block[++stack_depth].returnaddr;
+			else
+				codeptr = code_block[stack_depth+1].brk;
+		}
+
+		/* Since this isn't a RUNROUTINE_BLOCK, keep running this
+		   iteration of RunRoutine()
+		*/
+		goto ContinueRunning;
+
+	}
+
+	if (stack_depth<0) stack_depth = 0;
+
+	if (var[endflag]) return;
+
+
+LeaveRunRoutine:
+
+#if defined (DEBUGGER)
+
+/*
+ *      Finally, do any debugger-required cleaning-up
+ */
+
+	/* As noted above, wascalled is true if this was a routine call
+	   as opposed to, e.g., a conditional block.  In the former case,
+	   it is necessary to print the "Returning from..." message.
+	*/
+	if (wascalled)
+	{
+		if (debugger_step_over)
+		{
+			if (--step_nest<=0)
+			{
+				debugger_step_over = false;
+				debugger_interrupt = true;
+
+				if (debugger_finish || step_nest < 0)
+				{
+					debugger_finish = false;
+					goto ReturnfromRoutine;
+				}
+			}
+		}
+
+		else if (!debugger_step_over)
+		{
+ReturnfromRoutine:
+			sprintf(debug_line, "(Returning %d", ret);
+
+			/* Since a complex property routine will give "<Routine>" as the
+			   routine name, skip those
+			*/
+			called_from = RoutineName(currentroutine);
+			if (!trace_comp_prop && called_from[0]!='<')
+				sprintf(debug_line+strlen(debug_line), " from %s", called_from);
+
+			if (old_currentroutine!=mainaddr && old_currentroutine!=initaddr
+				&& currentroutine!=mainaddr && currentroutine!=initaddr)
+			{
+				sprintf(debug_line+strlen(debug_line), " to %s", RoutineName(old_currentroutine));
+			}
+			strcat(debug_line, ")");
+			AddStringtoCodeWindow(debug_line);
+			AddStringtoCodeWindow("");
+
+			if ((signed)--window[VIEW_CALLS].count < 0)
+				window[VIEW_CALLS].count = 0;
+			window[VIEW_CALLS].changed = true;
+		}
+
+		currentroutine = old_currentroutine;
+	}
+
+/*#elif defined (DEBUG_CODE)
+	if (wascalled)
+		{sprintf(line, "[RETURNING %d]", ret);
+		AP(line);}
+*/
+#endif
+
+	return;
+}
+
+#ifndef SAVEGAMEDATA_REPLACED
+
+int Hugo::SaveGameData() {
+	int c, j;
+	int lbyte, hbyte;
+	long i;
+	int samecount = 0;
+
+	/* Write ID */
+	if (hugo_fputc(id[0], save)==EOF || hugo_fputc(id[1], save)==EOF) goto SaveError;
+
+	/* Write serial number */
+	if (hugo_fputs(serial, save)==EOF) goto SaveError;
+
+	/* Save variables */
+	for (c=0; c<MAXGLOBALS+MAXLOCALS; c++)
+	{
+		hbyte = (unsigned int)var[c] / 256;
+		lbyte = (unsigned int)var[c] - hbyte * 256;
+		if (hugo_fputc(lbyte, save)==EOF || hugo_fputc(hbyte, save)==EOF) goto SaveError;
+	}
+
+	/* Save objtable to end of code space */
+
+	if (hugo_fseek(game, objtable*16L, SEEK_SET)) goto SaveError;
+
+	for (i=0; i<=codeend-(long)(objtable*16L); i++)
+	{
+		if ((lbyte = hugo_fgetc(game))==EOF) goto SaveError;
+		hbyte = MEM(objtable*16L+i);
+
+		/* If memory same as original game file */
+		if (lbyte==hbyte && samecount<255) samecount++;
+
+		/* If memory differs (or samecount exceeds 1 byte) */
+		else
+		{
+			if (samecount)
+				if (hugo_fputc(samecount, save)==EOF) goto SaveError;
+
+			if (lbyte!=hbyte)
+			{
+				if (hugo_fputc(0, save)==EOF) goto SaveError;
+				if (hugo_fputc(hbyte, save)==EOF) goto SaveError;
+				samecount = 0;
+			}
+			else samecount = 1;
+		}
+	}
+	if (samecount)
+		if (hugo_fputc(samecount, save)==EOF) goto SaveError;
+
+	/* Save undo data */
+	
+	/* Save the number of turns in this port's undo stack */
+	hbyte = (unsigned int)MAXUNDO / 256;
+	lbyte = (unsigned int)MAXUNDO - hbyte*256;
+	if (hugo_fputc(lbyte, save)==EOF || hugo_fputc(hbyte, save)==EOF)
+		goto SaveError;
+	for (c=0; c<MAXUNDO; c++)
+	{
+		for (j=0; j<5; j++)
+		{
+			hbyte = (unsigned int)undostack[c][j] / 256;
+			lbyte = (unsigned int)undostack[c][j] - hbyte*256;
+			if (hugo_fputc(lbyte, save)==EOF || hugo_fputc(hbyte, save)==EOF)
+				goto SaveError;
+		}
+	}
+	if (hugo_fputc(undoptr-(undoptr/256)*256, save)==EOF || hugo_fputc(undoptr/256, save)==EOF)
+		goto SaveError;
+	if (hugo_fputc(undoturn-(undoturn/256)*256, save)==EOF || hugo_fputc(undoturn/256, save)==EOF)
+		goto SaveError;
+	if (hugo_fputc(undoinvalid, save)==EOF || hugo_fputc(undorecord, save)==EOF)
+		goto SaveError;
+		
+	return true;
+		
+SaveError:
+	return false;
+}
+
+#endif	// SAVEGAMEDATA_REPLACED
+
+int Hugo::RunSave() {
+#ifdef PALMOS
+	/* Prevent simultaneous access to the same db record */
+	int dummy = MEM(objtable*16L);
+#endif
+
+#if !defined (GLK)
+	save = NULL;
+
+	/* stdio implementation */
+	hugo_getfilename("to save", savefile);
+
+#if defined (DEBUGGER)
+	if (debugger_collapsing) return 1;
+#endif
+	if (!strcmp(line, gamefile)) return 0;
+	if (!strcmp(line, "")) return 0;
+	if (!hugo_overwrite(line)) return 0;
+	if (!(save = HUGO_FOPEN(line, "w+b"))) return 0;
+
+#else
+	/* Glk implementation */
+	frefid_t savefile;
+
+	save = NULL;
+
+	savefile = glk_fileref_create_by_prompt(fileusage_SavedGame | fileusage_BinaryMode,
+		filemode_Write, 0);
+	if (!savefile) return 0;
+	save = glk_stream_open_file(savefile, filemode_Write, 0);
+	glk_fileref_destroy(savefile);
+	if (!save) return 0;
+
+#endif	/* GLK */
+
+	if (!SaveGameData()) goto SaveError;
+	
+	if (hugo_fclose(save)) FatalError(WRITE_E);
+	save = NULL;
+
+#if !defined (GLK)
+	strcpy(savefile, line);
+#endif
+
+	return(1);
+
+SaveError:
+	if ((save) && hugo_fclose(save)) FatalError(WRITE_E);
+	save = NULL;
+	return 0;
+}
+
+int Hugo::RunScriptSet() {
+	remaining = 0;
+
+	switch (Peek(codeptr))
+	{
+		case SCRIPTON_T:
+		{
+			if (!script)
+			{
+#if !defined (GLK)
+				/* stdio implementation */
+				hugo_getfilename("to begin transcription (or printer name)", scriptfile);
+#if defined (DEBUGGER)
+				if (debugger_collapsing) return 1;
+#endif
+				if (!strcmp(line, "")) return 0;
+				if (!hugo_overwrite(line)) return 0;
+				if (!(script = HUGO_FOPEN(line, "wt")))
+					return (0);
+				strcpy(scriptfile, line);
+
+#else
+				/* Glk implementation */
+				frefid_t fref;
+
+				fref = glk_fileref_create_by_prompt(fileusage_Transcript | fileusage_TextMode,
+					filemode_Write, 0);
+				script = glk_stream_open_file(fref, filemode_Write, 0);
+				glk_fileref_destroy(fref);
+				if (!script) return (0);
+#endif	/* GLK */
+				return 1;
+			}
+			break;
+		}
+
+		case SCRIPTOFF_T:
+		{
+			if (script)
+			{
+				if (hugo_fclose(script)) return (0);
+				script = NULL;
+				return 1;
+			}
+			break;
+		}
+	}
+	return 0;
+}
+
+int Hugo::RunString() {
+	int i, pos;
+	unsigned int aaddr;                     /* array address   */
+	unsigned int dword;                     /* dictionary word */
+	unsigned int maxlen = 32767;
+
+	codeptr += 2;                           /* skip "(" */
+
+	aaddr = GetValue();
+	if (game_version>=22)
+	{
+		/* Convert to 16-bit word value */
+		aaddr*=2;
+
+		if (game_version>=23)
+		{
+			defseg = arraytable;
+			maxlen = PeekWord(aaddr);
+			defseg = gameseg;
+
+			/* Space for array length */
+			aaddr+=2;
+		}
+	}
+
+	if (Peek(codeptr)==COMMA_T) codeptr++;
+
+	dword = GetValue();
+
+	if (Peek(codeptr)==COMMA_T) codeptr++;
+
+	if (Peek(codeptr)!=CLOSE_BRACKET_T)
+		maxlen = GetValue();
+	if (Peek(codeptr)==CLOSE_BRACKET_T) codeptr++;
+
+	strcpy(line, GetWord(dword));
+
+	defseg = arraytable;
+	pos = 0;
+	for (i=0; i<(int)strlen(line) && i<(int)maxlen; i++, pos++)
+	{
+		char a;
+
+		SaveUndo(ARRAYDATA_T, aaddr, i, PeekWord(aaddr+i*2), 0);
+
+		a = line[i];
+		if (a=='\\')
+			++i, a = SpecialChar(line, &i);
+		PokeWord(aaddr+pos*2, a);
+	}
+	PokeWord(aaddr+pos*2, 0);
+
+	defseg = gameseg;
+
+	return (i);
+}
+
+int Hugo::RunSystem() {
+	codeptr++;
+
+	/* Since the obsolete form of the system command is unimplemented, 
+	   simply get the parameter (in order to skip it), and exit the
+	   function.
+	*/
+	if (game_version < 25)
+	{
+		GetValue();
+		return 0;
+	}
+	
+	/* Otherwise, process the following system calls: */
+	
+	codeptr++;			/* skip opening bracket */
+	
+	var[system_status] = 0;
+
+	Flushpbuffer();
+	
+	switch (GetValue())
+	{
+		case 11:		/* READ_KEY */
+			if (!hugo_iskeywaiting())
+				return 0;
+			else
+			{
+				full = 0;
+				return hugo_getkey();
+			}
+
+		case 21:		/* NORMALIZE_RANDOM */
+#if !defined (RANDOM)
+			_random.setSeed(1);
+#else
+			SRANDOM(1);
+#endif
+			break;
+		case 22:		/* INIT_RANDOM */
+		{
+#if !defined (RANDOM)
+			_random.setSeed(g_system->getMillis());
+#else
+			time_t seed;
+			SRANDOM((unsigned int)time((time_t *)&seed));
+#endif
+			break;
+		}			
+		case 31:		/* PAUSE_SECOND */
+			if (!hugo_timewait(1))
+				var[system_status] = STAT_UNAVAILABLE;
+			break;
+			
+		case 32:		/* PAUSE_100TH_SECOND */
+			if (!hugo_timewait(100))
+				var[system_status] = STAT_UNAVAILABLE;
+			break;
+
+		case 41:		/* GAME_RESET */
+		{
+			if (game_reset)
+			{
+				game_reset = 0;
+				return true;
+			}
+			return false;
+		}
+
+		case 51:		/* SYSTEM_TIME */
+		{
+#ifndef NO_STRFTIME
+			TimeDate td;
+			g_system->getTimeAndDate(td);
+			sprintf(parseerr, "%Y-%m-%d %H:%M:%S", td.tm_year, td.tm_mon, td.tm_mday,
+				td.tm_hour, td.tm_min, td.tm_sec);
+#else
+			hugo_gettimeformatted(parseerr);
+#endif
+			return true;
+		}
+
+		case 61:		/* MINIMAL_INTERFACE */
+#ifdef MINIMAL_INTERFACE
+			return true;
+#else
+			return false;
+#endif
+
+		default:
+			var[system_status] = STAT_UNAVAILABLE;
+	}
+	
+	return 0;
+}
+
+void Hugo::SaveWindowData(SAVED_WINDOW_DATA *spw) {
+	spw->left = physical_windowleft;
+	spw->top = physical_windowtop;
+	spw->right = physical_windowright;
+	spw->bottom = physical_windowbottom;
+	spw->width = physical_windowwidth;
+	spw->height = physical_windowheight;
+	spw->currentfont = currentfont;
+	spw->charwidth = charwidth;
+	spw->lineheight = lineheight;
+	spw->currentpos = currentpos;
+	spw->currentline = currentline;
+}
+
+void Hugo::RestoreWindowData(SAVED_WINDOW_DATA *spw) {
+	physical_windowleft = spw->left;
+	physical_windowtop = spw->top;
+	physical_windowright = spw->right;
+	physical_windowbottom = spw->bottom;
+	physical_windowwidth = spw->width;
+	physical_windowheight = spw->height;
+
+	charwidth = spw->charwidth;
+	lineheight = spw->lineheight;
+	currentpos = spw->currentpos;
+	currentline = spw->currentline;
+
+/*	if (currentfont!=spw->currentfont) hugo_font((currentfont = spw->currentfont)); */
+}
+
+void Hugo::RunWindow() {
+	int top, bottom, left, right;
+	struct SAVED_WINDOW_DATA restorewindow;
+	int temp_current_text_y;
+	char restore_default_bgcolor;
+	int tempfull;
+	int temp_stack_depth = stack_depth;
+	HUGO_FILE tempscript;
+#ifdef MINIMAL_WINDOWING
+	int last_lowest_windowbottom = lowest_windowbottom;
+#endif
+
+#if defined (DEBUGGER)
+	unsigned char param_type;
+	int tempdbnest;
+	long param_start;
+#endif
+
+	Flushpbuffer();
+	tempfull = full;
+	full = 0;
+	override_full = false;
+
+	temp_current_text_y = current_text_y;
+
+	tempscript = script;
+	script = false;
+	restore_default_bgcolor = default_bgcolor;
+
+	/* v2.4 is the first version to support proper windowing */
+	if (game_version>=24)
+	{
+		/* Set up default top, left, etc. as character coordinates,
+		   and save the current physical window data
+		*/
+		left = physical_windowleft/FIXEDCHARWIDTH + 1;
+		top = physical_windowtop/FIXEDLINEHEIGHT + 1;
+		right = physical_windowright/FIXEDCHARWIDTH + 1;
+		bottom = physical_windowbottom/FIXEDLINEHEIGHT + 1;
+
+		SaveWindowData(&restorewindow);
+
+		/* if "window x1, y1, x2, y2" or "window n"... */
+		if (MEM(++codeptr)!=EOL_T)
+		{
+#if defined (DEBUGGER)
+			param_type = MEM(codeptr);
+			param_start = codeptr;
+#endif
+			left = GetValue();
+			if (MEM(codeptr++)==COMMA_T)
+			{
+				top = GetValue();
+				if (MEM(codeptr++)==COMMA_T)
+				{
+					right = GetValue();
+					if (MEM(codeptr++)==COMMA_T)
+					{
+						bottom = GetValue();
+						codeptr++;
+					}
+				}
+			}
+
+			/* if only one parameter, i.e., "window n" */
+			else
+			{
+				if (left!=0)
+				{
+					bottom = left;
+					top = 1;
+					left = 1;
+					right = SCREENWIDTH/FIXEDCHARWIDTH;
+				}
+
+				/* "window 0" restores full screen without
+				   running a code block
+				*/
+				else
+				{
+#if defined (DEBUGGER)
+					/* Here, check to see if left was 0 but the
+					   statement wasn't really "window 0", but
+					   rather something that evaluated to zero
+					*/
+					if (runtime_warnings)
+					{
+					  if (param_type!=VALUE_T || param_start!=codeptr-4)
+					    RuntimeWarning("Window size evaluates to zero");
+					}
+#endif
+					left = 1, top = 1;
+					right = SCREENWIDTH/FIXEDCHARWIDTH;
+					bottom = SCREENHEIGHT/FIXEDLINEHEIGHT;
+					physical_lowest_windowbottom = lowest_windowbottom = 0;
+					hugo_settextwindow(left, top, right, bottom);
+					goto LeaveWindow;
+				}
+			}
+		}
+
+		/* ...or just "window", so use last window defaults */
+		else
+		{
+			codeptr++;      /* skip EOL */
+
+			left = last_window_left;
+			top = last_window_top;
+			right = last_window_right;
+			bottom = last_window_bottom;
+		}
+
+		/* Remember, these are character/text coordinates */
+		if (top < 1) top = 1;
+		if (left < 1) left = 1;
+		if (bottom < 1) bottom = 1;
+		if (right < 1) right = 1;
+		if (top > SCREENHEIGHT/FIXEDLINEHEIGHT)
+			top = SCREENHEIGHT/FIXEDLINEHEIGHT;
+		if (left > SCREENWIDTH/FIXEDCHARWIDTH)
+			left = SCREENWIDTH/FIXEDCHARWIDTH;
+		if (bottom > SCREENHEIGHT/FIXEDLINEHEIGHT)
+			bottom = SCREENHEIGHT/FIXEDLINEHEIGHT;
+		if (right > SCREENWIDTH/FIXEDCHARWIDTH)
+			right = SCREENWIDTH/FIXEDCHARWIDTH;
+
+		/* Set the new text window */
+		inwindow = true;
+		hugo_settextwindow(left, top, right, bottom);
+		hugo_settextpos(1, 1);
+
+#if defined (DEBUGGER)
+		tempdbnest = dbnest++;
+#endif
+		SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+		RunRoutine(codeptr);
+
+#if defined (DEBUGGER)
+		dbnest = tempdbnest;
+#endif
+		stack_depth = temp_stack_depth;
+
+		Flushpbuffer();
+
+		/* Restore the old window parameters */
+		last_window_top = top;
+		last_window_bottom = bottom;
+		last_window_left = left;
+		last_window_right = right;
+
+		/* Figure out what the lowest window bottom is that we need
+		   to protect from scrolling
+		*/
+		if (bottom > lowest_windowbottom)
+			lowest_windowbottom = bottom;
+
+#ifdef MINIMAL_WINDOWING
+		if (minimal_windowing && illegal_window)
+			lowest_windowbottom = last_lowest_windowbottom;
+#endif
+		/* (error situation--shouldn't happen) */
+		if (lowest_windowbottom>=SCREENHEIGHT/FIXEDLINEHEIGHT)
+			lowest_windowbottom = 0;
+
+		/* Restore the old text window */
+		RestoreWindowData(&restorewindow);
+
+		inwindow = false;
+		hugo_settextwindow(physical_windowleft/FIXEDCHARWIDTH + 1,
+			lowest_windowbottom + 1,
+			physical_windowright/FIXEDCHARWIDTH + 1,
+			physical_windowbottom/FIXEDLINEHEIGHT + 1);
+
+		physical_lowest_windowbottom = lowest_windowbottom*FIXEDLINEHEIGHT;
+	}
+
+	/* v2.3 and earlier supported a very simple version of
+	   windowing:  mainly just moving the top/scroll-off line
+	   of the printable area to the bottom of the text printed
+	   in the "window" block
+	*/
+	else
+	{
+		inwindow = true;
+		hugo_settextwindow(1, 1,
+	       		SCREENWIDTH/FIXEDCHARWIDTH,
+		        SCREENHEIGHT/FIXEDLINEHEIGHT);
+		hugo_settextpos(1, 1);
+
+		SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+		RunRoutine(++codeptr);
+		Flushpbuffer();
+
+		inwindow = false;
+
+		stack_depth = temp_stack_depth;
+
+		hugo_settextwindow(1, full+1,
+		        SCREENWIDTH/FIXEDCHARWIDTH,
+			SCREENHEIGHT/FIXEDLINEHEIGHT);
+
+		physical_lowest_windowbottom = full*lineheight;
+	}
+
+LeaveWindow:
+
+	current_text_y = temp_current_text_y;
+
+#ifndef PALMOS
+	if (!current_text_y)
+		hugo_settextpos(1, physical_windowheight/lineheight);
+#endif
+	current_text_x = 0;
+	currentpos = 0;
+
+	default_bgcolor = restore_default_bgcolor;
+	script = tempscript;
+
+	if (!override_full)
+		full = tempfull;
+	override_full = false;
+
+	just_left_window = true;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index f7d39a7..bd8575b 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -68,7 +68,10 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 		debugger_finish(false), debugger_run(false), debugger_interrupt(false),
 		debugger_skip(false), runtime_error(false), currentroutine(false),
 		complex_prop_breakpoint(false), trace_complex_prop_routine(false), routines(0),
-		properties(0), current_locals(0), this_codeptr(0), debug_workspace(0), attributes(0)
+		properties(0), current_locals(0), this_codeptr(0), debug_workspace(0), attributes(0),
+		original_dictcount(0), buffered_code_lines(0), debugger_has_stepped_back(false),
+		debugger_step_back(false), debugger_collapsing(0), runaway_counter(0), history_count(0),
+		active_screen(0), step_nest(0), history_last(0)
 #endif
 		{
 	// heexpr
@@ -107,6 +110,8 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
 	Common::fill(&propertyname[0], &propertyname[MAX_PROPERTY], (char *)nullptr);
 	Common::fill(&codeline[0][0], &codeline[9][100], 0);
 	Common::fill(&localname[0][0], &localname[9][100], 0);
+	Common::fill(&code_history[0], &code_history[MAX_CODE_HISTORY], 0);
+	Common::fill(&dbnest_history[0], &dbnest_history[MAX_CODE_HISTORY], 0);
 #endif
 }
 
@@ -122,7 +127,7 @@ void Hugo::runGame() {
 
 	LoadGame();
 
-	PlayGame();
+	playGame();
 
 	hugo_cleanup_screen();
 
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index aa4deee..d022b1f 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -24,6 +24,7 @@
 #define GLK_HUGO_HUGO
 
 #include "common/scummsys.h"
+#include "common/str.h"
 #include "glk/glk_api.h"
 #include "glk/hugo/htokens.h"
 #include "glk/hugo/hugo_defines.h"
@@ -177,8 +178,8 @@ private:
 	char parse_called_twice;
 	char reparse_everything;
 	char punc_string[64];					///< punctuation string
+	bool  full_buffer;
 
-	char full_buffer;
 	/**
 	 * to MatchObject()
 	 * Necessary for proper disambiguation when addressing a character;
@@ -223,13 +224,12 @@ private:
 	bool debugger_interrupt;
 	bool debugger_skip;
 	bool runtime_error;
-	int currentroutine;
+	uint currentroutine;
 	bool complex_prop_breakpoint;
 	bool trace_complex_prop_routine;
 	char *objectname[MAX_OBJECT];
 	char *propertyname[MAX_PROPERTY];
-//	CODE code[999];
-	CALL call[999];
+	CALL call[MAXCALLS];
 	int routines;
 	int properties;
 	WINDOW window[99];
@@ -239,6 +239,20 @@ private:
 	long this_codeptr;
 	int debug_workspace;
 	int attributes;
+	int original_dictcount;
+	int buffered_code_lines;
+	bool debugger_has_stepped_back;
+	bool debugger_step_back;
+	int debugger_collapsing;
+	int runaway_counter;
+	int history_count;
+	int active_screen;
+	int step_nest;
+	BREAKPOINT breakpoint[MAXBREAKPOINTS];
+	BREAKPOINT watch[MAXBREAKPOINTS];
+	int code_history[MAX_CODE_HISTORY];
+	int dbnest_history[MAX_CODE_HISTORY];
+	int history_last;
 #endif
 private:
 	/**
@@ -822,6 +836,86 @@ private:
 	/**@}*/
 
 	/**
+	* \defgroup herun
+	* @{
+	*/
+
+	void RunDo();
+
+	void RunEvents();
+
+	void playGame();
+
+	void RunIf(char override);
+
+	void RunInput();
+
+	/**
+	 * (All the debugger range-checking is important because invalid memory writes due
+	 * to invalid object location calculations are a good way to crash the system.)
+	 */
+	void RunMove();
+
+	void RunPrint();
+
+	int RunRestart();
+
+	int RestoreGameData();
+
+	int RunRestore();
+
+	/**
+	 * This is the main loop for running each line of code in sequence;
+	 * the main switch statement is based on the first token in each line.
+	 * 
+	 * This routine is relatively complex, especially given the addition of debugger control.
+	 * Basically it is structured like this:
+	 *
+	 * 1.  If this is the debugger build, see what debugger information has to be set up upon
+	 * calling this block of code
+	 *
+	 * 2.  Get the next token, and as long as it isn't CLOSE_BRACE_T ('}')...
+	 *
+	 * 3.  ...If this is the debugger build, see if there is a standing debugger_interrupt
+	 * to pass control back to the debugger, and perform all operations for stepping
+	 * tracing, breakpoints, etc.
+	 *
+	 * 4.  ...See what token we're dealing with and execute accordingly
+	 *
+	 * 5.  ...Loop back to (2)
+	 *
+	 * 6.  If this is the debugger build, do whatever is necessary to tidy up after finishing
+	 * this block of code
+	 *
+	 * There's a bit of a trick involved since the original language design uses "{...}"
+	 * structures for both conditionals and blocks that necessitate another (i.e., nested) call
+	 * to RunRoutine(). The call_block structure array and stack_depth variable are the
+	 * navigation guides.
+	 */
+	void RunRoutine(long addr);
+
+	int SaveGameData();
+
+	int RunSave();
+
+	int RunScriptSet();
+
+	/**
+	 * As in 'x = string(<array>, "<string>"[, maxlen]'.
+	 */
+	int RunString();
+
+	int RunSystem();
+
+	void SaveWindowData(SAVED_WINDOW_DATA *spw);
+
+	void RestoreWindowData(SAVED_WINDOW_DATA *spw);
+
+	void RunWindow();
+
+	/**@}*/
+
+	/**
 	* \defgroup Miscellaneous
 	* @{
 	*/
@@ -833,10 +927,71 @@ private:
 	int hugo_fgetc(Common::SeekableReadStream *s) {
 		return s->readByte();
 	}
+	int hugo_fgetc(strid_t s) {
+		Common::SeekableReadStream *ws = *s;
+		return hugo_fgetc(ws);
+	}
+
+	int hugo_fputc(int c, Common::WriteStream *s) {
+		s->writeByte(c);
+		return s->err() ? EOF : 0;
+	}
+	int hugo_fputc(int c, strid_t s) {
+		Common::WriteStream *ws = *s;
+		return hugo_fputc(c, ws);
+	}
+
+	char *hugo_fgets(char *buf, int max, Common::SeekableReadStream *s) {
+		char *ptr = buf;
+		char c;
+		while (s->pos() < s->size() && (c = hugo_fgetc(s)) != '\n')
+			*ptr++ = c;
+		return buffer;
+	}
+	char *hugo_fgets(char *buf, int max, strid_t s) {
+		Common::SeekableReadStream *rs = *s;
+		return hugo_fgets(buf, max, rs);
+	}
+
+	size_t hugo_fread(void *ptr, size_t size, size_t count, Common::SeekableReadStream *s) {
+		return s->read(ptr, size * count);
+	}
+
+	int hugo_fprintf(Common::WriteStream *s, const char *fmt, ...) {
+		va_list va;
+		va_start(va, fmt);
+		Common::String text = Common::String::vformat(fmt, va);
+		va_end(va);
+
+		s->write(text.c_str(), text.size());
+		return s->err() ? -1 : 0;
+	}
+	int hugo_fprintf(strid_t s, const char *fmt, ...) {
+		va_list va;
+		va_start(va, fmt);
+		Common::String text = Common::String::vformat(fmt, va);
+		va_end(va);
+
+		Common::WriteStream *str = *s;
+		str->write(text.c_str(), text.size());
+		return str->err() ? -1 : 0;
+	}
+
+	int hugo_fputs(const char *str, Common::WriteStream *s) {
+		return s->write(str, strlen(str)) == strlen(str) ? 0 : -1;
+	}
+	int hugo_fputs(const char *str, strid_t s) {
+		Common::WriteStream *ws = *s;
+		return hugo_fputs(str, ws);
+	}
 
 	bool hugo_ferror(Common::SeekableReadStream *s) const {
 		return s->err();
 	}
+	bool hugo_ferror(strid_t s) const {
+		Common::SeekableReadStream *rs = *s;
+		return hugo_ferror(rs);
+	}
 
 	long hugo_ftell(Common::SeekableReadStream *s) {
 		return s->pos();
@@ -851,10 +1006,6 @@ private:
 		error("%s", line);
 	}
 
-	size_t hugo_fread(void *ptr, size_t size, size_t count, Common::SeekableReadStream *s) {
-		return s->read(ptr, size * count);
-	}
-
 	uint hugo_rand() {
 		return _random.getRandomNumber(0xffffff);
 	}
@@ -894,9 +1045,23 @@ private:
 
 	void SwitchtoDebugger() {}
 
+	void Debugger() {}
+	
+	void UpdateDebugScreen() {}
+
+	void SwitchtoGame() {}
+
 	void DebuggerFatal(DEBUGGER_ERROR err) { error("Debugger error"); }
 
+	void AddLinetoCodeWindow(int lineNum) {}
+
 	void RecoverLastGood() {}
+
+	void SetupWatchEval(int num) {}
+
+	bool EvalWatch() { return false; }
+
+	void RunSet(int v) {}
 #endif
 public:
 	/**
@@ -925,20 +1090,16 @@ public:
 	virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
 
 	// TODO: Stubs to be Properly implemented
-	void PlayGame() {}
 	void hugo_closefiles() {}
-	void RunRoutine(long v) {}
 	unsigned int FindWord(const char *a) { return 0; }
 	void hugo_stopsample() {}
 	void hugo_stopmusic() {}
 	int hugo_hasgraphics() { return 0; }
 	int hugo_writetoscript(const char *s) { return 0; }
-	short RunSave() { return 0; }
-	short RunRestore() { return 0; }
-	short RunScriptSet() { return 0; }
-	short RunRestart() { return 0; }
-	short RunString() { return 0; }
-	short RunSystem() { return 0; }
+	void DisplayPicture() {}
+	void PlayMusic() {}
+	void PlaySample() {}
+	void PlayVideo() {}
 };
 
 } // End of namespace Hugo
diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h
index 5d69c84..02cfab1 100644
--- a/engines/glk/hugo/hugo_defines.h
+++ b/engines/glk/hugo/hugo_defines.h
@@ -32,7 +32,7 @@ namespace Hugo {
 #define HEREVISION 3
 #define HEINTERIM ".0"
 #define GLK
-#define DEBUGGER
+#define DEBUGGER 1
 
 #define MAXOBJLIST 32
 #define MAX_CONTEXT_COMMANDS	32
@@ -44,9 +44,10 @@ namespace Hugo {
 #define MAX_MOBJ 16     /* maximum number of matchable object words */
 #define MAXBUFFER 255
 #define MAXUNDO 1024
-
+#define MAXCALLS 99
+#define MAXBREAKPOINTS 99
+#define MAX_CODE_HISTORY 99
 #define CHARWIDTH 1
-#define STAT_UNAVAILABLE (-1)
 
 #define HUGO_FILE	strid_t
 #define MAXPATH         256
@@ -149,13 +150,20 @@ browsing.
 #define TAIL_RECURSION_ROUTINE          (-1)
 #define TAIL_RECURSION_PROPERTY         (-2)
 
+#define STAT_UNAVAILABLE	((short)-1)
+
+#define PRINTFATALERROR(a)	error("%s", a)
+
 #if defined (DEBUGGER)
 #define VIEW_CALLS 0
 #define VIEW_LOCALS 1
 #define CODE_WINDOW 2
-#endif
+#define VIEW_BREAKPOINTS 3
+#define VIEW_WATCH 4
 
-#define PRINTFATALERROR(a)	error("%s", a)
+#define FORCE_REDRAW 1
+
+#endif
 
 } // End of namespace Hugo
 } // End of namespace Glk
diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h
index dacde03..fcdaa8e 100644
--- a/engines/glk/hugo/hugo_types.h
+++ b/engines/glk/hugo/hugo_types.h
@@ -100,6 +100,13 @@ struct pobject_structure {
 	pobject_structure() : obj(0), type(0) {}
 };
 
+struct SAVED_WINDOW_DATA {
+	int left, top, right, bottom;
+	int width, height, charwidth, lineheight;
+	int currentpos, currentline;
+	int currentfont;
+};
+
 /**
  * Structure used for navigating {...} blocks:
  */
@@ -133,8 +140,19 @@ struct CALL {
 
 struct WINDOW {
 	int count;
+	bool changed;
+
+	WINDOW() : count(99), changed(false) {}
+};
+
+struct BREAKPOINT {
+	bool isbreak;
+	long addr;
+	const char *in;
+	int count;
 
-	WINDOW() : count(99) {}
+	BREAKPOINT() : isbreak(false), addr(0), in(nullptr), count(0) {
+	}
 };
 #endif
 
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 00f5e35..00641dd 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -77,6 +77,7 @@ MODULE_OBJS := \
 	hugo/hemisc.o \
 	hugo/heobject.o \
 	hugo/heparse.o \
+	hugo/herun.o \
 	hugo/htokens.o \
 	hugo/hugo.o \
 	hugo/stringfn.o \


Commit: 96ebd81e5f29e9cde3d83c4af0b9166cd1f40b26
    https://github.com/scummvm/scummvm/commit/96ebd81e5f29e9cde3d83c4af0b9166cd1f40b26
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-05-11T16:15:07+10:00

Commit Message:
GLK: HUGO: NULL to nullptr, Amiga compilation fix

Changed paths:
    engines/glk/alan2/execute.cpp
    engines/glk/alan2/parse.cpp
    engines/glk/alan2/parse.h
    engines/glk/alan2/saveload.cpp
    engines/glk/frotz/mem.cpp
    engines/glk/hugo/heglk.cpp
    engines/glk/hugo/hemisc.cpp
    engines/glk/hugo/herun.cpp


diff --git a/engines/glk/alan2/execute.cpp b/engines/glk/alan2/execute.cpp
index 8359c47..4fc0acb 100644
--- a/engines/glk/alan2/execute.cpp
+++ b/engines/glk/alan2/execute.cpp
@@ -234,7 +234,7 @@ bool Execute::confirm(MsgKind msgno) {
 #ifdef USE_READLINE
 	if (!readline(buf)) return true;
 #else
-	if (gets(buf) == NULL) return true;
+	if (gets(buf) == nullptr) return true;
 #endif
 
 #endif
@@ -260,7 +260,7 @@ void Execute::quit() {
 #ifdef USE_READLINE
 	if (!readline(buf)) terminate(0);
 #else
-	if (gets(buf) == NULL) terminate(0);
+	if (gets(buf) == nullptr) terminate(0);
 #endif
 #endif
 
@@ -682,17 +682,17 @@ void Execute::dscrobj(Aword obj) {
 
 
 void Execute::dscract(Aword act) {
-	ScrElem *scr = NULL;
+	ScrElem *scr = nullptr;
 
 	if (_acts[act - ACTMIN].script != 0) {
 		for (scr = (ScrElem *) addrTo(_acts[act - ACTMIN].scradr); !endOfTable(scr); scr++)
 			if (scr->code == _acts[act - ACTMIN].script)
 				break;
 		if (endOfTable(scr))
-			scr = NULL;
+			scr = nullptr;
 	}
 
-	if (scr != NULL && scr->dscr != 0)
+	if (scr != nullptr && scr->dscr != 0)
 		_vm->_interpreter->interpret(scr->dscr);
 	else if (_acts[act - ACTMIN].dscr != 0)
 		_vm->_interpreter->interpret(_acts[act - ACTMIN].dscr);
diff --git a/engines/glk/alan2/parse.cpp b/engines/glk/alan2/parse.cpp
index aac858a..8e6427d 100644
--- a/engines/glk/alan2/parse.cpp
+++ b/engines/glk/alan2/parse.cpp
@@ -75,7 +75,7 @@ char *Parser::gettoken(char *tokenBuffer) {
 	static char *marker;
 	static char oldch;
 
-	if (tokenBuffer == NULL)
+	if (tokenBuffer == nullptr)
 		*marker = oldch;
 	else
 		marker = tokenBuffer;
@@ -92,7 +92,7 @@ char *Parser::gettoken(char *tokenBuffer) {
 		while (*marker != '\"') marker++;
 		marker++;
 	} else if (*marker == '\0' || *marker == '\n')
-		return NULL;
+		return nullptr;
 	else
 		marker++;
 
@@ -128,7 +128,7 @@ void Parser::agetline() {
 			quit();
 		}
 #else
-		if (fgets(buf, LISTLEN, stdin) == NULL) {
+		if (fgets(buf, LISTLEN, stdin) == nullptr) {
 			newline();
 			quit();
 		}
@@ -148,12 +148,12 @@ void Parser::agetline() {
 
 		token = gettoken(isobuf);
 
-		if (token != NULL && strcmp("debug", token) == 0 && _vm->header->debug) {
+		if (token != nullptr && strcmp("debug", token) == 0 && _vm->header->debug) {
 			dbgflg = true;
 			debug();
-			token = NULL;
+			token = nullptr;
 		}
-	} while (token == NULL);
+	} while (token == nullptr);
 
   eol = false;
   lin = 1;
@@ -212,7 +212,7 @@ void Parser::scan() {
 			unknown(token);
 
 		wrds[i] = EOF;
-		eol = (token = gettoken(NULL)) == NULL;
+		eol = (token = gettoken(nullptr)) == nullptr;
 	} while (!eol);
 }
 
@@ -377,10 +377,10 @@ void Parser::unambig(ParamElem plst[]) {
 	static ParamElem *savlst;	// Saved list for backup at EOF
 	int firstWord, lastWord;	// The words the player used
 
-	if (refs == NULL)
+	if (refs == nullptr)
 		refs = new ParamElem[MAXENTITY + 1];
 
-	if (savlst == NULL)
+	if (savlst == nullptr)
 		savlst = new ParamElem[MAXENTITY + 1];
 
 	if (isLiteral(wrds[wrdidx])) {
@@ -490,12 +490,12 @@ void Parser::unambig(ParamElem plst[]) {
 }
 
 void Parser::simple(ParamElem olst[]) {
-	static ParamElem *tlst = NULL;
+	static ParamElem *tlst = nullptr;
 	int savidx = wrdidx;
 	bool savplur = false;
 	int i;
 
-	if (tlst == NULL)
+	if (tlst == nullptr)
 		tlst = new ParamElem[MAXENTITY + 1];
 
 	tlst[0].code = (Aword)EOF;
@@ -549,9 +549,9 @@ void Parser::complex(ParamElem olst[]) {
 	// they work on words.Below all is converted to indices into the
 	// entity tables.Particularly this goes for literals...
 
-	static ParamElem *alst = NULL;
+	static ParamElem *alst = nullptr;
 
-	if (alst == NULL)
+	if (alst == nullptr)
 		alst = new ParamElem[MAXENTITY + 1];
 
 	if (isAll(wrds[wrdidx])) {
@@ -653,18 +653,18 @@ AltElem *Parser::findalt(Aword vrbsadr, Aword param) {
 	AltElem *alt;
 
 	if (vrbsadr == 0)
-		return NULL;
+		return nullptr;
 
 	for (vrb = (VrbElem *)addrTo(vrbsadr); !endOfTable(vrb); vrb++) {
 		if ((int)vrb->code == _vm->cur.vrb) {
 			for (alt = (AltElem *)addrTo(vrb->alts); !endOfTable(alt); alt++)
 				if (alt->param == param || alt->param == 0)
 					return alt;
-			return NULL;
+			return nullptr;
 		}
 	}
 
-	return NULL;
+	return nullptr;
 }
 
 bool Parser::trycheck(Aaddr adr, bool act) {
@@ -732,10 +732,10 @@ void Parser::tryMatch(ParamElem mlstArr[]) {
 	ClaElem *cla;			// Pointer to class definitions
 	bool anyPlural = false;	// Any parameter that was plural?
 	int i, p;
-	static ParamElem *tlst = NULL; // List of params found by complex()
-	static bool *checked = NULL; // Corresponding parameter checked?
+	static ParamElem *tlst = nullptr; // List of params found by complex()
+	static bool *checked = nullptr; // Corresponding parameter checked?
 
-	if (tlst == NULL) {
+	if (tlst == nullptr) {
 		tlst = new ParamElem[MAXENTITY + 1];
 		checked = new bool[MAXENTITY + 1];
 	}
@@ -930,7 +930,7 @@ void Parser::action(ParamElem plst[]) {
 }
 
 void Parser::parse() {
-	if (mlst == NULL) {		// Allocate large enough paramlists
+	if (mlst == nullptr) {		// Allocate large enough paramlists
 		mlst = new ParamElem[MAXENTITY + 1];
 		mlst[0].code = (Aword)EOF;
 		pmlst = new ParamElem[MAXENTITY + 1];
diff --git a/engines/glk/alan2/parse.h b/engines/glk/alan2/parse.h
index 1e3601c..c5b223b 100644
--- a/engines/glk/alan2/parse.h
+++ b/engines/glk/alan2/parse.h
@@ -71,7 +71,7 @@ private:
 	int listLength(ParamElem a[]);
 	
 	/**
-	 * Compact a list, i.e remove any NULL elements
+	 * Compact a list, i.e remove any nullptr elements
 	 */
 	void listCompact(ParamElem a[]);
 	
diff --git a/engines/glk/alan2/saveload.cpp b/engines/glk/alan2/saveload.cpp
index 2d92d34..838c258 100644
--- a/engines/glk/alan2/saveload.cpp
+++ b/engines/glk/alan2/saveload.cpp
@@ -39,7 +39,7 @@ void SaveLoad::save() {
 	// TODO
 #if 0
 	frefid_t fref = glk_fileref_create_by_prompt(fileusage_SavedGame, filemode_Write, 0);
-	if (fref == NULL)
+	if (fref == nullptr)
 		_vm->printError(M_SAVEFAILED);
 
 	strcpy(str, garglk_fileref_get_name(fref));
@@ -53,12 +53,12 @@ void SaveLoad::save() {
 
 	// TODO
 #if 0
-	if ((savfil = fopen(str, READ_MODE)) != NULL)
+	if ((savfil = fopen(str, READ_MODE)) != nullptr)
 		// It already existed
 		if (!confirm(M_SAVEOVERWRITE))
 			_vm->printError(MSGMAX);            // Return to player without saying anything
 
-	if ((savfil = fopen(str, WRITE_MODE)) == NULL)
+	if ((savfil = fopen(str, WRITE_MODE)) == nullptr)
 		_vm->printError(M_SAVEFAILED);
 #endif
 
@@ -124,7 +124,7 @@ void SaveLoad::restore() {
 	// TODO
 #if 0
 	frefid_t fref = glk_fileref_create_by_prompt(fileusage_SavedGame, filemode_Read, 0);
-	if (fref == NULL)
+	if (fref == nullptr)
 		_vm->printError(M_SAVEFAILED);
 
 	strcpy(str, garglk_fileref_get_name(fref));
diff --git a/engines/glk/frotz/mem.cpp b/engines/glk/frotz/mem.cpp
index 32e3ab3..57dc5b5 100644
--- a/engines/glk/frotz/mem.cpp
+++ b/engines/glk/frotz/mem.cpp
@@ -224,9 +224,9 @@ void Mem::free_undo(int count) {
 		undo_count--;
 	}
 	if (first_undo)
-		first_undo->prev = NULL;
+		first_undo->prev = nullptr;
 	else
-		last_undo = NULL;
+		last_undo = nullptr;
 }
 
 void Mem::reset_memory() {
diff --git a/engines/glk/hugo/heglk.cpp b/engines/glk/hugo/heglk.cpp
index 4884d0f..f6f4081 100644
--- a/engines/glk/hugo/heglk.cpp
+++ b/engines/glk/hugo/heglk.cpp
@@ -52,7 +52,7 @@ void Hugo::hugo_getline(const char *prmpt) {
 	window that hasn't been created, switch as a failsafe
 	to mainwin
 	*/
-	if (currentwin == NULL)
+	if (currentwin == nullptr)
 		glk_set_window(currentwin = mainwin);
 
 	/* Print prompt */
@@ -97,7 +97,7 @@ int Hugo::hugo_waitforkey() {
 	window that hasn't been created, switch as a failsafe
 	to mainwin
 	*/
-	if (currentwin == NULL)
+	if (currentwin == nullptr)
 		glk_set_window(currentwin = mainwin);
 
 #if defined (NO_KEYPRESS_CURSOR)
@@ -211,7 +211,7 @@ void Hugo::hugo_clearwindow() {
 	not really a window
 	*/
 	if (inwindow && currentwin == mainwin) return;
-	if (currentwin == NULL) return;
+	if (currentwin == nullptr) return;
 
 	glk_window_clear(currentwin);
 
@@ -227,7 +227,7 @@ void Hugo::hugo_clearwindow() {
 		stream_result_t sr;
 
 		glk_window_close(auxwin, &sr);
-		auxwin = NULL;
+		auxwin = nullptr;
 		glk_set_window(currentwin = mainwin);
 	}
 
@@ -256,11 +256,11 @@ void Hugo::hugo_settextwindow(int left, int top, int right, int bottom) {
 		in_valid_window = false;
 
 		/* Glk-illegal floating window; setting currentwin
-		to NULL will tell hugo_print() not to print in it:
+		to nullptr will tell hugo_print() not to print in it:
 		*/
 		if (bottom<physical_windowbottom / FIXEDLINEHEIGHT + 1)
 		{
-			currentwin = NULL;
+			currentwin = nullptr;
 			glk_set_window(mainwin);
 			return;
 		}
@@ -313,7 +313,7 @@ void Hugo::hugo_settextwindow(int left, int top, int right, int bottom) {
 		}
 		else
 		{
-			currentwin = NULL;
+			currentwin = nullptr;
 			glk_set_window(mainwin);
 			secondwin_bottom = 0;
 			return;
@@ -329,37 +329,37 @@ void Hugo::hugo_settextwindow(int left, int top, int right, int bottom) {
 }
 
 int Hugo::heglk_get_linelength() {
-	static uint32 width;
+	static uint width;
 
 	// Try to use whatever fixed-width linelength is available
 	if (secondwin)
-		glk_window_get_size(secondwin, &width, NULL);
+		glk_window_get_size(secondwin, &width, nullptr);
 	else if (auxwin)
-		glk_window_get_size(auxwin, &width, NULL);
+		glk_window_get_size(auxwin, &width, nullptr);
 
 	// Otherwise try to approximate it by the proportionally spaced linelength
 	else
-		glk_window_get_size(mainwin, &width, NULL);
+		glk_window_get_size(mainwin, &width, nullptr);
 
 	// -1 to override automatic line wrapping
 	return width - 1;
 }
 
 int Hugo::heglk_get_screenheight() {
-	static uint32 height = 0, mainheight = 0;
+	static uint height = 0, mainheight = 0;
 
 	if (secondwin)
-		glk_window_get_size(secondwin, NULL, &height);
+		glk_window_get_size(secondwin, nullptr, &height);
 	else if (auxwin)
-		glk_window_get_size(auxwin, NULL, &height);
+		glk_window_get_size(auxwin, nullptr, &height);
 
-	glk_window_get_size(mainwin, NULL, &mainheight);
+	glk_window_get_size(mainwin, nullptr, &mainheight);
 
 	return height + mainheight;
 }
 
 void Hugo::hugo_settextpos(int x, int y) {
-	if (currentwin == NULL) return;
+	if (currentwin == nullptr) return;
 
 	// Try to determine if we're trying to position fixed-width text in the main window,
 	// as in a menu, for example
@@ -374,7 +374,7 @@ void Hugo::hugo_settextpos(int x, int y) {
 			/* If not, create it, making it 100% of
 			mainwin's height
 			*/
-			if (auxwin == NULL)
+			if (auxwin == nullptr)
 			{
 				auxwin = glk_window_open(mainwin,
 					winmethod_Below | winmethod_Proportional,
@@ -398,7 +398,7 @@ void Hugo::hugo_settextpos(int x, int y) {
 
 		/* Close auxwin */
 		glk_window_close(auxwin, &sr);
-		auxwin = NULL;
+		auxwin = nullptr;
 
 		/* Clear the screen (both windows) */
 		glk_window_clear(mainwin);
@@ -425,7 +425,7 @@ void Hugo::hugo_print(const char *a) {
 	/* Can't print in a Glk-illegal window since it hasn't been
 	created
 	*/
-	if (currentwin == NULL) return;
+	if (currentwin == nullptr) return;
 
 	/* In lieu of colors, in case we're highlighting something
 	such as a menu selection:
diff --git a/engines/glk/hugo/hemisc.cpp b/engines/glk/hugo/hemisc.cpp
index 4a2ea25..07800bb 100644
--- a/engines/glk/hugo/hemisc.cpp
+++ b/engines/glk/hugo/hemisc.cpp
@@ -734,7 +734,7 @@ if (n==UNKNOWN_OP_E || n==ILLEGAL_OP_E || n==EXPECT_VAL_E || n==OVERFLOW_E)
 
 	hugo_closefiles();
 	hugo_blockfree(mem);
-	mem = NULL;
+	mem = nullptr;
 	error("Error code: %d", (int)n);
 }
 
@@ -786,16 +786,16 @@ void Hugo::FileIO() {
 	{
 #if !defined (GLK)
 		/* stdio implementation */
-		if ((io = HUGO_FOPEN(fileiopath, "wb"))==NULL) goto LeaveFileIO;
+		if ((io = HUGO_FOPEN(fileiopath, "wb"))==nullptr) goto LeaveFileIO;
 #else
 		/* Glk implementation */
-		frefid_t fref = NULL;
+		frefid_t fref = nullptr;
 
 		fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
 			fileiopath, 0);
 		io = glk_stream_open_file(fref, filemode_Write, 0);
 		glk_fileref_destroy(fref);
-		if (io==NULL) goto LeaveFileIO;
+		if (io==nullptr) goto LeaveFileIO;
 #endif
 		ioblock = 1;
 	}
@@ -803,16 +803,16 @@ void Hugo::FileIO() {
 	{
 #if !defined (GLK)
 		/* stdio implementation */
-		if ((io = HUGO_FOPEN(fileiopath, "rb"))==NULL) goto LeaveFileIO;
+		if ((io = HUGO_FOPEN(fileiopath, "rb"))==nullptr) goto LeaveFileIO;
 #else
 		/* Glk implementation */
-		frefid_t fref = NULL;
+		frefid_t fref = nullptr;
 
 		fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
 			fileiopath, 0);
 		io = glk_stream_open_file(fref, filemode_Read, 0);
 		glk_fileref_destroy(fref);
-		if (io==NULL) goto LeaveFileIO;
+		if (io==nullptr) goto LeaveFileIO;
 #endif
 		ioblock = 2;
 	}
@@ -834,7 +834,7 @@ void Hugo::FileIO() {
 	if (ioerror) retflag = 0;
 
 	hugo_fclose(io);
-	io = NULL;
+	io = nullptr;
 	ioblock = 0;
 
 LeaveFileIO:
@@ -1098,16 +1098,16 @@ void Hugo::LoadGame() {
 #if defined (DEBUGGER)
 	if (!strcmp(gamefile, ""))
 	{
-		game = NULL;
+		game = nullptr;
 		strcpy(gamefile, "(no file)");
 		return;
 	}
 #endif
 
 #if !defined (GLK) /* since in Glk the game stream is always open */
-	if ((game = TrytoOpen(gamefile, "rb", "games"))==NULL)
+	if ((game = TrytoOpen(gamefile, "rb", "games"))==nullptr)
 	{
-		if ((game = TrytoOpen(gamefile, "rb", "object"))==NULL)
+		if ((game = TrytoOpen(gamefile, "rb", "object"))==nullptr)
 			FatalError(OPEN_E);
 	}
 #endif
@@ -1154,7 +1154,7 @@ void Hugo::LoadGame() {
 #endif
 		hugo_closefiles();
 		hugo_blockfree(mem);
-		mem = NULL;
+		mem = nullptr;
 
 		hugo_exit(line);
 	}
@@ -1173,7 +1173,7 @@ void Hugo::LoadGame() {
 #endif
 		hugo_closefiles();
 		hugo_blockfree(mem);
-		mem = NULL;
+		mem = nullptr;
 		hugo_exit(line);           /* ditto */
 	}
 
@@ -1198,10 +1198,10 @@ void Hugo::LoadGame() {
 
 #ifndef LOADGAMEDATA_REPLACED
 	/* Allocate as much memory as is required */
-	if ((!loaded_in_memory) || (mem = (unsigned char *)hugo_blockalloc(filelength))==NULL)
+	if ((!loaded_in_memory) || (mem = (unsigned char *)hugo_blockalloc(filelength))==nullptr)
 	{
 		loaded_in_memory = 0;
-		if ((mem = (unsigned char *)hugo_blockalloc(codeend))==NULL)
+		if ((mem = (unsigned char *)hugo_blockalloc(codeend))==nullptr)
 			FatalError(MEMORY_E);
 	}
 
@@ -1321,7 +1321,7 @@ signed char def_slbgcolor = DEF_SLBGCOLOR;
 void ParseCommandLine(int argc, char *argv[])
 {
 	char drive[MAXDRIVE], dir[MAXDIR], fname[MAXFILENAME], ext[MAXEXT];
-	char* game_file_arg = NULL;
+	char* game_file_arg = nullptr;
 
 #if defined(GCC_UNIX) && defined(DO_COLOR)
         int ch;
@@ -1345,7 +1345,7 @@ void ParseCommandLine(int argc, char *argv[])
 	    default:
 	      Banner();
 	      if (mem) hugo_blockfree(mem);
-	      mem = NULL;
+	      mem = nullptr;
 	      exit(0);
 	  }
 	}
@@ -1358,11 +1358,11 @@ void ParseCommandLine(int argc, char *argv[])
 	}
 #endif
 
-	if (game_file_arg==NULL)
+	if (game_file_arg==nullptr)
 	{
 		Banner();
 		if (mem) hugo_blockfree(mem);
-		mem = NULL;
+		mem = nullptr;
 		exit(0);
 	}
 
@@ -1617,7 +1617,7 @@ void Hugo::Printout(char *a, int no_scrollback_linebreak) {
 		hugo_setbackcolor(bgcolor);
 	}
 
-#if defined (AMIGA)
+#if defined (AMIGA) && !defined (GLK)
 	else
 	{
 		if (currentpos + l >= physical_windowwidth)
@@ -1750,7 +1750,7 @@ void Hugo::PromptMore() {
 	{
 		if (hugo_fclose(playback))
 			FatalError(READ_E);
-		playback = NULL;
+		playback = nullptr;
 	}
 	else if (playback && k=='+')
 		skipping_more = true;
@@ -1802,7 +1802,7 @@ int Hugo::RecordCommands() {
 				strcpy(recordfile, line);
 #else
 				/* Glk implementation */
-				frefid_t fref = NULL;
+				frefid_t fref = nullptr;
 
 				fref = glk_fileref_create_by_prompt(fileusage_Transcript | fileusage_TextMode,
 					filemode_Write, 0);
@@ -1824,7 +1824,7 @@ int Hugo::RecordCommands() {
 			{
 				if (hugo_fclose(record)) return (0);
 
-				record = NULL;
+				record = nullptr;
 				return 1;
 			}
 			break;
@@ -1844,7 +1844,7 @@ int Hugo::RecordCommands() {
 				strcpy(recordfile, line);
 #else
 				/* Glk implementation */
-				frefid_t fref = NULL;
+				frefid_t fref = nullptr;
 
 				fref = glk_fileref_create_by_prompt(fileusage_InputRecord | fileusage_TextMode,
 					filemode_Read, 0);
@@ -2173,7 +2173,7 @@ HUGO_FILE TrytoOpen(char *f, char *p, char *d)
 		}
 	}
 
-	return NULL;            /* return NULL if not openable */
+	return nullptr;            /* return nullptr if not openable */
 }
 
 #endif	/* GLK */
diff --git a/engines/glk/hugo/herun.cpp b/engines/glk/hugo/herun.cpp
index 4383797..6466f88 100644
--- a/engines/glk/hugo/herun.cpp
+++ b/engines/glk/hugo/herun.cpp
@@ -163,7 +163,7 @@ RestartDebugger:
 	/* If no gamefile is loaded, jump immediately to the debugger
 	   interrupt function.
 	*/
-	if (game==NULL) Debugger();
+	if (game==nullptr) Debugger();
 #endif
 
 	stack_depth = RESET_STACK_DEPTH;
@@ -271,7 +271,7 @@ FreshInput:
 							{
 								if (hugo_fclose(playback))
 									FatalError(READ_E);
-								playback = NULL;
+								playback = nullptr;
 								GetCommand();
 							}
 							else
@@ -635,7 +635,7 @@ EndofCommand:
 	if (playback)
 	{
 		if (hugo_fclose(playback)) FatalError(READ_E);
-		playback = NULL;
+		playback = nullptr;
 	}
 
 	Flushpbuffer();
@@ -702,7 +702,7 @@ NormalTermination:
 
 	debugger_collapsing = false;
 
-	if ((game!=NULL) && !RunRestart())
+	if ((game!=nullptr) && !RunRestart())
 		DebugMessageBox("Restart Error", "Unable to restart");
 
 	SwitchtoGame();
@@ -1115,7 +1115,7 @@ int Hugo::RunRestart() {
 	{
 		if (hugo_fclose(playback))
 			FatalError(READ_E);
-		playback = NULL;
+		playback = nullptr;
 	}
 
 	if (active_screen!=DEBUGGER)
@@ -1169,7 +1169,7 @@ int Hugo::RestoreGameData() {
 	{
 		AP("Incorrect save file.");
 		if (hugo_fclose(save)) FatalError(READ_E);
-		save = NULL;
+		save = nullptr;
 		return 0;
 	}
 
@@ -1179,7 +1179,7 @@ int Hugo::RestoreGameData() {
 	{
 		AP("Save file created by different version.");
 		if (hugo_fclose(save)) FatalError(READ_E);
-		save = NULL;
+		save = nullptr;
 		return 0;
 	}
 
@@ -1258,7 +1258,7 @@ RestoreError:
 
 int Hugo::RunRestore() {
 #if !defined (GLK)
-	save = NULL;
+	save = nullptr;
 
 	/* stdio implementation */
 	hugo_getfilename("to restore", savefile);
@@ -1273,7 +1273,7 @@ int Hugo::RunRestore() {
 	/* Glk implementation */
 	frefid_t savefile;
 
-	save = NULL;
+	save = nullptr;
 
 	savefile = glk_fileref_create_by_prompt(fileusage_SavedGame | fileusage_BinaryMode,
 		filemode_Read, 0);
@@ -1281,7 +1281,7 @@ int Hugo::RunRestore() {
 	if (glk_fileref_does_file_exist(savefile))
 		save = glk_stream_open_file(savefile, filemode_Read, 0);
 	else
-		save = NULL;
+		save = nullptr;
 	glk_fileref_destroy(savefile);
 	if (!save) return 0;
 
@@ -1290,7 +1290,7 @@ int Hugo::RunRestore() {
 	if (!RestoreGameData()) goto RestoreError;
 
 	if (hugo_fclose(save)) FatalError(READ_E);
-	save = NULL;
+	save = nullptr;
 
 #if !defined (GLK)
 	strcpy(savefile, line);
@@ -1302,7 +1302,7 @@ int Hugo::RunRestore() {
 
 RestoreError:
 	if ((save) && hugo_fclose(save)) FatalError(READ_E);
-	save = NULL;
+	save = nullptr;
 	game_reset = false;
 	return 0;
 }
@@ -1413,7 +1413,7 @@ void Hugo::RunRoutine(long addr) {
 			}
 
 			/* If not object.property or an event */
-			if (strchr(debug_line, '.')==NULL && strstr(debug_line, "vent ")==NULL)
+			if (strchr(debug_line, '.')==nullptr && strstr(debug_line, "vent ")==nullptr)
 			{
 				strcat(debug_line, "(");
 				for (i=0; i<arguments_passed; i++)
@@ -2302,7 +2302,7 @@ int Hugo::RunSave() {
 #endif
 
 #if !defined (GLK)
-	save = NULL;
+	save = nullptr;
 
 	/* stdio implementation */
 	hugo_getfilename("to save", savefile);
@@ -2319,7 +2319,7 @@ int Hugo::RunSave() {
 	/* Glk implementation */
 	frefid_t savefile;
 
-	save = NULL;
+	save = nullptr;
 
 	savefile = glk_fileref_create_by_prompt(fileusage_SavedGame | fileusage_BinaryMode,
 		filemode_Write, 0);
@@ -2333,7 +2333,7 @@ int Hugo::RunSave() {
 	if (!SaveGameData()) goto SaveError;
 	
 	if (hugo_fclose(save)) FatalError(WRITE_E);
-	save = NULL;
+	save = nullptr;
 
 #if !defined (GLK)
 	strcpy(savefile, line);
@@ -2343,7 +2343,7 @@ int Hugo::RunSave() {
 
 SaveError:
 	if ((save) && hugo_fclose(save)) FatalError(WRITE_E);
-	save = NULL;
+	save = nullptr;
 	return 0;
 }
 
@@ -2388,7 +2388,7 @@ int Hugo::RunScriptSet() {
 			if (script)
 			{
 				if (hugo_fclose(script)) return (0);
-				script = NULL;
+				script = nullptr;
 				return 1;
 			}
 			break;





More information about the Scummvm-git-logs mailing list