[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