scummvm/engines/glk/hugo/heset.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

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