mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
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.
582 lines
11 KiB
C++
582 lines
11 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 {
|
|
|
|
void Hugo::RunSet(int gotvalue) {
|
|
char inc = 0; /* increment/decrement */
|
|
char temparrexpr, propval = 0;
|
|
int a = 0, t = 0, obj = 0;
|
|
int newl = 0; /* new length */
|
|
int newp = 0; /* new property val */
|
|
unsigned int element = 0; /* of an array */
|
|
|
|
unsigned short n, m, v; /* must be 16 bits */
|
|
|
|
inobj = 0;
|
|
|
|
if (gotvalue!=-1)
|
|
{
|
|
obj = gotvalue;
|
|
t = SetCompound(t);
|
|
goto StoreVal;
|
|
}
|
|
|
|
t = MEM(codeptr);
|
|
|
|
switch (t)
|
|
{
|
|
case OBJECTNUM_T:
|
|
{
|
|
codeptr++;
|
|
obj = PeekWord(codeptr);
|
|
codeptr += 2;
|
|
t = SetCompound(t);
|
|
break;
|
|
}
|
|
|
|
case VAR_T:
|
|
{
|
|
a = MEM(codeptr + 1);
|
|
|
|
/* Check for ++, --, +=, etc. */
|
|
inc = IsIncrement(codeptr+2);
|
|
|
|
if (MEM(codeptr + 2)==EQUALS_T || inc)
|
|
{
|
|
if (a < MAXGLOBALS) SaveUndo(VAR_T, a, var[a], 0, 0);
|
|
|
|
/* anonymous function */
|
|
if (!inc && MEM(codeptr+3)==EOL_T)
|
|
{
|
|
var[a] = GetAnonymousFunction(codeptr+4);
|
|
return;
|
|
}
|
|
|
|
if (inc)
|
|
{
|
|
var[a] = (Increment(var[a], inc)) + incdec;
|
|
|
|
/* backward-compatibility tweak */
|
|
if ((game_version<23) && MEM(codeptr)!=CLOSE_BRACE_T) codeptr--;
|
|
|
|
codeptr++; /* eol */
|
|
}
|
|
else
|
|
{
|
|
codeptr += 3;
|
|
inexpr = 1;
|
|
SetupExpr();
|
|
inexpr = 0;
|
|
v = EvalExpr(0);
|
|
var[a] = v;
|
|
}
|
|
|
|
/* If a global variable */
|
|
if (a < MAXGLOBALS)
|
|
{
|
|
if (a==wordcount) words = var[wordcount];
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
obj = var[a];
|
|
codeptr += 2;
|
|
t = SetCompound(t);
|
|
|
|
break;
|
|
}
|
|
|
|
case WORD_T: /* "word" */
|
|
{
|
|
codeptr += 2; /* skip "[" */
|
|
n = GetValue();
|
|
#if defined (DEBUGGER)
|
|
if ((debug_eval) && debug_eval_error)
|
|
return;
|
|
#endif
|
|
codeptr += 2; /* skip "] =" */
|
|
inexpr = 1;
|
|
SetupExpr();
|
|
inexpr = 0;
|
|
|
|
GetNextWord:
|
|
if (n >= MAXWORDS) n = MAXWORDS-1;
|
|
SaveUndo(WORD_T, n, wd[n], 0, 0);
|
|
wd[n] = EvalExpr(0);
|
|
|
|
if (MEM(codeptr)==COMMA_T)
|
|
{
|
|
codeptr++;
|
|
n++;
|
|
goto GetNextWord;
|
|
}
|
|
|
|
/* Have to (rather unfortunately) rebuild the entire
|
|
input buffer and word array here
|
|
*/
|
|
buffer[0] = '\0';
|
|
t = 0;
|
|
for (a=1; a<=(int)MAXWORDS; a++)
|
|
{
|
|
if ((unsigned short)wd[a]!=UNKNOWN_WORD)
|
|
Common::strcpy_s(buffer+t, sizeof(buffer) - t, GetWord(wd[a]));
|
|
else
|
|
hugo_itoa(parsed_number, buffer+t, 10, sizeof(buffer) - t);
|
|
word[a] = buffer + t;
|
|
t+=strlen(word[a])+1;
|
|
}
|
|
|
|
if (n>(unsigned)var[wordcount])
|
|
var[wordcount] = n;
|
|
|
|
return;
|
|
}
|
|
|
|
case ARRAYDATA_T:
|
|
case ARRAY_T:
|
|
{
|
|
char af_flag = false;
|
|
/* array[n]... */
|
|
if (t==ARRAYDATA_T)
|
|
{
|
|
m = PeekWord(codeptr + 1);
|
|
codeptr += 4; /* "[" */
|
|
n = GetValue();
|
|
#if defined (DEBUGGER)
|
|
if ((debug_eval) && debug_eval_error)
|
|
return;
|
|
#endif
|
|
codeptr++; /* "]" */
|
|
}
|
|
|
|
/* ...or array val[n] */
|
|
else
|
|
{
|
|
codeptr++;
|
|
m = GetValue();
|
|
#if defined (DEBUGGER)
|
|
if ((debug_eval) && debug_eval_error)
|
|
return;
|
|
#endif
|
|
codeptr++; /* "[" */
|
|
n = GetValue();
|
|
#if defined (DEBUGGER)
|
|
if ((debug_eval) && debug_eval_error)
|
|
return;
|
|
#endif
|
|
codeptr++; /* "]" */
|
|
}
|
|
|
|
if (game_version>=22)
|
|
{
|
|
/* Convert to word value */
|
|
m*=2;
|
|
|
|
if (game_version>=23)
|
|
/* Space for array length */
|
|
a = 2;
|
|
}
|
|
|
|
#if defined (DEBUGGER)
|
|
CheckinRange(m+a+n*2, debug_workspace, "array data");
|
|
#endif
|
|
/* Check for ++, --, +=, etc. */
|
|
inc = IsIncrement(codeptr);
|
|
if (inc)
|
|
{
|
|
defseg = arraytable;
|
|
v = PeekWord(m+a+n*2);
|
|
defseg = gameseg;
|
|
v = (Increment(v, inc)) + incdec;
|
|
|
|
codeptr++; /* eol */
|
|
|
|
element = m+a+n*2;
|
|
|
|
goto WriteArrayValue;
|
|
}
|
|
|
|
if (MEM(codeptr)==EQUALS_T)
|
|
{
|
|
codeptr++;
|
|
|
|
do
|
|
{
|
|
element = m+a+n*2;
|
|
|
|
temparrexpr = arrexpr;
|
|
arrexpr = true;
|
|
|
|
/* anonymous function */
|
|
if (!inc && MEM(codeptr)==EOL_T)
|
|
{
|
|
v = GetAnonymousFunction(codeptr+1);
|
|
af_flag = true;
|
|
}
|
|
else
|
|
{
|
|
v = GetValue();
|
|
}
|
|
#if defined (DEBUGGER)
|
|
if ((debug_eval) && debug_eval_error)
|
|
return;
|
|
#endif
|
|
if (arrexpr==false && MEM(codeptr-1)==76)
|
|
codeptr--;
|
|
arrexpr = temparrexpr;
|
|
|
|
if (!af_flag && (MEM(codeptr)==COMMA_T || MEM(codeptr)==CLOSE_BRACKET_T))
|
|
codeptr++;
|
|
WriteArrayValue:
|
|
defseg = arraytable;
|
|
|
|
/* Make sure the value to be written is within range */
|
|
if ((element>0) && (element < (unsigned)(dicttable-arraytable)*16))
|
|
{
|
|
SaveUndo(ARRAYDATA_T, m+a, n, PeekWord(element), 0);
|
|
|
|
PokeWord(element, (unsigned int)v);
|
|
}
|
|
|
|
defseg = gameseg;
|
|
|
|
if (inc || af_flag) return;
|
|
|
|
n++;
|
|
}
|
|
while (MEM(codeptr)!=EOL_T);
|
|
|
|
codeptr++;
|
|
return;
|
|
}
|
|
|
|
defseg = arraytable;
|
|
obj = PeekWord((unsigned int)(m+a + n*2));
|
|
defseg = gameseg;
|
|
t = SetCompound(t);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
StoreVal:
|
|
|
|
/* Now store the evaluated expression in the appropriate place... */
|
|
|
|
/*
|
|
t = 1: property
|
|
t = 2: attribute
|
|
t = 3: not attribute
|
|
t = 4: property reference
|
|
*/
|
|
|
|
n = 1;
|
|
|
|
if (t==4)
|
|
{
|
|
inobj = true;
|
|
n = GetValue();
|
|
|
|
#if defined (DEBUGGER)
|
|
if ((debug_eval) && debug_eval_error)
|
|
return;
|
|
#endif
|
|
inobj = false;
|
|
|
|
LoopBack:
|
|
if (MEM(codeptr)==IS_T || MEM(codeptr)==DECIMAL_T)
|
|
{
|
|
obj = GetProp(obj, set_value, n, 0);
|
|
t = SetCompound(t);
|
|
goto LoopBack;
|
|
}
|
|
/* Don't set t = 1 if it changed above before going back
|
|
to LoopBack:
|
|
*/
|
|
else if (t==4)
|
|
t = 1; /* Just a property */
|
|
}
|
|
else if (t==1)
|
|
{
|
|
while (MEM(codeptr)==IS_T || MEM(codeptr)==DECIMAL_T)
|
|
{
|
|
obj = GetProp(obj, set_value, n, 0);
|
|
t = SetCompound(t);
|
|
}
|
|
}
|
|
|
|
switch (t)
|
|
{
|
|
case 1:
|
|
{
|
|
incdec = 0;
|
|
|
|
if (MEM(codeptr) != EQUALS_T)
|
|
{
|
|
/* Check for ++, --, +=, etc. */
|
|
if (!(inc = IsIncrement(codeptr)))
|
|
{
|
|
#if defined (DEBUGGER)
|
|
if (debug_eval)
|
|
{
|
|
debug_eval_error = true;
|
|
return;
|
|
}
|
|
#endif
|
|
FatalError(ILLEGAL_OP_E);
|
|
}
|
|
else if (MEM(codeptr)==EOL_T)
|
|
{
|
|
goto GetNextPropVal;
|
|
}
|
|
}
|
|
else
|
|
codeptr++;
|
|
|
|
/* Property routine (anonymous function)... */
|
|
if (MEM(codeptr)==EOL_T)
|
|
{
|
|
/* m = skipptr to the end of the property
|
|
routine block (i.e., the next statement
|
|
following it)
|
|
*/
|
|
m = PeekWord(codeptr + 1);
|
|
|
|
newl = PROP_ROUTINE;
|
|
newp =(unsigned int)(((codeptr + 4)+(codeptr + 4)%address_scale)/address_scale);
|
|
codeptr = (long)m*address_scale;
|
|
m = PropAddr(obj, set_value, 0);
|
|
}
|
|
|
|
/* ...or not */
|
|
else
|
|
{
|
|
GetNextPropVal:
|
|
inexpr = false;
|
|
temparrexpr = multiprop;
|
|
multiprop = true;
|
|
propval = true;
|
|
if (!inc) newp = GetValue();
|
|
#if defined (DEBUGGER)
|
|
if ((debug_eval) && debug_eval_error)
|
|
return;
|
|
#endif
|
|
|
|
if (!multiprop)
|
|
codeptr--;
|
|
multiprop = temparrexpr;
|
|
|
|
m = PropAddr(obj, set_value, 0);
|
|
if (m)
|
|
{
|
|
defseg = proptable;
|
|
newl = Peek((unsigned int)m + 1);
|
|
if (newl==PROP_ROUTINE) newl = 1;
|
|
}
|
|
|
|
/* Deal with setting built-in display object
|
|
properties
|
|
*/
|
|
else if ((obj==display_object) && n==1)
|
|
{
|
|
if (set_value==title_caption)
|
|
{
|
|
Common::strlcpy(game_title, GetWord(newp), MAX_GAME_TITLE);
|
|
hugo_setgametitle(game_title);
|
|
}
|
|
else if (set_value==needs_repaint)
|
|
{
|
|
display_needs_repaint = (char)newp;
|
|
}
|
|
}
|
|
#if defined (DEBUGGER)
|
|
/*
|
|
else if (runtime_warnings)
|
|
{
|
|
RuntimeWarning("Setting non-existent property");
|
|
}
|
|
*/
|
|
#endif
|
|
}
|
|
|
|
/* Write property obj.z = newl words of newp */
|
|
|
|
if (m && (int)n <= 0)
|
|
{
|
|
#if defined (DEBUGGER)
|
|
RuntimeWarning("Property element <= 0");
|
|
#endif
|
|
if (inc) codeptr++;
|
|
}
|
|
else if (m && (int)n <= newl)
|
|
{
|
|
defseg = proptable;
|
|
|
|
#if defined (DEBUGGER)
|
|
CheckinRange((unsigned)n, (unsigned)Peek(m+1), "property element");
|
|
#endif
|
|
/* Check to make sure this property value is within range */
|
|
if ((unsigned)(m+2+(n-1)*2)<(unsigned)(eventtable-proptable)*16)
|
|
{
|
|
SaveUndo(PROP_T, obj, (unsigned int)set_value, n, PeekWord((unsigned int)(m+2+(n-1)*2)));
|
|
|
|
/* Save the (possibly changed) length) */
|
|
Poke((unsigned int)m + 1, (unsigned char)newl);
|
|
|
|
/* An assignment such as obj.prop++ or
|
|
obj.prop += ...
|
|
*/
|
|
if (inc)
|
|
{
|
|
PokeWord((unsigned int)(m+2+(n-1)*2), Increment(PeekWord((unsigned int)(m+2+(n-1)*2)), inc) + incdec);
|
|
codeptr++; /* eol */
|
|
}
|
|
|
|
/* A regular obj.prop = ... assignment */
|
|
else
|
|
PokeWord((unsigned int)(m+2+(n-1)*2), newp);
|
|
}
|
|
}
|
|
else if (inc) codeptr++; /* eol */
|
|
|
|
defseg = gameseg;
|
|
|
|
if (inc) return;
|
|
|
|
if (propval && MEM(codeptr)==COMMA_T)
|
|
{n++;
|
|
codeptr++;
|
|
goto GetNextPropVal;}
|
|
|
|
if (propval) codeptr++;
|
|
if (MEM(codeptr)==EOL_T) codeptr++;
|
|
return;
|
|
}
|
|
|
|
case 2:
|
|
case 3:
|
|
{
|
|
ModifyAttribute:
|
|
|
|
#if defined (DEBUGGER)
|
|
CheckinRange((unsigned int)set_value, (unsigned)attributes, "attribute");
|
|
#endif
|
|
SaveUndo(ATTR_T, obj, (unsigned int)set_value, TestAttribute(obj, (unsigned int)set_value, 0), 0);
|
|
|
|
SetAttribute(obj, set_value, (t==2));
|
|
t = 2; /* reset after 'not' */
|
|
|
|
if (MEM(codeptr++)==EOL_T) return;
|
|
|
|
/* Allow multiple attributes, comma-separated */
|
|
if (MEM(codeptr)==COMMA_T)
|
|
codeptr++;
|
|
|
|
if (MEM(codeptr)==NOT_T)
|
|
{
|
|
t = 3;
|
|
codeptr++;
|
|
}
|
|
|
|
set_value = GetValue();
|
|
goto ModifyAttribute;
|
|
}
|
|
|
|
default:
|
|
{
|
|
#if defined (DEBUGGER)
|
|
if (debug_eval)
|
|
{
|
|
debug_eval_error = true;
|
|
return;
|
|
}
|
|
#endif
|
|
/* Not any sort of variable data type */
|
|
FatalError(ILLEGAL_OP_E);
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned int Hugo::GetAnonymousFunction(long addr) {
|
|
long skipaddr;
|
|
unsigned int af_addr;
|
|
|
|
skipaddr = PeekWord(addr);
|
|
/* The address of the anonymous function is the next address boundary,
|
|
calculated as:
|
|
(((addr+2)/address_scale+1)*address_scale)/address_scale */
|
|
af_addr =(unsigned int)((addr+2)/address_scale+1);
|
|
codeptr = (long)skipaddr*address_scale;
|
|
return af_addr;
|
|
}
|
|
|
|
int Hugo::SetCompound(int t) {
|
|
if (Peek(codeptr)==DECIMAL_T) /* obj.property */
|
|
{
|
|
codeptr++;
|
|
inobj = 1;
|
|
set_value = GetValue(); /* the prop. # */
|
|
inobj = 0;
|
|
|
|
if (Peek(codeptr)==POUND_T) /* if obj.prop #... */
|
|
{
|
|
codeptr++;
|
|
return 4;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (Peek(codeptr)==IS_T) /* obj is ... */
|
|
{
|
|
inobj = 1;
|
|
if (Peek(codeptr+1)==NOT_T)
|
|
{
|
|
codeptr += 2;
|
|
set_value = GetValue(); /* the attr. # */
|
|
inobj = 0;
|
|
return 3;
|
|
}
|
|
|
|
codeptr++;
|
|
set_value = GetValue(); /* the attr. # */
|
|
inobj = 0;
|
|
return 2;
|
|
}
|
|
|
|
#if defined (DEBUGGER)
|
|
if (debug_eval)
|
|
debug_eval_error = true;
|
|
else
|
|
#endif
|
|
|
|
FatalError(ILLEGAL_OP_E);
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // End of namespace Hugo
|
|
} // End of namespace Glk
|