/* 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #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