/* 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 .
*
*/
#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 MAXOBJLIST) objcount = MAXOBJLIST;
}
void Hugo::AddPossibleObject(int obj, char type, unsigned int w) {
int i;
if (pobjcount==MAXPOBJECTS)
return;
for (i=0; i 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; iwords)
return;
for (i=a; i 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; i65535 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=(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 1)
{
for (i=0; i 1 && Peek(grammaraddr)==ANYTHING_T)
{
for (i=0; i 1 && findobjectaddr)
{
for (k=0; k 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 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; i1 && 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 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 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 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 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 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