From 0b2346c1f14d94efa1264aadef5bf13c1f15ddef Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sat, 11 May 2019 10:18:13 +1000 Subject: GLK: HUGO: Added heobject --- engines/glk/hugo/heobject.cpp | 671 ++++++++++++++++++++++++++++++++++++++++ engines/glk/hugo/hugo.cpp | 35 ++- engines/glk/hugo/hugo.h | 130 +++++++- engines/glk/hugo/hugo_defines.h | 13 + engines/glk/hugo/hugo_types.h | 25 +- engines/glk/module.mk | 1 + 6 files changed, 863 insertions(+), 12 deletions(-) create mode 100644 engines/glk/hugo/heobject.cpp diff --git a/engines/glk/hugo/heobject.cpp b/engines/glk/hugo/heobject.cpp new file mode 100644 index 0000000000..cc1416d98d --- /dev/null +++ b/engines/glk/hugo/heobject.cpp @@ -0,0 +1,671 @@ +/* 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); + } + } +} + +char *Hugo::Name(int obj) { + int p; + + p = GetProp(obj, 0, 1, 0); + + if (p) + return GetWord((unsigned int)p); + else + return 0; +} + +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 diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp index 8e8113421a..a5fbdb935f 100644 --- a/engines/glk/hugo/hugo.cpp +++ b/engines/glk/hugo/hugo.cpp @@ -26,8 +26,12 @@ namespace Glk { namespace Hugo { Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc), - mainwin(nullptr), currentwin(nullptr), secondwin(nullptr), auxwin(nullptr), address_scale(16), + mainwin(nullptr), currentwin(nullptr), secondwin(nullptr), auxwin(nullptr), + runtime_warnings(false), dbnest(0), address_scale(16), SCREENWIDTH(0), SCREENHEIGHT(0), FIXEDCHARWIDTH(0), FIXEDLINEHEIGHT(0), + // heexpr + evalcount(0), incdec(0), getaddress(0), inexpr(0), inobj(0), + // hemisc game_version(0), object_size(0), game(nullptr), script(nullptr), save(nullptr), playback(nullptr), record(nullptr), io(nullptr), ioblock('\0'), ioerror('\0'), codestart(0), objtable(0), eventtable(0), proptable(0), arraytable(0), dicttable(0), @@ -52,18 +56,30 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam objgrammar(0), objstart(0), objfinish(0), addflag(0), speaking(0), oopscount(0), parse_called_twice(0), reparse_everything(0), full_buffer(false), recursive_call(false), parse_location(0), - // herun arguments_passed(0), ret(0), retflag(0), during_player_input(false), override_full(0), game_reset(false), stack_depth(0), tail_recursion(0), tail_recursion_addr(0), last_window_top(0), last_window_bottom(0), last_window_left(0), last_window_right(0), - lowest_windowbottom(0), physical_lowest_windowbottom(0), just_left_window(false) { + lowest_windowbottom(0), physical_lowest_windowbottom(0), just_left_window(false), + // heset + arrexpr(0), multiprop(0), set_value(0) +#if defined (DEBUGGER) + , debug_eval(false), debug_eval_error(false), debugger_step_over(false), + debugger_finish(false), debugger_run(false), currentroutine(false), + complex_prop_breakpoint(false), trace_complex_prop_routine(false), properties(0), + current_locals(0) +#endif + { + // heexpr + Common::fill(&eval[0], &eval[MAX_EVAL_ELEMENTS], 0); + Common::fill(&var[0], &var[MAXLOCALS + MAXGLOBALS], 0); + + // hemisc Common::fill(&context_command[0][0], &context_command[MAX_CONTEXT_COMMANDS][64], 0); Common::fill(&id[0], &id[3], '\0'); Common::fill(&serial[0], &serial[9], '\0'); Common::fill(&pbuffer[0], &pbuffer[MAXBUFFER * 2 + 1], 0); Common::fill(&undostack[0][0], &undostack[MAXUNDO][5], 0); - Common::fill(&var[0], &var[MAXLOCALS + MAXGLOBALS], 0); // heparse Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0'); @@ -80,6 +96,17 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam // herun Common::fill(&passlocal[0], &passlocal[MAXLOCALS], 0); + + // heset + game_title[0] = '\0'; + +#ifdef DEBUGGER + debug_line[0] = '\0'; + Common::fill(&objectname[0], &objectname[MAX_OBJECT], nullptr); + Common::fill(&propertyname[0], &propertyname[MAX_PROPERTY], nullptr); + Common::fill(&codeline[0][0], &codeline[9][100], 0); + Common::fill(&localname[0][0], &localname[9][100], 0); +#endif } void Hugo::runGame() { diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h index a33c5998c8..de64744df6 100644 --- a/engines/glk/hugo/hugo.h +++ b/engines/glk/hugo/hugo.h @@ -40,6 +40,8 @@ class Hugo : public GlkAPI, public HTokens, public StringFunctions { private: winid_t mainwin, currentwin; winid_t secondwin, auxwin; + bool runtime_warnings; + int dbnest; /** * address_scale refers to the factor by which addresses are multiplied to @@ -48,6 +50,18 @@ private: */ int address_scale; + // heexpr + int eval[MAX_EVAL_ELEMENTS]; ///< expression components + int evalcount; ///< # of expr. components + int var[MAXLOCALS + MAXGLOBALS]; ///< variables + int incdec; ///< value is being incremented/dec. + char getaddress; ///< true when finding &routine + char inexpr; ///< true when in expression + char inobj; ///< true when in object compound + + int last_precedence; + + // hemisc char gamefile[255]; int game_version; @@ -119,9 +133,6 @@ private: bool just_cleared_screen; int secondwin_bottom; - // heexpr - int var[MAXLOCALS + MAXGLOBALS]; - // heobject int display_object; ///< i.e., non-existent (yet) char display_needs_repaint; ///< for display object @@ -196,6 +207,32 @@ private: physical_lowest_windowbottom; ///< in pixels or text lines bool just_left_window; + // heset + char game_title[MAX_GAME_TITLE]; + char arrexpr; ///< true when assigning array + char multiprop; ///< true in multiple prop. assign. + int set_value; + +#if defined (DEBUGGER) + char debug_line[MAX_DEBUG_LINE]; + bool debug_eval; + bool debug_eval_error; + bool debugger_step_over; + bool debugger_finish; + bool debugger_run; + int currentroutine; + bool complex_prop_breakpoint; + bool trace_complex_prop_routine; + char *objectname[MAX_OBJECT]; + char *propertyname[MAX_PROPERTY]; +// CODE code[999]; + CALL call[999]; + int properties; + WINDOW window[99]; + int codeline[9][100]; + char localname[9][100]; + int current_locals; +#endif private: /** * \defgroup heglk @@ -491,6 +528,88 @@ private: int Undo(); + /**@}*/ + + /** + * \defgroup heobject - Object/property/attribute management functions + * @{ + */ + +#if defined (DEBUGGER) + int CheckinRange(uint v1, uint v2, const char *v3) { + // TODO: Where the heck is this actualy implemented in Gargoyle + return 1; + } + + /** + * Shorthand since many of these object functions may call CheckinRange() if the debugger + * is running and runtime_warnings is set. + */ + int CheckObjectRange(int obj); + + void DebugRunRoutine(long addr) {} + + void RuntimeWarning(const char *msg) {} + + void DebugMessageBox(const char *title, const char *msg) {} + + bool IsBreakpoint(long loc) const { return false; } + + const char *RoutineName(long loc) { return "Routine"; } + + void AddStringtoCodeWindow(const char *str) {} +#endif + + int Child(int obj); + + int Children(int obj); + + int Elder(int obj); + + /** + * Returns one of four sets of 32 attributes. + */ + unsigned long GetAttributes(int obj, int attribute_set); + + /** + * Returns the value of '.

#' If is true, the self global + * is not set to in order to facilitate ..

calls. + */ + int GetProp(int obj, int p, int n, char s); + + /** + * Returns the value of the last object above in the tree before object 0. + */ + int GrandParent(int obj); + + void MoveObj(int obj, int p); + + char *Name(int obj); + + int Parent(int obj); + + /** + * Returns address of .

(with provided for additive properties-- + * i.e. subsequent calls with the same and

. + */ + unsigned int PropAddr(int obj, int p, unsigned int offset); + + /** + * Writes (puts) one of four sets of 32 attributes. + */ + void PutAttributes(int obj, unsigned long a, int attribute_set); + + /** + * Set an attribute + * c = 1 for set, 0 for clear + */ + void SetAttribute(int obj, int attr, int c); + + int Sibling(int obj); + + int TestAttribute(int obj, int attr, int nattr); + + int Youngest(int obj); /**@}*/ private: @@ -535,10 +654,7 @@ public: void PromptMore() {} void hugo_stopsample() {} void hugo_stopmusic() {} - void SetAttribute(int obj, int attr, int c) {} - unsigned int PropAddr(int obj, int p, unsigned int offset) { return 0; } - int GetProp(int obj, int p, int n, char s) { return 0; } - void MoveObj(int obj, int p) {} + int hugo_hasgraphics() { return 0; } }; } // End of namespace Hugo diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h index 260586eab3..ce2d0061fd 100644 --- a/engines/glk/hugo/hugo_defines.h +++ b/engines/glk/hugo/hugo_defines.h @@ -31,9 +31,16 @@ namespace Hugo { #define HEVERSION 3 #define HEREVISION 3 #define HEINTERIM ".0" +#define GLK +#define DEBUGGER #define MAXOBJLIST 32 #define MAX_CONTEXT_COMMANDS 32 +#define MAX_EVAL_ELEMENTS 256 +#define MAX_GAME_TITLE 64 +#define MAX_DEBUG_LINE 256 +#define MAX_OBJECT 999 +#define MAX_PROPERTY 999 #define MAXBUFFER 255 #define MAXUNDO 1024 #define CHARWIDTH 1 @@ -140,6 +147,12 @@ browsing. #define TAIL_RECURSION_ROUTINE (-1) #define TAIL_RECURSION_PROPERTY (-2) +#if defined (DEBUGGER) +#define VIEW_CALLS 0 +#define VIEW_LOCALS 1 +#define CODE_WINDOW 2 +#endif + } // End of namespace Hugo } // End of namespace Glk diff --git a/engines/glk/hugo/hugo_types.h b/engines/glk/hugo/hugo_types.h index 9298510504..3d7af86894 100644 --- a/engines/glk/hugo/hugo_types.h +++ b/engines/glk/hugo/hugo_types.h @@ -107,10 +107,33 @@ struct CODE_BLOCK { int type; ///< see #defines, below long brk; ///< break address, or 0 to indicate NOP long returnaddr; ///< used only for do-while loops +#if defined (DEBUGGER) + int dbnest; ///< for recovering from 'break' +#endif + + CODE_BLOCK() : type(0), brk(0), returnaddr(0) +#if defined (DEBUGGER) + , dbnest(0) +#endif + { + } +}; + +#if defined (DEBUGGER) +struct CALL { + long addr; + bool param; - CODE_BLOCK() : type(0), brk(0), returnaddr(0) {} + CALL() : addr(0), param(false) {} }; +struct WINDOW { + int count; + + WINDOW() : count(99) {} +}; +#endif + } // End of namespace Hugo } // End of namespace Glk diff --git a/engines/glk/module.mk b/engines/glk/module.mk index 74ab60f9a8..41c53c38e7 100644 --- a/engines/glk/module.mk +++ b/engines/glk/module.mk @@ -74,6 +74,7 @@ MODULE_OBJS := \ hugo/detection.o \ hugo/heglk.o \ hugo/hemisc.o \ + hugo/heobject.o \ hugo/htokens.o \ hugo/hugo.o \ hugo/stringfn.o \ -- cgit v1.2.3