/* 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 {
#if defined (DEBUGGER)
int Hugo::CheckObjectRange(int obj) {
if (runtime_warnings) {
return CheckinRange((unsigned)obj, (unsigned)objects, "object");
} else
return true;
}
#endif
int Hugo::Child(int obj) {
int c;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
c = PeekWord(2 + obj*object_size + object_size - 4);
defseg = gameseg;
return c;
}
int Hugo::Children(int obj) {
int count = 0;
int nextobj;
if (obj<0 || obj>=objects) return 0;
nextobj = Child(obj);
while (nextobj)
{count++;
nextobj = Sibling(nextobj);}
return count;
}
int Hugo::Elder(int obj) {
int lastobj;
int p, cp;
if (obj<0 || obj>=objects) return 0;
p = Parent(obj);
cp = Child(p);
if (p==0 || cp==obj)
return 0;
lastobj = cp;
while (Sibling(lastobj) != obj)
lastobj = Sibling(lastobj);
return lastobj;
}
unsigned long Hugo::GetAttributes(int obj, int attribute_set) {
unsigned long a;
defseg = objtable;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
a = (unsigned long)PeekWord(2 + obj*object_size + attribute_set*4)
+ (unsigned long)PeekWord(2 + obj*object_size + attribute_set*4 + 2)*65536L;
defseg = gameseg;
return a;
}
int Hugo::GetProp(int obj, int p, int n, char s) {
char objonly, /* no verbroutine given in before, etc. */
isadditive = 0, /* before, after, etc. */
gotone = 0, /* when a match has been made */
getpropaddress = 0; /* when getting &object.property */
int i;
int tempself,
objtype, /* i.e., what we're matching to */
flag = 0;
int g = 0;
int templocals[MAXLOCALS];
int temp_stack_depth;
char tempinexpr = inexpr;
unsigned int pa, /* property address */
offset = 0;
long inprop, /* code position in complex property */
returnaddr;
#if defined (DEBUGGER)
long orig_inprop;
int tempdbnest;
/* Don't check a possible non-existent display object (-1) */
if (obj!=-1 || display_object!=-1) CheckObjectRange(obj);
#endif
/* This way either -1 (the non-existent display object) or a too-high
object will fail
*/
if (obj<0 || obj>=objects) return 0;
/* The display object, which is automatically created by the compiler,
did not exist pre-v2.4
*/
if ((obj==display_object) && game_version>=24)
{
/* There are no actual default "properties" per se on the
display object--but reading them returns certain data about
the current state of display affairs
*/
/* no display. #2, etc. */
if (n==1 && p<=pointer_y)
{
if (p==screenwidth)
#if defined (GLK) && defined (ACTUAL_LINELENGTH)
g = ACTUAL_LINELENGTH();
#else
g = SCREENWIDTH/FIXEDCHARWIDTH;
#endif
else if (p==screenheight)
/* ACTUAL_SCREENHEIGHT can be set to a non-portable function if
SCREENHEIGHT and SCREENHEIGHT have been set to large values in
order to force the non-portable layer to handle wrapping and
scrolling (as in the Glk port).
*/
#if defined (ACTUAL_SCREENHEIGHT)
g = ACTUAL_SCREENHEIGHT();
#else
g = SCREENHEIGHT/FIXEDLINEHEIGHT;
#endif
else if (p==linelength)
/* ACTUAL_LINELENGTH functions similarly to ACTUAL_SCREENWIDTH,
above.
*/
#if defined (ACTUAL_LINELENGTH)
g = ACTUAL_LINELENGTH();
#else
g = physical_windowwidth/FIXEDCHARWIDTH;
#endif
else if (p==windowlines)
g = physical_windowheight/FIXEDLINEHEIGHT;
else if (p==cursor_column)
g = (currentpos+1+hugo_textwidth(pbuffer))/FIXEDCHARWIDTH;
else if (p==cursor_row)
g = currentline;
else if (p==hasgraphics)
g = hugo_hasgraphics();
else if (p==title_caption)
g = FindWord(game_title);
else if (p==hasvideo)
#if !defined (COMPILE_V25)
g = hugo_hasvideo();
#else
g = 0;
#endif
else if (p==needs_repaint)
g = display_needs_repaint;
else if (p==pointer_x)
g = display_pointer_x;
else if (p==pointer_y)
g = display_pointer_y;
else
g = 0;
return g;
}
}
/* To avoid prematurely getting an address in &obj.prop.prop */
if (getaddress && MEM(codeptr)!=DECIMAL_T)
getpropaddress = true;
tempself = var[self];
if (!s) var[self] = obj;
temp_stack_depth = stack_depth;
GetNextProp:
pa = PropAddr(obj, p, offset);
defseg = proptable;
/* If the object doesn't have property p, see if there's a
default value.
*/
if (!pa)
{
if (offset) goto NoMorePropMatches;
if (getpropaddress) /* if an &value */
g = 0;
else
g = PeekWord(p * 2 + 2);
}
else
{
/* Property is a value... */
if (Peek(pa+1) < PROP_ROUTINE)
{
if (getaddress || (int)Peek(pa+1) < n || n<=0)
{
#if defined (DEBUGGER)
if (n!=1)
CheckinRange(n, (int)Peek(pa+1), "property element");
#endif
g = 0;
}
else
g = PeekWord(pa + n * 2);
}
/* ...or a property routine */
else
{
/* Check if this is an additive property */
defseg = proptable;
if (Peek(2 + Peek(0)*2 + p)&ADDITIVE_FLAG)
isadditive = true;
/* If an &value, return the address of the
property routine.
*/
if (getpropaddress)
{
g = PeekWord(pa+2);
goto NoMorePropMatches;
}
else
{
#if defined (DEBUGGER)
if (debug_eval)
{
debug_eval_error = true;
DebugMessageBox("Expression Error",
"Property routine illegal in watch/assignment");
defseg = gameseg;
return 0;
}
#endif
/* If not a complex property such as
before or after:
*/
if ((game_version>=22 && (Peek(2 + Peek(0)*2 + p)&COMPLEX_FLAG)==0) || (game_version<22 && p!=before && p!=after))
{
ret = 1;
returnaddr = codeptr;
/* Check to see if this is a valid tail-recursive return... */
if (tail_recursion==TAIL_RECURSION_PROPERTY && MEM(codeptr)==EOL_T)
{
PassLocals(0);
tail_recursion_addr = (long)PeekWord(pa+2)*address_scale;
return 0;
}
/* ...but if we're not immediately followed by and end-of-line marker,
or another property value, cancel the pending tail-recursion
*/
else if (MEM(codeptr)!=DECIMAL_T)
{
tail_recursion = 0;
}
for (i=0; i=objects) return 0;
defseg = objtable;
while ((nextobj = PeekWord(2 + obj*object_size + object_size-8)) != 0)
obj = nextobj;
defseg = gameseg;
return obj;
}
void Hugo::MoveObj(int obj, int p) {
int oldparent, prevobj, s;
unsigned int objaddr, parentaddr, lastobjaddr;
if (obj==p) return;
if (obj<0 || obj>=objects) return;
oldparent = Parent(obj);
/* if (oldparent==p) return; */
objaddr = 2 + obj*object_size;
/* First, detach the object from its old parent and siblings... */
prevobj = Elder(obj);
s = Sibling(obj);
defseg = objtable;
if (prevobj) /* sibling */
PokeWord(2 + prevobj*object_size + object_size-6, s);
else /* child */
PokeWord(2 + oldparent*object_size + object_size-4, s);
/* Then move it to the new parent... */
defseg = objtable;
PokeWord(objaddr + object_size-8, p); /* new parent */
PokeWord(objaddr + object_size-6, 0); /* erase old sibling */
/* Only operate on the new parent if it isn't object 0 */
if (p!=0)
{
/* Object is sole child, or... */
if (Child(p)==0)
{
parentaddr = 2 + p*object_size;
defseg = objtable;
PokeWord(parentaddr + object_size-4, obj);
}
/* ...object is next sibling. */
else
{
lastobjaddr = 2 + Youngest(p)*object_size;
defseg = objtable;
PokeWord(lastobjaddr + object_size-6, obj);
}
}
}
const char *Hugo::Name(int obj) {
int p;
p = GetProp(obj, 0, 1, 0);
if (p)
return GetWord((unsigned int)p);
else
return nullptr;
}
int Hugo::Parent(int obj) {
int p;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
p = PeekWord(2 + obj*object_size + object_size-8);
defseg = gameseg;
return p;
}
unsigned int Hugo::PropAddr(int obj, int p, unsigned int offset) {
unsigned char c;
int proplen;
unsigned int ptr;
#if defined (DEBUGGER)
/* Don't check any non-existent display object (-1) */
if (p!=-1) CheckinRange(p, properties, "property");
CheckObjectRange(obj);
#endif
/* This way either -1 (the non-existent display object) or a too-high
object will fail
*/
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
/* Position in the property table...
i.e., ptr = PeekWord(2 + obj*object_size + (object_size-2));
*/
ptr = PeekWord(object_size*(obj+1));
/* ...unless a position has already been given */
if (offset) ptr = offset;
defseg = proptable;
c = Peek(ptr);
while (c != PROP_END && c != (unsigned char)p)
{
proplen = Peek(ptr + 1);
/* Property routine address is 1 word */
if (proplen==PROP_ROUTINE) proplen = 1;
ptr += proplen * 2 + 2;
c = Peek(ptr);
}
defseg = gameseg;
if (c==PROP_END)
return 0;
else
return ptr;
}
void Hugo::PutAttributes(int obj, unsigned long a, int attribute_set) {
unsigned int lword, hword;
hword = (unsigned int)(a/65536L);
lword = (unsigned int)(a%65536L);
defseg = objtable;
PokeWord(2 + obj*object_size + attribute_set*4, lword);
PokeWord(2 + obj*object_size + attribute_set*4 + 2, hword);
defseg = gameseg;
}
void Hugo::SetAttribute(int obj, int attr, int c) {
unsigned long a, mask;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return;
#endif
if (obj<0 || obj>=objects) return;
a = GetAttributes(obj, attr/32);
mask = 1L<<(long)(attr%32);
if (c==1)
a = a | mask;
else
{
if (a & mask)
a = a ^ mask;
}
PutAttributes(obj, a, attr/32);
}
int Hugo::Sibling(int obj) {
int s;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
s = PeekWord(2+ obj*object_size + object_size-6);
defseg = gameseg;
return s;
}
int Hugo::TestAttribute(int obj, int attr, int nattr) {
unsigned long a, mask, ta;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
a = GetAttributes(obj, attr/32);
mask = 1L<<(attr%32);
ta = a & mask;
if (ta) ta = 1;
if (nattr) ta = ta ^ 1;
return (int)ta;
}
int Hugo::Youngest(int obj) {
int nextobj;
if (Child(obj)==0) return 0;
nextobj = Child(obj);
while (Sibling(nextobj))
nextobj = Sibling(nextobj);
return nextobj;
}
} // End of namespace Hugo
} // End of namespace Glk