scummvm/engines/glk/hugo/heparse.cpp
D G Turner 72db454796 COMMON: Forbid itoa() and uitoa() Usage
These functions are often present, but not reliably portable.

They are not defined in ANSI C and while they are likely present in
the stdlib.h, the exact behaviour especialy for invalid inputs can
vary.

Replacing usage of these functions in engines with Common::String::format()
is recommended.
2024-03-09 21:38:18 +02:00

2583 lines
50 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/hugo/hugo.h"
namespace Glk {
namespace Hugo {
#define STARTS_AS_NUMBER(a) (((a[0]>='0' && a[0]<='9') || a[0]=='-')?1:0)
static char EMPTY[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];
Common::sprintf_s(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(const 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] = EMPTY;
RemoveWord(a);
words--;
}
int Hugo::MatchCommand() {
int i, j, flag, a, mw = 0, gotspeaker = 0;
int wordnum = 0;
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"))
{
parseerr[0] = '\0';
/* "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;
Common::strcpy_s(line, word[2]);
for (i=1; i<=(int)strlen(errbuf); i++)
{
if (!strcmp(Mid(errbuf, i, strlen(oops)), oops))
break;
}
Common::strcpy_s(buffer, errbuf);
buffer[i-1] = '\0';
Common::strcat_s(buffer, line);
Common::strcat_s(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)
{
parseerr[0] = '\0';
ParseError(6, 0); /* "...doesn't make any sense..." */
return 0;
}
/* No provision made for addressing objects (characters) */
if (flag==0 || speaktoaddr==0)
{
parseerr[0] = '\0';
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;
Common::strcpy_s(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 = (byte)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:
*/
parseerr[0] = '\0';
/* "...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
parseerr[0] = '\0';
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') Common::strcat_s(parseerr, " ");
Common::strcat_s(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)
{
Common::strcpy_s(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)
{
Common::strcpy_s(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)
{
Common::strcpy_s(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;
Common::sprintf_s(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 = 0;
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 = 1;
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 = 0;
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 = 0;
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 = 0;
return false;
}
full_buffer = 0;
}
RestoreTempArrays:
/* Rebuild <buffer> and word[] array */
buffer[0] = '\0';
for (i=1; i<=wtemp; i++)
{
Common::strcat_s(buffer, GetWord(wdtemp[i]));
Common::strcat_s(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)
{
Common::strcpy_s(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 = 0;
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 */
Common::strcpy_s(parseerr, word[1]);
ParseError(9, 0); /* "Nothing to (verb)..." */
return false;
}
}
}
/* Go back for the next object phrase */
pobjcount = 0;
mobjs = 0;
parseerr[0] = '\0';
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(hugo_itoa(atoi(word[*wordnum]), num, 10, sizeof(num)), 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], hugo_itoa(atoi(word[*wordnum]), num, 10, sizeof(num))))
{
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)
{
Common::strcpy_s(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], hugo_itoa(atoi(word[*wordnum]), num, 10, sizeof(num))))
{
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(",");
parsestr[0] = '\0'; /* 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)
{
Common::strcpy_s(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], hugo_itoa(atoi(word[i]), num, 10, sizeof(num))))
{
#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')
Common::strcpy_s(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)
{
Common::strcpy_s(parseerr, word[i]);
Common::strcpy_s(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);
errbuf[0] = '\0';
for (i=1; i<=words; i++)
{
Common::strcat_s(errbuf, word[i]);
if (i != words) Common::strcat_s(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)
{buffer[0] = '\0';
words = 0;
ParseError(0, 0);
return 0;}
for (k=words; k>i; k--)
{
Common::strcpy_s(tempword, word[k]);
word[k] += m;
Common::strcpy_s(word[k], sizeof(buffer) - (word[k] - buffer), tempword);
}
}
Common::strcpy_s(word[i], sizeof(buffer) - (word[i] - buffer), GetWord(wd[i]));
i--;
break;
}
case 1: /* removal */
{
KillWord(i);
i--;
break;
}
case 2: /* compound */
{
if (wd[i+1]==PeekWord(synptr+3))
{
Common::strcat_s(word[i], sizeof(buffer) - (word[i] - buffer), 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"))
{
static char AND[5] = "~and";
word[i] = AND;
wd[i] = FindWord("~and");
}
else
KillWord(i);
}
if (wd[i]==period)
{
wd[i] = 0;
word[i] = EMPTY;
}
}
defseg = gameseg;
if (strcmp(word[1], "~oops")) oops[0] = '\0';
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:
Common::sprintf_s(line, "You can't use the word \"%s\".", parseerr);
AP(line);
break;
case 2:
AP("Better start with a verb.");
break;
case 3:
Common::sprintf_s(line, "You can't %s multiple objects.", parseerr);
AP(line);
break;
case 4:
AP("Can't do that.");
break;
case 5:
Common::sprintf_s(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:
{
Common::sprintf_s(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) Common::strcat_s(line, ",");
Common::strcat_s(line, " or ");
}
else
{
if (count != 1)
Common::strcat_s(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"))
Common::strcat_s(line, "the ");
else
Common::sprintf_s(line+strlen(line), "%s ", w);
*/
/* We'll just use "the" */
if (w) Common::strcat_s(line, "the ");
}
Common::strcat_s(line, Name(i));
count++;
}
}
Common::strcat_s(line, "?");
AP(line);
break;
}
case 9:
Common::sprintf_s(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:
Common::sprintf_s(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] = EMPTY;
wd[i] = 0;
}
word[1] = buffer;
Common::strcpy_s(a, buffer);
buffer[0] = '\0';
for (i=1; i<=(int)strlen(a); i++)
{
if (inquote!=1 && Common::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)
{
Common::strcpy_s(buffer+bloc, sizeof(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;
word[words][0] = '\0';
}
if (b[0]=='\"' && inquote==0)
{
Common::strcpy_s(buffer+bloc, sizeof(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;
Common::strcpy_s(word[words], sizeof(buffer)-bloc, b);
bloc += strlen(b) + 1;
if (++words > MAXWORDS) words = MAXWORDS;
word[words] = buffer + bloc;
word[words][0] = '\0';
}
else
{
Common::strcpy_s(buffer+bloc, sizeof(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)
{
Common::strcpy_s(w1, Left(word[i], strcspn(word[i], ":")));
Common::strcpy_s(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"))
Common::strcpy_s(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, hugo_itoa((int)n1, temp, 10, sizeof(temp))) &&
!strcmp(w2, hugo_itoa((int)n2, temp, 10, sizeof(temp))) &&
(n1 > 0 && n1 < 25) && (n2 >= 0 && n2 < 60))
{
Common::strcpy_s(parseerr, word[i]);
hugo_itoa(n1 * 60 + n2, word[i], 10, strlen(word[i]) + 1);
}
}
}
}
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];
Common::sprintf_s(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))
{
parseerr[0] = '\0';
if (GetProp(obj, article, 1, 0))
Common::strcpy_s(parseerr, "the ");
Common::strcat_s(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))
{
parseerr[0] = '\0';
if (GetProp(obj, article, 1, 0))
Common::strcpy_s(parseerr, "the ");
Common::strcat_s(parseerr, Name(obj));
/* "...can't do that with..." */
ParseError(12, obj);
return 0;
}
break;
}
}
return true;
}
} // End of namespace Hugo
} // End of namespace Glk