aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2019-05-10 11:02:16 +1000
committerPaul Gilbert2019-05-10 12:11:06 +1000
commit88300636350e18eced9d27691980705f47d2a43c (patch)
treeae669f681ea328edc8f8c393ee4d0b3f74430beb
parent2765ba9afa4918e46ab0835c0f6e9183c57b612a (diff)
downloadscummvm-rg350-88300636350e18eced9d27691980705f47d2a43c.tar.gz
scummvm-rg350-88300636350e18eced9d27691980705f47d2a43c.tar.bz2
scummvm-rg350-88300636350e18eced9d27691980705f47d2a43c.zip
GLK: HUGO: Added hemisc, htokens, and stringfn files
-rw-r--r--engines/glk/hugo/heglk.cpp4
-rw-r--r--engines/glk/hugo/hemisc.cpp1899
-rw-r--r--engines/glk/hugo/htokens.cpp79
-rw-r--r--engines/glk/hugo/htokens.h100
-rw-r--r--engines/glk/hugo/hugo.cpp45
-rw-r--r--engines/glk/hugo/hugo.h255
-rw-r--r--engines/glk/hugo/hugo_defines.h19
-rw-r--r--engines/glk/hugo/hugo_types.h36
-rw-r--r--engines/glk/hugo/stringfn.cpp110
-rw-r--r--engines/glk/hugo/stringfn.h65
-rw-r--r--engines/glk/module.mk3
11 files changed, 2599 insertions, 16 deletions
diff --git a/engines/glk/hugo/heglk.cpp b/engines/glk/hugo/heglk.cpp
index 86be90c268..1d4586c9ab 100644
--- a/engines/glk/hugo/heglk.cpp
+++ b/engines/glk/hugo/heglk.cpp
@@ -82,8 +82,8 @@ void Hugo::hugo_getline(const char *prmpt) {
/* Copy the input to the script file (if open) */
if (script) {
- Common::String line = Common::String::format("%s%s\n", prompt, buffer);
- script->putBuffer(line.c_str(), line.size());
+ Common::String text = Common::String::format("%s%s\n", prompt, buffer);
+ script->putBuffer(text.c_str(), text.size());
}
}
diff --git a/engines/glk/hugo/hemisc.cpp b/engines/glk/hugo/hemisc.cpp
new file mode 100644
index 0000000000..ac3a0d4dd0
--- /dev/null
+++ b/engines/glk/hugo/hemisc.cpp
@@ -0,0 +1,1899 @@
+/* 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 {
+
+void Hugo::AP(const char *a) {
+ char sticky = false, skipspchar = false, startofline = 0;
+ int i, alen, plen, cwidth;
+ char c = 0; /* current character */
+#ifdef USE_SMARTFORMATTING
+ char lastc = 0; /* for smart formatting */
+#endif
+
+ static int lastfcolor = 16, lastbgcolor = 17;
+ static int lastfont = NORMAL_FONT;
+ static int thisline = 0; /* width in pixels or characters */
+ static int linebreaklen = 0, linebreak = 0;
+ int tempfont;
+ char printed_something = false;
+#ifdef USE_TEXTBUFFER
+ int bufferfont = currentfont;
+#endif
+
+ /* Shameless little trick to override control characters in engine-
+ printed text, such as MS-DOS filenames that contain '\'s:
+ */
+ if (a[0] == NO_CONTROLCHAR)
+ {
+ skipspchar = true;
+ a++;
+ }
+
+ /* Semi-colon overrides LF */
+ if ((strlen(a) >= 2) && a[strlen(a) - 1] == ';' && a[strlen(a) - 2] == '\\')
+ {
+ sticky = true;
+ }
+
+ if (hugo_strlen(pbuffer))
+ printed_something = true;
+
+ plen = strlen(pbuffer);
+ if (plen == 0)
+ {
+ thisline = 0;
+ linebreak = 0;
+ linebreaklen = 0;
+ lastfont = currentfont;
+ startofline = true;
+#ifdef USE_TEXTBUFFER
+ bufferbreak = 0;
+ bufferbreaklen = 0;
+#endif
+#ifdef USE_SMARTFORMATTING
+ leftquote = true;
+#endif
+ }
+
+ /* Check for color changes */
+ if ((a[0]) && (lastfcolor != fcolor || lastbgcolor != bgcolor || startofline))
+ {
+ if (plen >= MAXBUFFER * 2 - 3) FatalError(OVERFLOW_E);
+ pbuffer[plen++] = COLOR_CHANGE;
+ pbuffer[plen++] = (char)(fcolor + 1);
+ pbuffer[plen++] = (char)(bgcolor + 1);
+ pbuffer[plen] = '\0';
+ lastfcolor = fcolor;
+ lastbgcolor = bgcolor;
+ }
+
+ /* Check for font changes--since fonts can only get changed
+ by printing, we don't check lastfont */
+ if ((a[0]) && startofline)
+ {
+ if (plen >= MAXBUFFER * 2 - 2) FatalError(OVERFLOW_E);
+ pbuffer[plen++] = FONT_CHANGE;
+ pbuffer[plen++] = (char)(currentfont + 1);
+ pbuffer[plen] = '\0';
+ lastfont = currentfont;
+ }
+
+ /* Begin by looping through the entire provided string: */
+
+ alen = (int)strlen(a);
+ if (sticky)
+ alen -= 2;
+
+ /* Not printing any actual text, so we won't need to go through
+ queued font changes
+ */
+ if (alen == 0)
+ lastfont = currentfont;
+
+ for (i = 0; i<alen; i++)
+ {
+ c = a[i];
+
+ /* Left-justification */
+ if (thisline == 0 && c == ' ' && !textto && currentpos == 0)
+ continue;
+
+ /* First check control characters */
+ if (c == '\\' && !skipspchar)
+ {
+ c = a[++i];
+
+ switch (c)
+ {
+ case 'n':
+ {
+ c = '\n';
+ break;
+ }
+ case 'B':
+ {
+ currentfont |= BOLD_FONT;
+ goto AddFontCode;
+ }
+ case 'b':
+ {
+ currentfont &= ~BOLD_FONT;
+ goto AddFontCode;
+ }
+ case 'I':
+ {
+ currentfont |= ITALIC_FONT;
+ goto AddFontCode;
+ }
+ case 'i':
+ {
+ currentfont &= ~ITALIC_FONT;
+ goto AddFontCode;
+ }
+ case 'P':
+ {
+ currentfont |= PROP_FONT;
+ goto AddFontCode;
+ }
+ case 'p':
+ {
+ currentfont &= ~PROP_FONT;
+ goto AddFontCode;
+ }
+ case 'U':
+ {
+ currentfont |= UNDERLINE_FONT;
+ goto AddFontCode;
+ }
+ case 'u':
+ {
+ currentfont &= ~UNDERLINE_FONT;
+ AddFontCode:
+ if (!textto)
+ {
+ int m, n;
+ int newfull;
+ double ratio;
+
+ if (!printed_something)
+ {
+ for (m = 0; m<plen;)
+ {
+ if (pbuffer[m] == FONT_CHANGE)
+ {
+ for (n = m; n<plen - 2; n++)
+ {
+ pbuffer[n] = pbuffer[n + 2];
+ }
+ plen -= 2;
+ pbuffer[plen] = '\0';
+ lastfont = currentfont;
+ }
+ else if (pbuffer[m] == COLOR_CHANGE)
+ m += 3;
+ else
+ break;
+ }
+ }
+#ifdef USE_TEXTBUFFER
+ if (hugo_strlen(pbuffer + bufferbreak) == 0)
+ bufferfont = currentfont;
+#endif
+ if (plen >= MAXBUFFER * 2 - 2) FatalError(OVERFLOW_E);
+ pbuffer[plen + 2] = '\0';
+ pbuffer[plen + 1] = (char)(currentfont + 1);
+ pbuffer[plen] = FONT_CHANGE;
+ plen += 2;
+
+ /* Convert full if font height changes, since
+ the amount of used screen real estate for
+ this particular font (in terms of lines)
+ will have changed:
+ */
+ ratio = (double)lineheight;
+ hugo_font(currentfont);
+ ratio /= (double)lineheight;
+ newfull = (int)(((double)full)*ratio + 0.5);
+ if (newfull) full = newfull;
+ }
+ continue;
+ }
+ case '_': /* forced space */
+ {
+ if (textto) c = ' ';
+ else c = FORCED_SPACE;
+ break;
+ }
+ default:
+ c = SpecialChar(a, &i);
+ }
+ }
+ else if (game_version <= 22)
+ {
+ if (c == '~')
+ c = '\"';
+ else if (c == '^')
+ c = '\n';
+ }
+
+ /* Add the new character */
+
+ /* Text may be sent to an address in the array table instead
+ of being output to the screen
+ */
+ if (textto)
+ {
+ /* space for array length */
+ int n = (game_version>23) ? 2 : 0;
+
+ if (c == '\n')
+ {
+ SETMEM(arraytable * 16L + textto * 2 + n, 0);
+ textto++;
+ }
+ else if ((unsigned char)c >= ' ')
+ {
+ SETMEM(arraytable * 16L + textto * 2 + n, c);
+ textto++;
+ }
+ /* Add a terminating zero in case we don't
+ print any more to the array */
+ SETMEM(arraytable * 16L + textto * 2 + n, 0);
+
+ if (i >= alen) return;
+
+ continue; /* back to for (i=0; i<slen; i++) */
+ }
+
+ printed_something = true;
+
+ /* Handle in-text newlines */
+ if (c == '\n')
+ {
+ hugo_font(currentfont = lastfont);
+#ifdef USE_TEXTBUFFER
+ TB_AddWord(pbuffer + bufferbreak,
+ current_text_x + bufferbreaklen,
+ current_text_y,
+ current_text_x + thisline - 1,
+ current_text_y + lineheight - 1);
+#endif
+ Printout(pbuffer);
+ lastfont = currentfont;
+#ifdef USE_TEXTBUFFER
+ bufferfont = currentfont;
+ bufferbreak = 0;
+ bufferbreaklen = 0;
+#endif
+ strcpy(pbuffer, "");
+ plen = 0;
+ linebreak = 0;
+ linebreaklen = 0;
+ thisline = 0;
+#ifdef USE_SMARTFORMATTING
+ leftquote = true;
+ lastc = '\n';
+#endif
+ pbuffer[plen++] = COLOR_CHANGE;
+ pbuffer[plen++] = (char)(fcolor + 1);
+ pbuffer[plen++] = (char)(bgcolor + 1);
+ pbuffer[plen] = '\0';
+
+
+ continue;
+ }
+
+#ifdef USE_SMARTFORMATTING
+ /* Smart formatting only for non-fixed fonts */
+ if ((currentfont & PROP_FONT) && smartformatting)
+ {
+ if ((!strncmp(a + i, "--", 2)) && lastc != '-' && strncmp(a + i, "---", 3))
+ {
+ lastc = '-';
+ c = (char)151;
+ i++;
+ leftquote = false;
+ }
+ else if (c == '\"')
+ {
+ if (leftquote)
+ c = (char)147;
+ else
+ c = (char)148;
+ leftquote = false;
+ lastc = c;
+ }
+ else if (c == '\'')
+ {
+ if (leftquote)
+ c = (char)145;
+ else
+ c = (char)146;
+ leftquote = false;
+ lastc = c;
+ }
+ else
+ {
+ if (c == ' ')
+ leftquote = true;
+ else
+ leftquote = false;
+ lastc = c;
+ }
+ }
+#endif
+
+ /* Add the new character to the printing buffer */
+ if (plen >= MAXBUFFER * 2 - 1) FatalError(OVERFLOW_E);
+ pbuffer[plen + 1] = '\0';
+ pbuffer[plen] = c;
+ plen++;
+
+ cwidth = hugo_charwidth(c);
+
+ /* Check to see if we've overrun the current line */
+
+ if (thisline + cwidth + currentpos > physical_windowwidth)
+ {
+ char t;
+
+ if (!linebreak)
+ {
+ linebreak = plen - 1;
+ linebreaklen = thisline;
+ }
+
+ t = pbuffer[linebreak];
+ pbuffer[linebreak] = '\0';
+
+ tempfont = currentfont;
+ hugo_font(currentfont = lastfont);
+ Printout(pbuffer);
+ lastfont = currentfont;
+ hugo_font(currentfont = tempfont);
+
+ pbuffer[linebreak] = t;
+ plen = strlen(pbuffer + linebreak);
+ memmove(pbuffer, pbuffer + linebreak, plen + 1);
+ thisline = thisline - linebreaklen;
+ linebreak = 0;
+ linebreaklen = 0;
+ startofline = 0;
+#ifdef USE_TEXTBUFFER
+ bufferbreak = 0;
+ bufferbreaklen = 0;
+#endif
+ }
+
+ thisline += cwidth;
+
+#ifdef USE_TEXTBUFFER
+ if ((c == ' ' || c == FORCED_SPACE) ||
+ (c == '/' && a[i + 1] != '/') || (c == '-' && a[i + 1] != '-'))
+ {
+ TB_AddWord(pbuffer + bufferbreak,
+ current_text_x + bufferbreaklen,
+ current_text_y,
+ current_text_x + thisline - 1,
+ current_text_y + lineheight - 1);
+
+ bufferbreak = plen;
+ bufferbreaklen = thisline;
+ bufferfont = currentfont;
+ }
+#endif
+ if ((c == ' ') || (c == '/' && a[i + 1] != '/') || (c == '-' && a[i + 1] != '-'))
+ {
+ linebreak = plen, linebreaklen = thisline;
+ }
+ }
+
+#ifdef USE_TEXTBUFFER
+ if (!sticky || alen > 1)
+ {
+ tempfont = currentfont;
+ currentfont = bufferfont;
+
+ TB_AddWord(pbuffer + bufferbreak,
+ current_text_x + bufferbreaklen,
+ current_text_y,
+ current_text_x + thisline - 1,
+ current_text_y + lineheight - 1);
+
+ bufferbreak = plen;
+ bufferbreaklen = thisline;
+ currentfont = tempfont;
+ }
+#endif
+ if (!sticky)
+ {
+ hugo_font(currentfont = lastfont);
+ Printout(pbuffer);
+ lastfont = currentfont;
+ strcpy(pbuffer, "");
+ linebreak = 0;
+ linebreaklen = 0;
+ thisline = 0;
+ plen = 0;
+#ifdef USE_TEXTBUFFER
+ bufferbreak = 0;
+ bufferbreaklen = 0;
+#endif
+#ifdef USE_SMARTFORMATTING
+ leftquote = true;
+#endif
+ }
+ }
+
+int Hugo::CallRoutine(unsigned int addr) {
+ int arg, i;
+ int val;
+ int templocals[MAXLOCALS], temppass[MAXLOCALS];
+ int temp_stack_depth;
+ long tempptr;
+ int potential_tail_recursion = tail_recursion;
+#if defined (DEBUGGER)
+ int tempdbnest;
+#endif
+ arg = 0;
+ tail_recursion = 0;
+
+ /* Pass local variables to routine, if specified */
+ if (MEM(codeptr) == OPEN_BRACKET_T)
+ {
+ codeptr++;
+ while (MEM(codeptr) != CLOSE_BRACKET_T)
+ {
+ if (arg)
+ {
+ for (i = 0; i<arg; i++)
+ temppass[i] = passlocal[i];
+ }
+
+ passlocal[arg++] = GetValue();
+
+ if (arg > 1)
+ {
+ for (i = 0; i<arg - 1; i++)
+ passlocal[i] = temppass[i];
+ }
+
+ if (MEM(codeptr) == COMMA_T) codeptr++;
+ }
+ codeptr++;
+ }
+
+ /* TAIL_RECURSION_ROUTINE if we came from a routine call immediately
+ following a 'return' statement...
+ */
+ tail_recursion = potential_tail_recursion;
+ if (tail_recursion == TAIL_RECURSION_ROUTINE && MEM(codeptr) == EOL_T)
+ {
+ tail_recursion_addr = (long)addr*address_scale;
+ PassLocals(arg);
+ return 0;
+ }
+ /* ...but if we're not immediately followed by and end-of-line marker,
+ cancel the pending tail-recursion
+ */
+ else
+ {
+ tail_recursion = 0;
+ }
+
+ for (i = 0; i<MAXLOCALS; i++)
+ templocals[i] = var[MAXGLOBALS + i];
+ PassLocals(arg);
+
+ temp_stack_depth = stack_depth;
+
+ SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+ tempptr = codeptr; /* store calling address */
+ ret = 0;
+
+#if defined (DEBUGGER)
+ tempdbnest = dbnest;
+ DebugRunRoutine((long)addr*address_scale);
+ dbnest = tempdbnest;
+#else
+ RunRoutine((long)addr*address_scale);
+#endif
+ retflag = 0;
+ val = ret;
+ codeptr = tempptr;
+
+ stack_depth = temp_stack_depth;
+
+ for (i = 0; i<MAXLOCALS; i++)
+ var[MAXGLOBALS + i] = templocals[i];
+
+ return val;
+ }
+
+void Hugo::ContextCommand() {
+ unsigned int n;
+
+ ContextCommandLoop:
+
+ codeptr++;
+
+ n = GetValue();
+#if !defined (COMPILE_V25)
+ if (n == 0)
+ {
+ context_commands = 0;
+ }
+ else if (context_commands < MAX_CONTEXT_COMMANDS)
+ {
+ char *cc;
+
+ strncpy(context_command[context_commands], cc = GetWord(n), 64);
+ context_command[context_commands][63] = '\0';
+ if (strlen(cc) >= 64)
+ sprintf(context_command[context_commands] + 60, "...");
+ context_commands++;
+ }
+#endif
+ if (Peek(codeptr) == COMMA_T) goto ContextCommandLoop;
+ codeptr++;
+ }
+
+unsigned int Hugo::Dict() {
+ int i, len = 256;
+ unsigned int arr;
+ unsigned int pos = 2, loc;
+
+ codeptr += 2; /* "(" */
+
+ if (MEM(codeptr) == PARSE_T || MEM(codeptr) == WORD_T)
+ strcpy(line, GetWord(GetValue()));
+ else
+ {
+ /* Get the array address to read the to-be-
+ created dictionary entry from:
+ */
+ arr = GetValue();
+ if (game_version >= 22)
+ {
+ /* Convert the address to a word
+ value:
+ */
+ arr *= 2;
+
+ if (game_version >= 23)
+ /* space for array length */
+ arr += 2;
+ }
+
+ defseg = arraytable;
+ for (i = 0; i<len && PeekWord(arr + i * 2) != 0; i++)
+ line[i] = (char)PeekWord(arr + i * 2);
+ defseg = gameseg;
+ line[i] = '\0';
+ }
+
+ if (Peek(codeptr) == COMMA_T) codeptr++;
+ len = GetValue();
+
+ if ((loc = FindWord(line)) != UNKNOWN_WORD) return loc;
+
+ defseg = dicttable;
+
+ for (i = 1; i <= dictcount; i++)
+ pos += Peek(pos) + 1;
+
+ loc = pos - 2;
+
+ if ((long)(pos + strlen(line)) > (long)(codeend - dicttable * 16L))
+ {
+#ifdef DEBUGGER
+ sprintf(debug_line, "$MAXDICTEXTEND dictionary space exceeded");
+ RuntimeWarning(debug_line);
+#endif
+ defseg = gameseg;
+ return 0;
+ }
+
+ Poke(pos++, (unsigned char)strlen(line));
+ for (i = 0; i<(int)strlen(line) && i<len; i++)
+ Poke(pos++, (unsigned char)(line[i] + CHAR_TRANSLATION));
+ PokeWord(0, ++dictcount);
+
+ defseg = gameseg;
+
+ SaveUndo(DICT_T, strlen(line), 0, 0, 0);
+
+ return loc;
+ }
+
+void Hugo::FatalError(int n){
+ char fatalerrorline[64];
+
+ hugo_cleanup_screen();
+
+ switch (n)
+ {
+ case MEMORY_E:
+ {sprintf(line, "Out of memory\n");
+ break; }
+
+ case OPEN_E:
+ {sprintf(line, "Cannot open file\n");
+ break; }
+
+ case READ_E:
+ {sprintf(line, "Cannot read from file\n");
+ break; }
+
+ case WRITE_E:
+ {sprintf(line, "Cannot write to save file\n");
+ break; }
+
+ case EXPECT_VAL_E:
+ {sprintf(line, "Expecting value at $%s\n", PrintHex(codeptr));
+ break; }
+
+ case UNKNOWN_OP_E:
+ {sprintf(line, "Unknown operation at $%s\n", PrintHex(codeptr));
+ break; }
+
+ case ILLEGAL_OP_E:
+ {sprintf(line, "Illegal operation at $%s\n", PrintHex(codeptr));
+ break; }
+
+ case OVERFLOW_E:
+ {sprintf(line, "Overflow at $%s\n", PrintHex(codeptr));
+ break; }
+
+ case DIVIDE_E:
+ {sprintf(line, "Divide by zero at $%s\n", PrintHex(codeptr));
+ break; }
+ }
+
+ sprintf(fatalerrorline, "\nFatal Error: %s", line);
+ error(fatalerrorline);
+}
+
+void Hugo::FileIO() {
+ char fileiopath[MAXPATH];
+ char iotype;
+ unsigned int fnameval;
+ long skipaddr;
+ int i, temp_stack_depth = stack_depth;
+
+ iotype = MEM(codeptr++);
+ skipaddr = (long)PeekWord(codeptr)*address_scale;
+ codeptr += 2;
+ fnameval = GetValue();
+ if (game_version >= 23) codeptr++; /* eol */
+
+ ioerror = 0;
+
+ /* Make sure the filename is legal, 8 alphanumeric characters or less */
+ strcpy(line, GetWord(fnameval));
+ if (strlen(line) > 8) goto LeaveFileIO;
+ for (i = 0; i<(int)strlen(line); i++)
+ {
+ if ((line[i] >= '0' && line[i] <= '9') || (line[i] >= 'A' && line[i] <= 'Z') ||
+ (line[i] >= 'a' && line[i] <= 'z'))
+ {
+ continue;
+ }
+ else
+ goto LeaveFileIO;
+ }
+
+ if (ioblock) goto LeaveFileIO; /* can't nest file operations */
+
+ strcpy(fileiopath, GetWord(fnameval));
+
+ if (iotype == WRITEFILE_T) /* "writefile" */
+ {
+ /* Glk implementation */
+ frefid_t fref = nullptr;
+
+ fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
+ fileiopath, 0);
+ io = glk_stream_open_file(fref, filemode_Write, 0);
+ glk_fileref_destroy(fref);
+ if (io == nullptr) goto LeaveFileIO;
+
+ ioblock = 1;
+ }
+ else /* "readfile" */
+ {
+ /* Glk implementation */
+ frefid_t fref = nullptr;
+
+ fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
+ fileiopath, 0);
+ if (glk_fileref_does_file_exist(fref))
+ io = glk_stream_open_file(fref, filemode_Read, 0);
+ else
+ io = nullptr;
+ glk_fileref_destroy(fref);
+ if (io == nullptr) goto LeaveFileIO;
+
+ ioblock = 2;
+ }
+
+ SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+ RunRoutine(codeptr);
+
+ stack_depth = temp_stack_depth;
+
+ if (ioerror) retflag = 0;
+
+ delete io;
+ io = nullptr;
+ ioblock = 0;
+
+ LeaveFileIO:
+ ioerror = 0;
+ codeptr = skipaddr;
+ }
+
+void Hugo::Flushpbuffer() {
+ if (pbuffer[0] == '\0') return;
+
+#ifdef USE_TEXTBUFFER
+ /* For (single) characters left over from AP(), when Flushpbuffer() gets
+ called, say, from RunWindow()
+ */
+ if (bufferbreaklen && pbuffer + bufferbreaklen && !(currentfont&PROP_FONT))
+ {
+ TB_AddWord(pbuffer + bufferbreak,
+ current_text_x + bufferbreaklen,
+ current_text_y,
+ current_text_x + bufferbreaklen + FIXEDCHARWIDTH,
+ current_text_y + lineheight - 1);
+ }
+#endif
+
+ pbuffer[strlen(pbuffer) + 1] = '\0';
+ pbuffer[strlen(pbuffer)] = (char)NO_NEWLINE;
+ Printout(Ltrim(pbuffer));
+ currentpos = hugo_textwidth(pbuffer); /* -charwidth; */
+ strcpy(pbuffer, "");
+ }
+
+void Hugo::GetCommand() {
+ char a[256];
+#ifdef USE_TEXTBUFFER
+ int start, width, i, y;
+#endif
+ Flushpbuffer();
+ AP("");
+
+ hugo_settextcolor(fcolor);
+ hugo_setbackcolor(bgcolor);
+ if (icolor == -1)
+ icolor = fcolor; /* check unset input color */
+
+ strncpy(a, GetWord(var[prompt]), 255);
+ during_player_input = true;
+ full = 0;
+#ifdef USE_TEXTBUFFER
+ /* Add the prompt to the textbuffer (using TB_Add()) */
+ y = current_text_y;
+ width = hugo_textwidth(GetWord(var[prompt]));
+ TB_AddWord(GetWord(var[prompt]), physical_windowleft, y,
+ physical_windowleft + width, y + lineheight - 1);
+
+ hugo_getline(a);
+
+ /* If hugo_scrollwindowup() called by hugo_getline() shifted things */
+ if (current_text_y > y)
+ {
+ y += (current_text_y - y);
+ }
+
+ /* Add each word in the input buffer */
+ start = 0;
+ for (i = 0; i<(int)strlen(buffer); i++)
+ {
+ if (buffer[i] == ' ')
+ {
+ buffer[i] = '\0';
+ TB_AddWord(buffer + start, physical_windowleft + width, y - lineheight,
+ physical_windowleft + width + hugo_textwidth(buffer + start), y - 1);
+ width += hugo_textwidth(buffer + start) + hugo_textwidth(" ");
+ start = i + 1;
+ buffer[i] = ' ';
+ }
+ }
+ /* Add the final word */
+ TB_AddWord(buffer + start, physical_windowleft + width, y - lineheight,
+ physical_windowleft + width + hugo_textwidth(buffer + start), y - 1);
+#else
+ hugo_getline(a);
+#endif
+ during_player_input = false;
+ strcpy(buffer, Rtrim(buffer));
+
+ strcpy(parseerr, "");
+
+ full = 1;
+ remaining = 0;
+ }
+
+char *Hugo::GetString(long addr) {
+ static char a[256];
+ int i, length;
+
+ length = Peek(addr);
+
+ for (i = 1; i <= length; i++)
+ a[i - 1] = (char)(Peek(addr + i) - CHAR_TRANSLATION);
+ a[i - 1] = '\0';
+
+ return a;
+}
+
+char *Hugo::GetText(long textaddr) {
+ static char g[1025];
+ int i, a;
+ int tdatal, tdatah, tlen; /* low byte, high byte, length */
+
+
+ /* Read the string from memory... */
+ if (loaded_in_memory)
+ {
+ tlen = MEM(codeend + textaddr) + MEM(codeend + textaddr + 1) * 256;
+ for (i = 0; i<tlen; i++)
+ {
+ g[i] = (char)(MEM(codeend + textaddr + 2 + i) - CHAR_TRANSLATION);
+ }
+ g[i] = '\0';
+
+ return g;
+ }
+
+ /* ...Or load the string from disk */
+ if (game->seek(codeend + textaddr)) FatalError(READ_E);
+
+ tdatal = game->readByte();
+ tdatah = game->readByte();
+ if (tdatal == EOF || tdatah == EOF || game->err()) FatalError(READ_E);
+
+ tlen = tdatal + tdatah * 256;
+
+ for (i = 0; i<tlen; i++)
+ {
+ if ((a = game->readByte()) == EOF) FatalError(READ_E);
+ g[i] = (char)(a - CHAR_TRANSLATION);
+ }
+ g[i] = '\0';
+
+ return g;
+}
+
+char *Hugo::GetWord(unsigned int w) {
+ static char *b;
+ unsigned short a;
+
+ a = w;
+
+ if (a == 0) return "";
+
+ if (a == PARSE_STRING_VAL) return parseerr;
+ if (a == SERIAL_STRING_VAL) return serial;
+
+ /* bounds-checking to avoid some sort of memory arena error */
+ if ((long)(a + dicttable * 16L) > codeend)
+ {
+ b = "";
+ return b;
+ }
+
+ defseg = dicttable;
+ b = GetString((long)a + 2);
+ defseg = gameseg;
+
+ return b;
+}
+
+void Hugo::HandleTailRecursion(long addr) {
+ codeptr = addr;
+
+ /* Set up proper default return value for property or routine */
+ if (tail_recursion == TAIL_RECURSION_PROPERTY)
+ ret = 1;
+ else
+ ret = 0;
+
+ /* Unstack until we get to any routine call that got us here */
+ while (stack_depth)
+ {
+ if (code_block[stack_depth].type == RUNROUTINE_BLOCK)
+ break;
+ stack_depth--;
+ }
+
+#ifdef DEBUGGER
+ currentroutine = (unsigned int)(codeptr / address_scale);
+ call[window[VIEW_CALLS].count - 1].addr = currentroutine;
+ call[window[VIEW_CALLS].count - 1].param = true;
+
+ sprintf(debug_line, "Calling: %s", RoutineName(currentroutine));
+ /* Don't duplicate blank separator line in code window */
+ if (codeline[window[CODE_WINDOW].count - 1][0] != 0)
+ AddStringtoCodeWindow("");
+ AddStringtoCodeWindow(debug_line);
+
+ /* Adjust for very long runs */
+ dbnest--;
+#endif
+
+ tail_recursion = 0;
+ tail_recursion_addr = 0;
+}
+
+void Hugo::InitGame() {
+ int i;
+
+ /* Stop any audio if this is a restart */
+ hugo_stopsample();
+ hugo_stopmusic();
+
+#if !defined (COMPILE_V25)
+ hugo_stopvideo();
+ context_commands = 0;
+#endif
+ game_reset = false;
+
+ SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
+
+ /* Figure out which objects have either a noun or an adjective property;
+ store it in obj_parselist, one bit per object */
+ if ((!obj_parselist) && (obj_parselist = (char *)hugo_blockalloc(sizeof(char)*((objects + 7) / 8))))
+ {
+
+ for (i = 0; i<objects; i++)
+ {
+ if (i % 8 == 0) obj_parselist[i / 8] = 0;
+
+ if (PropAddr(i, adjective, 0) || PropAddr(i, noun, 0))
+ obj_parselist[i / 8] |= 1 << (i % 8);
+ else
+ obj_parselist[i / 8] &= ~(1 << (i % 8));
+ }
+ }
+
+#if defined (DEBUGGER)
+ for (i = 0; i<MAXLOCALS; i++) strcpy(localname[i], "");
+ window[VIEW_LOCALS].count = current_locals = 0;
+
+ PassLocals(0);
+ DebugRunRoutine((long)initaddr*address_scale);
+#else
+ PassLocals(0);
+ RunRoutine((long)initaddr*address_scale);
+#endif
+
+ ret = 0;
+ retflag = 0;
+ var[actor] = var[player];
+}
+
+void Hugo::LoadGame() {
+ int i, data;
+ unsigned int synptr;
+ size_t ccount;
+ int check_version;
+ long textbank, filelength;
+#ifndef LOADGAMEDATA_REPLACED
+ long c;
+#endif
+ game = &_gameFile;
+ filelength = game->size();
+ game->seek(0);
+
+ if (game->err()) FatalError(READ_E);
+ if ((game_version = game->readByte()) == EOF) FatalError(OPEN_E);
+
+ /* Earlier versions of the compiler wrote the version code as
+ 1 or 2 instead of 10 or 20.
+ */
+ if (game_version == 1 || game_version == 2)
+ game_version *= 10;
+
+ if (game_version < 21) object_size = 12;
+
+ if (game_version < 31) address_scale = 4;
+
+ check_version = HEVERSION * 10 + HEREVISION;
+
+ defseg = gameseg;
+
+ if (game_version < HEVERSION)
+ {
+ sprintf(line, "Hugo Compiler v%d.%d or later required.\n", HEVERSION, HEREVISION);
+ if (game_version>0)
+ sprintf(line + strlen(line), "File \"%s\" is v%d.%d.\n", gamefile, game_version / 10, game_version % 10);
+
+ error(line);
+ }
+ else if (game_version > check_version)
+ {
+ error("File \"%s\" is incorrect or unknown version.\n", gamefile);
+ }
+
+ hugo_settextpos(1, physical_windowheight / lineheight);
+
+ if (game_version >= 25)
+ game->seek(H_TEXTBANK, SEEK_SET);
+ else
+ /* Because pre-v2.5 didn't have performaddr in the header */
+ game->seek(H_TEXTBANK - 2L, SEEK_SET);
+
+ data = game->readByte();
+ textbank = game->readByte();
+ if (data == EOF || textbank == EOF || game->err()) FatalError(READ_E);
+ textbank = (textbank * 256L + (long)data) * 16L;
+ codeend = textbank;
+
+ /* Use a 1024-byte read block */
+ ccount = 1024;
+
+ if (game->seek(0, SEEK_SET)) FatalError(READ_E);
+
+#ifndef LOADGAMEDATA_REPLACED
+ /* Allocate as much memory as is required */
+ if ((!loaded_in_memory) || (mem = (unsigned char *)hugo_blockalloc(filelength)) == nullptr)
+ {
+ loaded_in_memory = 0;
+ if ((mem = (unsigned char *)hugo_blockalloc(codeend)) == nullptr)
+ FatalError(MEMORY_E);
+ }
+
+ c = 0;
+
+ /* Load either the entire file or just up to the start of the
+ text bank
+ */
+ while (c < (loaded_in_memory ? filelength : codeend))
+ {
+ /* Complicated, but basically just makes sure that
+ the last read (whether loaded_in_memory or not)
+ doesn't override the end of the file. Shouldn't
+ normally be a problem for fread(), but it caused
+ a crash under MSVC++.
+ */
+ i = game->read((unsigned char *)&mem[c],
+ (loaded_in_memory) ?
+ ((filelength - c>(long)ccount) ? ccount : (size_t)(filelength - c)) :
+ ((codeend - c>(long)ccount) ? ccount : (size_t)(codeend - c)));
+
+ if (!i) break;
+ c += i;
+ }
+#else
+ if (!LoadGameData(false)) FatalError(READ_E);
+#endif
+
+ if (game->err()) FatalError(READ_E);
+
+ defseg = gameseg;
+
+ /* Read header: */
+
+ id[0] = Peek(H_ID);
+ id[1] = Peek(H_ID + 1);
+ id[2] = '\0';
+
+ for (i = 0; i<8; i++)
+ serial[i] = Peek(H_SERIAL + i);
+ serial[8] = '\0';
+
+ codestart = PeekWord(H_CODESTART);
+ objtable = PeekWord(H_OBJTABLE) + gameseg;
+ proptable = PeekWord(H_PROPTABLE) + gameseg;
+ eventtable = PeekWord(H_EVENTTABLE) + gameseg;
+ arraytable = PeekWord(H_ARRAYTABLE) + gameseg;
+ dicttable = PeekWord(H_DICTTABLE) + gameseg;
+ syntable = PeekWord(H_SYNTABLE) + gameseg;
+
+ initaddr = PeekWord(H_INIT);
+ mainaddr = PeekWord(H_MAIN);
+ parseaddr = PeekWord(H_PARSE);
+ parseerroraddr = PeekWord(H_PARSEERROR);
+ findobjectaddr = PeekWord(H_FINDOBJECT);
+ endgameaddr = PeekWord(H_ENDGAME);
+ speaktoaddr = PeekWord(H_SPEAKTO);
+ performaddr = PeekWord(H_PERFORM);
+
+
+ /* Read totals: */
+
+ defseg = objtable;
+ objects = PeekWord(0);
+
+ defseg = eventtable;
+ events = PeekWord(0);
+
+ defseg = dicttable;
+ dictcount = PeekWord(0);
+
+ defseg = syntable;
+ syncount = PeekWord(0);
+
+
+ /* Additional information to be found: */
+
+ /* display object */
+ if (game_version >= 24)
+ {
+ data = FindWord("(display)");
+
+ for (i = 0; i<objects; i++)
+ {
+ if (GetProp(i, 0, 1, true) == data)
+ {
+ display_object = i;
+ break;
+ }
+ }
+ }
+
+ /* build punctuation string (additional user-specified punctuation) */
+ synptr = 2;
+ strcpy(punc_string, "");
+ for (i = 1; i <= syncount; i++)
+ {
+ defseg = syntable;
+ if (Peek(synptr) == 3) /* 3 = punctuation */
+ {
+ strcpy(line, GetWord(PeekWord(synptr + 1)));
+ if (strlen(line) + strlen(punc_string) > 63) break;
+ strcat(punc_string, line);
+ }
+ synptr += 5;
+ }
+}
+
+void Hugo::PassLocals(int n) {
+ int i;
+
+ for (i = 0; i<MAXLOCALS; i++)
+ {
+ var[MAXGLOBALS + i] = passlocal[i];
+ passlocal[i] = 0;
+ }
+ arguments_passed = n;
+}
+
+const char *Hugo::PrintHex(long a) {
+ static char hex[7];
+ int h = 0;
+
+ strcpy(hex, "");
+
+ if (a < 0L) a = 0;
+
+ hex[h++] = '0';
+ if (a < 65536L) hex[h++] = '0';
+ if (a < 4096L) hex[h++] = '0';
+ if (a < 256L) hex[h++] = '0';
+ if (a < 16L) hex[h++] = '0';
+
+ sprintf(hex + h, "%lX", a);
+
+ return hex;
+}
+
+void Hugo::Printout(char *a) {
+ char b[2], sticky = 0, trimmed = 0;
+ char tempfcolor;
+ int i, l;
+ int n;
+ int last_printed_font = currentfont;
+
+ /* hugo_font() should do this if necessary, but just in case */
+ if (lineheight < FIXEDLINEHEIGHT)
+ lineheight = FIXEDLINEHEIGHT;
+
+ tempfcolor = fcolor;
+
+ /* The before-check of the linecount: */
+ if (full)
+ {
+ /* -1 here since it's before printing */
+ if (full >= physical_windowheight / lineheight - 1)
+ PromptMore();
+ }
+
+ if ((a[0] != '\0') && a[strlen(a) - 1] == (char)NO_NEWLINE)
+ {
+ a[strlen(a) - 1] = '\0';
+ sticky = true;
+ }
+
+ b[0] = b[1] = '\0';
+
+
+ /* The easy part is just skimming <a> and processing each code
+ or printed character, as the case may be:
+ */
+
+ l = 0; /* physical length of string */
+
+ for (i = 0; i<(int)strlen(a); i++)
+ {
+ if ((a[i] == ' ') && !trimmed && currentpos == 0)
+ {
+ continue;
+ }
+
+ if ((unsigned char)a[i] > ' ' || a[i] == FORCED_SPACE)
+ {
+ trimmed = true;
+ last_printed_font = currentfont;
+ }
+
+ switch (b[0] = a[i])
+ {
+ case FONT_CHANGE:
+ n = (int)(a[++i] - 1);
+ if (currentfont != n)
+ hugo_font(currentfont = n);
+ break;
+
+ case COLOR_CHANGE:
+ fcolor = (char)(a[++i] - 1);
+ hugo_settextcolor((int)fcolor);
+ hugo_setbackcolor((int)(a[++i] - 1));
+ hugo_font(currentfont);
+ break;
+
+ default:
+ if (b[0] == FORCED_SPACE) b[0] = ' ';
+ l += hugo_charwidth(b[0]);
+
+ /* A minor adjustment for font changes and RunWindow() to make
+ sure we're not printing unnecessarily downscreen
+ */
+ if ((just_left_window) && current_text_y > physical_windowbottom - lineheight)
+ {
+ current_text_y = physical_windowbottom - lineheight;
+ }
+ just_left_window = false;
+
+ hugo_print(b);
+ }
+
+ if (script && (unsigned char)b[0] >= ' ')
+ script->putBuffer(b, strlen(b));
+
+#if defined (SCROLLBACK_DEFINED)
+ if (!inwindow && (unsigned char)b[0] >= ' ')
+ {
+#ifdef USE_SMARTFORMATTING
+ /* Undo smart-formatting for ASCII scrollback */
+ switch ((unsigned char)b[0])
+ {
+ case 151:
+ hugo_sendtoscrollback("--");
+ continue;
+ case 145:
+ case 146:
+ b[0] = '\'';
+ break;
+ case 147:
+ case 148:
+ b[0] = '\"';
+ }
+#endif
+ hugo_sendtoscrollback(b);
+ }
+#endif
+ }
+
+ /* If we've got a linefeed and didn't hit the right edge of the
+ window
+ */
+#ifdef NO_TERMINAL_LINEFEED
+ if (!sticky)
+#else
+ if (!sticky && currentpos + l < physical_windowwidth)
+#endif
+ {
+ /* The background color may have to be temporarily set if we're
+ not in a window--the reason is that full lines of the
+ current background color might be printed by the OS-specific
+ scrolling function. (This behavior is overridden by the
+ Hugo Engine for in-window printing, which always adds new
+ lines in the current background color when scrolling.)
+ */
+ hugo_setbackcolor((inwindow) ? bgcolor : default_bgcolor);
+ hugo_print("\r");
+
+ i = currentfont;
+ hugo_font(currentfont = last_printed_font);
+
+#ifndef GLK
+ if (currentline > physical_windowheight / lineheight)
+ {
+ int full_limit = physical_windowheight / lineheight;
+
+ hugo_scrollwindowup();
+
+ if ((current_text_y)
+ && full >= full_limit - 3
+ && physical_windowbottom - current_text_y - lineheight > lineheight / 2)
+ {
+ PromptMore();
+ }
+ currentline = full_limit;
+ }
+
+ /* Don't scroll single-line windows before PromptMore() */
+ else if (physical_windowheight / lineheight > 1)
+#endif
+ {
+ hugo_print("\n");
+ }
+
+ hugo_font(currentfont = i);
+ hugo_setbackcolor(bgcolor);
+ }
+
+#if defined (AMIGA)
+ else
+ {
+ if (currentpos + l >= physical_windowwidth)
+ AmigaForceFlush();
+ }
+#endif
+ just_left_window = false;
+
+ /* If no newline is to be printed after the current line: */
+ if (sticky)
+ {
+ currentpos += l;
+ }
+
+ /* Otherwise, take care of all the line-feeding, line-counting,
+ etc.
+ */
+ else
+ {
+ currentpos = 0;
+ if (currentline++ > physical_windowheight / lineheight)
+ currentline = physical_windowheight / lineheight;
+
+ if (!playback) skipping_more = false;
+
+ ++full;
+
+ /* The after-check of the linecount: */
+ if ((full) && full >= physical_windowheight / lineheight)
+ {
+ PromptMore();
+ }
+
+ if (script)
+ {
+ script->putBuffer("\n", 1);
+ }
+
+#if defined (SCROLLBACK_DEFINED)
+ if (!inwindow) hugo_sendtoscrollback("\n");
+#endif
+ }
+
+ fcolor = tempfcolor;
+}
+
+int Hugo::RecordCommands() {
+ remaining = 0;
+ skipping_more = false;
+
+ switch (Peek(codeptr))
+ {
+ case RECORDON_T:
+ {
+ if (!record && !playback)
+ {
+ /* Glk implementation */
+ frefid_t fref = nullptr;
+
+ fref = glk_fileref_create_by_prompt(fileusage_Transcript | fileusage_TextMode,
+ filemode_Write, 0);
+ record = glk_stream_open_file(fref, filemode_Write, 0);
+ glk_fileref_destroy(fref);
+ if (!record)
+ return 0;
+
+ return 1;
+ }
+ break;
+ }
+
+ case RECORDOFF_T:
+ {
+ if (playback) return 1;
+
+ if (record)
+ {
+ delete record;
+ record = nullptr;
+ return 1;
+ }
+ break;
+ }
+
+ case PLAYBACK_T:
+ {
+ if (!playback)
+ {
+ /* Glk implementation */
+ frefid_t fref = nullptr;
+
+ fref = glk_fileref_create_by_prompt(fileusage_InputRecord | fileusage_TextMode,
+ filemode_Read, 0);
+ if (glk_fileref_does_file_exist(fref))
+ playback = glk_stream_open_file(fref, filemode_Read, 0);
+ else
+ playback = nullptr;
+ glk_fileref_destroy(fref);
+ if (!playback)
+ return 0;
+
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+void Hugo::SaveUndo(int a, int b, int c, int d, int e) {
+ int tempptr;
+
+ if (undorecord)
+ {
+ undostack[undoptr][0] = a; /* save the operation */
+ undostack[undoptr][1] = b;
+ undostack[undoptr][2] = c;
+ undostack[undoptr][3] = d;
+ undostack[undoptr][4] = e;
+
+ /* Put zeroes at end of this operation in case
+ the stack wraps around */
+ tempptr = undoptr;
+ if (++undoptr == MAXUNDO) undoptr = 0;
+ undostack[undoptr][0] = 0;
+ undostack[undoptr][1] = 0;
+ undoptr = tempptr;
+
+ if (++undoturn == MAXUNDO) /* turn too complex */
+ {
+ undoptr = 0;
+ undoturn = MAXUNDO;
+ undoinvalid = 1;
+ }
+
+ if (++undoptr == MAXUNDO) undoptr = 0;
+ }
+}
+
+void Hugo::SetStackFrame(int depth, int type, long brk, long returnaddr) {
+ if (depth == RESET_STACK_DEPTH) stack_depth = 0;
+ else if (++stack_depth >= MAXSTACKDEPTH) FatalError(MEMORY_E);
+
+ code_block[stack_depth].type = type;
+ code_block[stack_depth].brk = brk;
+ code_block[stack_depth].returnaddr = returnaddr;
+}
+
+void Hugo::SetupDisplay() {
+ hugo_settextmode();
+
+ hugo_settextwindow(1, 1,
+ SCREENWIDTH / FIXEDCHARWIDTH, SCREENHEIGHT / FIXEDLINEHEIGHT);
+
+ last_window_left = 1;
+ last_window_top = 1;
+ last_window_right = SCREENWIDTH / FIXEDCHARWIDTH;
+ last_window_bottom = SCREENHEIGHT / FIXEDLINEHEIGHT;
+
+ hugo_settextcolor(16);
+ hugo_setbackcolor(17);
+ hugo_clearfullscreen();
+}
+
+char Hugo::SpecialChar(const char *a, int *i) {
+ char r, s, skipbracket = 0;
+
+ r = a[*i];
+ s = r;
+
+ if (r == '\"') return r;
+
+ /* For a couple of versions, Hugo allowed Inform-style
+ punctuation control characters; I don't remember
+ exactly why.
+ */
+ if (game_version <= 22)
+ if (r == '~' || r == '^') return r;
+
+ if (r == '(')
+ {
+ r = a[++*i];
+ skipbracket = true;
+ }
+
+ switch (r)
+ {
+ case '`': /* accent grave */
+ {
+ /* Note that the "s = '...'" characters are
+ Latin-1 and may not display properly under,
+ e.g., DOS */
+
+ s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+ switch (s)
+ {
+ case 'a': s = (char)0xe0; break; /* ? */
+ case 'e': s = (char)0xe8; break; /* ? */
+ case 'i': s = (char)0xec; break; /* ? */
+ case 'o': s = (char)0xf2; break; /* ? */
+ case 'u': s = (char)0xf9; break; /* ? */
+ case 'A': s = (char)0xc0; break; /* ? */
+ case 'E': s = (char)0xc8; break; /* ? */
+ case 'I': s = (char)0xcc; break; /* ? */
+ case 'O': s = (char)0xd2; break; /* ? */
+ case 'U': s = (char)0xd9; break; /* ? */
+ }
+#endif
+ break;
+ }
+ case '\'': /* accent acute */
+ {
+ s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+ switch (s)
+ {
+ case 'a': s = (char)0xe1; break; /* ? */
+ case 'e': s = (char)0xe9; break; /* ? */
+ case 'i': s = (char)0xed; break; /* ? */
+ case 'o': s = (char)0xf3; break; /* ? */
+ case 'u': s = (char)0xfa; break; /* ? */
+ case 'y': s = (char)0xfd; break;
+ case 'A': s = (char)0xc1; break; /* ? */
+ case 'E': s = (char)0xc9; break; /* ? */
+ case 'I': s = (char)0xcd; break; /* ? */
+ case 'O': s = (char)0xd3; break; /* ? */
+ case 'U': s = (char)0xda; break; /* ? */
+ case 'Y': s = (char)0xdd; break; /* ? */
+ }
+#endif
+ break;
+ }
+ case '~': /* tilde */
+ {
+ s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+ switch (s)
+ {
+ case 'a': s = (char)0xe3; break; /* ? */
+ case 'n': s = (char)0xf1; break; /* ? */
+ case 'o': s = (char)0xf5; break; /* ? */
+ case 'A': s = (char)0xc3; break; /* ? */
+ case 'N': s = (char)0xd1; break; /* ? */
+ case 'O': s = (char)0xd5; break; /* ? */
+ }
+#endif
+ break;
+ }
+ case '^': /* circumflex */
+ {
+ s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+ switch (s)
+ {
+ case 'a': s = (char)0xe2; break; /* ? */
+ case 'e': s = (char)0xea; break; /* ? */
+ case 'i': s = (char)0xee; break; /* ? */
+ case 'o': s = (char)0xf4; break; /* ? */
+ case 'u': s = (char)0xfb; break; /* ? */
+ case 'A': s = (char)0xc2; break; /* ? */
+ case 'E': s = (char)0xca; break; /* ? */
+ case 'I': s = (char)0xce; break; /* ? */
+ case 'O': s = (char)0xd4; break; /* ? */
+ case 'U': s = (char)0xdb; break; /* ? */
+ }
+#endif
+ break;
+ }
+ case ':': /* umlaut */
+ {
+ s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+ switch (s)
+ {
+ case 'a': s = (char)0xe4; break; /* ? */
+ case 'e': s = (char)0xeb; break; /* ? */
+ case 'i': s = (char)0xef; break; /* ? */
+ case 'o': s = (char)0xf6; break; /* ? */
+ case 'u': s = (char)0xfc; break; /* ? */
+ /* case 'y': s = (char)0xff; break; */ /* ? */
+ case 'A': s = (char)0xc4; break; /* ? */
+ case 'E': s = (char)0xcb; break; /* ? */
+ case 'I': s = (char)0xcf; break; /* ? */
+ case 'O': s = (char)0xd6; break; /* ? */
+ case 'U': s = (char)0xdc; break; /* ? */
+ }
+#endif
+ break;
+ }
+ case ',': /* cedilla */
+ {
+ s = a[++*i];
+#ifndef NO_LATIN1_CHARSET
+ switch (s)
+ {
+ case 'C': s = (char)0xc7; break; /* ? */
+ case 'c': s = (char)0xe7; break; /* ? */
+ }
+#endif
+ break;
+ }
+ case '<': /* Spanish left quotation marks */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xab; /* ? */
+#endif
+ break;
+ case '>': /* Spanish right quotation marks */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xbb; /* ? */
+ break;
+#endif
+ case '!': /* upside-down exclamation mark */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xa1; /* ? */
+#endif
+ break;
+ case '?': /* upside-down question mark */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xbf; /* ? */
+#endif
+ break;
+ case 'a': /* ae ligature */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xe6; ++*i; /* ? */
+#else
+ s = 'e'; ++*i;
+#endif
+ break;
+ case 'A': /* AE ligature */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xc6; ++*i; /* ? */
+#else
+ s = 'E'; ++*i;
+#endif
+ break;
+ case 'c': /* cents symbol */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xa2; /* ? */
+#endif
+ break;
+ case 'L': /* British pound */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xa3; /* ? */
+#endif
+ break;
+ case 'Y': /* Japanese Yen */
+#ifndef NO_LATIN1_CHARSET
+ s = (char)0xa5; /* ? */
+#endif
+ break;
+ case '-': /* em dash */
+#ifndef NO_LATIN1_CHARSET
+ /* s = (char)0x97; */ /* ? */
+#endif
+ break;
+ case '#': /* 3-digit decimal code */
+ {
+ s = (char)((a[++*i] - '0') * 100);
+ s += (a[++*i] - '0') * 10;
+ s += (a[++*i] - '0');
+#ifdef NO_LATIN1_CHARSET
+ if ((unsigned)s>127) s = '?';
+#endif
+ }
+ }
+
+ if (skipbracket)
+ {
+ ++*i;
+ if (a[*i + 1] == ')')++*i;
+ if (s == ')') s = r;
+ }
+
+ return s;
+}
+
+int Hugo::Undo() {
+ int count = 0, n;
+ int turns, turncount, tempptr;
+ int obj, prop, attr, v;
+ unsigned int addr;
+
+ if (--undoptr < 0) undoptr = MAXUNDO - 1;
+
+ if (undostack[undoptr][1] != 0)
+ {
+ /* Get the number of operations to be undone for
+ the last turn.
+ */
+ if ((turns = undostack[undoptr][1]) >= MAXUNDO)
+ goto CheckUndoFailed;
+
+ turns--;
+
+ turncount = 0;
+ tempptr = undoptr;
+
+ /* Count the number of operations available to see if there
+ are enough to undo the last turn (as per the number
+ required in <turns>.
+ */
+ do
+ {
+ if (--undoptr < 0) undoptr = MAXUNDO - 1;
+ turncount++;
+
+ /* if end of turn */
+ if (undostack[undoptr][0] == 0)
+ break;
+ } while (true);
+
+ if (turncount<turns) goto CheckUndoFailed;
+
+ undoptr = tempptr;
+
+ if (--undoptr < 0) undoptr = MAXUNDO - 1;
+
+ while (undostack[undoptr][0] != 0)
+ {
+ switch (undostack[undoptr][0])
+ {
+ case MOVE_T:
+ {
+ MoveObj(undostack[undoptr][1], undostack[undoptr][2]);
+ count++;
+ break;
+ }
+
+ case PROP_T:
+ {
+ obj = undostack[undoptr][1];
+ prop = undostack[undoptr][2];
+ n = undostack[undoptr][3];
+ v = undostack[undoptr][4];
+
+ if ((addr = PropAddr(obj, prop, 0)) != 0)
+ {
+ defseg = proptable;
+
+ if (n == PROP_ROUTINE)
+ {
+ Poke(addr + 1, PROP_ROUTINE);
+ n = 1;
+ }
+
+ /* Use this new prop count number if the
+ existing one is too low or a prop routine
+ */
+ else if (Peek(addr + 1) == PROP_ROUTINE || Peek(addr + 1)<(unsigned char)n)
+ Poke(addr + 1, (unsigned char)n);
+
+ /* property length */
+ if (n <= (int)Peek(addr + 1))
+ PokeWord(addr + 2 + (n - 1) * 2, v);
+ }
+ count++;
+ break;
+ }
+
+ case ATTR_T:
+ {
+ obj = undostack[undoptr][1];
+ attr = undostack[undoptr][2];
+ n = undostack[undoptr][3];
+ SetAttribute(obj, attr, n);
+ count++;
+ break;
+ }
+
+ case VAR_T:
+ {
+ n = undostack[undoptr][1];
+ v = undostack[undoptr][2];
+ var[n] = v;
+ count++;
+ break;
+ }
+
+ case ARRAYDATA_T:
+ {
+ defseg = arraytable;
+ addr = undostack[undoptr][1];
+ n = undostack[undoptr][2];
+ v = undostack[undoptr][3];
+
+ /* The array length was already accounted for before calling
+ SaveUndo(), so there is no adjustment of
+ +2 here.
+ */
+ PokeWord(addr + n * 2, v);
+ count++;
+ break;
+ }
+
+ case DICT_T:
+ {
+ defseg = dicttable;
+ PokeWord(0, --dictcount);
+ count++;
+ break;
+ }
+ case WORD_T:
+ {
+ n = undostack[undoptr][1];
+ v = undostack[undoptr][2];
+ wd[n] = v;
+ word[n] = GetWord(wd[n]);
+ count++;
+ }
+ }
+ defseg = gameseg;
+
+ if (--undoptr < 0) undoptr = MAXUNDO - 1;
+ }
+ }
+
+CheckUndoFailed:
+ if (!count)
+ {
+ undoinvalid = 1;
+ game_reset = false;
+ return 0;
+ }
+
+ game_reset = true;
+
+ undoptr++;
+ return 1;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/htokens.cpp b/engines/glk/hugo/htokens.cpp
new file mode 100644
index 0000000000..fd913b260e
--- /dev/null
+++ b/engines/glk/hugo/htokens.cpp
@@ -0,0 +1,79 @@
+/* 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/htokens.h"
+#include "Common/algorithm.h"
+
+namespace Glk {
+namespace Hugo {
+
+const char *const HTokens::token[] = {
+ /* 0x00 - 0x0f */
+ "", "(", ")", ".", ":", "=", "-", "+",
+ "*", "/", "|", ";", "{", "}", "[", "]",
+
+ /* 0x10 - 0x1f */
+ "#", "~", ">=", "<=", "~=", "&", ">", "<",
+ "if", ",", "else", "elseif", "while", "do", "select", "case",
+
+ /* 0x20 - 0x2f */
+ "for", "return", "break", "and", "or", "jump", "run", "is",
+ "not", "true", "false", "local", "verb", "xverb", "held", "multi",
+
+ /* 0x30 - 0x3f */
+ "multiheld", "newline", "anything", "print",
+ "number", "capital", "text", "graphics",
+ "color", "remove", "move", "to",
+ "parent", "sibling", "child", "youngest",
+
+ /* 0x40 - 0x4f */
+ "eldest", "younger", "elder", "prop#",
+ "attr#", "var#", "dictentry#", "textdata#",
+ "routine#","debugdata#","objectnum#", "value#",
+ "eol#", "system", "notheld", "multinotheld",
+
+ /* 0x50 - 0x5f */
+ "window", "random", "word", "locate",
+ "parse$", "children", "in", "pause",
+ "runevents", "arraydata#", "call", "stringdata#",
+ "save", "restore", "quit", "input",
+
+ /* 0x60 - 0x6f */
+ "serial$", "cls", "scripton", "scriptoff",
+ "restart", "hex", "object", "xobject",
+ "string", "array", "printchar", "undo",
+ "dict", "recordon", "recordoff", "writefile",
+
+ /* 0x70 - */
+ "readfile", "writeval", "readval", "playback",
+ "colour", "picture", "label#", "sound",
+ "music", "repeat", "addcontext", "video"
+};
+
+int HTokens::token_hash[TOKENS + 1];
+
+HTokens::HTokens() {
+ Common::fill(&token_hash[0], &token_hash[TOKENS + 1], 0);
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/htokens.h b/engines/glk/hugo/htokens.h
new file mode 100644
index 0000000000..08961c7bc0
--- /dev/null
+++ b/engines/glk/hugo/htokens.h
@@ -0,0 +1,100 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_HUGO_HTOKENS
+#define GLK_HUGO_HTOKENS
+
+namespace Glk {
+namespace Hugo {
+
+/*
+ * This file contains token definitions for the Hugo Compiler and Engine
+ * The enum constants of type TOKEN_T reflect the token names given in the token array.
+ * Token names followed by a # are for system use.
+ */
+
+/* i.e., highest numbered token */
+#define TOKENS 0x7B
+
+/* arbitrary */
+#define HASH_KEY 1023
+
+enum TOKEN_T {
+ /* 0x00 - 0x0f */
+ NULL_T, OPEN_BRACKET_T, CLOSE_BRACKET_T, DECIMAL_T,
+ COLON_T, EQUALS_T, MINUS_T, PLUS_T,
+ ASTERISK_T, FORWARD_SLASH_T, PIPE_T, SEMICOLON_T,
+ OPEN_BRACE_T, CLOSE_BRACE_T, OPEN_SQUARE_T, CLOSE_SQUARE_T,
+
+ /* 0x10 - 0x1f */
+ POUND_T, TILDE_T, GREATER_EQUAL_T, LESS_EQUAL_T,
+ NOT_EQUAL_T, AMPERSAND_T, GREATER_T, LESS_T,
+ IF_T, COMMA_T, ELSE_T, ELSEIF_T,
+ WHILE_T, DO_T, SELECT_T, CASE_T,
+
+ /* 0x20 - 0x2f */
+ FOR_T, RETURN_T, BREAK_T, AND_T,
+ OR_T, JUMP_T, RUN_T, IS_T,
+ NOT_T, TRUE_T, FALSE_T, LOCAL_T,
+ VERB_T, XVERB_T, HELD_T, MULTI_T,
+
+ /* 0x30 - 0x3f */
+ MULTIHELD_T, NEWLINE_T, ANYTHING_T, PRINT_T,
+ NUMBER_T, CAPITAL_T, TEXT_T, GRAPHICS_T,
+ COLOR_T, REMOVE_T, MOVE_T, TO_T,
+ PARENT_T, SIBLING_T, CHILD_T, YOUNGEST_T,
+
+ /* 0x40 - 0x4f */
+ ELDEST_T, YOUNGER_T, ELDER_T, PROP_T,
+ ATTR_T, VAR_T, DICTENTRY_T, TEXTDATA_T,
+ ROUTINE_T, DEBUGDATA_T, OBJECTNUM_T, VALUE_T,
+ EOL_T, SYSTEM_T, NOTHELD_T, MULTINOTHELD_T,
+
+ /* 0x50 - 0x5f */
+ WINDOW_T, RANDOM_T, WORD_T, LOCATE_T,
+ PARSE_T, CHILDREN_T, IN_T, PAUSE_T,
+ RUNEVENTS_T, ARRAYDATA_T, CALL_T, STRINGDATA_T,
+ SAVE_T, RESTORE_T, QUIT_T, INPUT_T,
+
+ /* 0x60 - 0x6f */
+ SERIAL_T, CLS_T, SCRIPTON_T, SCRIPTOFF_T,
+ RESTART_T, HEX_T, OBJECT_T, XOBJECT_T,
+ STRING_T, ARRAY_T, PRINTCHAR_T, UNDO_T,
+ DICT_T, RECORDON_T, RECORDOFF_T, WRITEFILE_T,
+
+ /* 0x70 - */
+ READFILE_T, WRITEVAL_T, READVAL_T, PLAYBACK_T,
+ COLOUR_T, PICTURE_T, LABEL_T, SOUND_T,
+ MUSIC_T, REPEAT_T, ADDCONTEXT_T, VIDEO_T
+};
+
+struct HTokens {
+ static const char *const token[];
+ static int token_hash[];
+
+ HTokens();
+};
+
+} // End of namespace Hugo
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/hugo/hugo.cpp b/engines/glk/hugo/hugo.cpp
index afc788e59c..8e8113421a 100644
--- a/engines/glk/hugo/hugo.cpp
+++ b/engines/glk/hugo/hugo.cpp
@@ -40,29 +40,54 @@ Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gam
physical_windowwidth(0), physical_windowheight(0), physical_windowtop(0),
physical_windowleft(0), physical_windowbottom(0), physical_windowright(0),
inwindow(0), charwidth(0), lineheight(0), current_text_x(0), current_text_y(0),
- undoptr(0), undoturn(0), undoinvalid(0), undorecord(0), context_commands(0),
- in_valid_window(false), glk_fcolor(DEF_FCOLOR), glk_bgcolor(DEF_BGCOLOR),
- mainwin_bgcolor(0), glk_current_font(0), just_cleared_screen(false), secondwin_bottom(0) {
+ skipping_more(false), undoptr(0), undoturn(0), undoinvalid(0), undorecord(0),
+ context_commands(0), in_valid_window(false), glk_fcolor(DEF_FCOLOR), glk_bgcolor(DEF_BGCOLOR),
+ mainwin_bgcolor(0), glk_current_font(0), just_cleared_screen(false), secondwin_bottom(0),
+ // heobject
+ display_object(-1), display_needs_repaint(0), display_pointer_x(0), display_pointer_y(0),
+ // heparse
+ words(0), parsed_number(0), remaining(0), xverb(0), starts_with_verb(0),
+ grammaraddr(0), obj_parselist(nullptr), domain(0), odomain(0), objcount(0),
+ parse_allflag(false), pobjcount(0), pobj(0), obj_match_state(0), object_is_number(0),
+ 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) {
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(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
Common::fill(&var[0], &var[MAXLOCALS + MAXGLOBALS], 0);
-}
+
+ // heparse
+ Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
+ Common::fill(&errbuf[0], &errbuf[MAXBUFFER + 1], 0);
+ Common::fill(&line[0], &line[1025], 0);
+ Common::fill(&word[0], &word[MAXWORDS + 1], nullptr);
+ Common::fill(&wd[0], &wd[MAXWORDS + 1], 0);
+ Common::fill(&parseerr[0], &parseerr[MAXBUFFER + 1], '\0');
+ Common::fill(&parsestr[0], &parsestr[MAXBUFFER + 1], '\0');
+ Common::fill(&objlist[0], &objlist[MAXOBJLIST], 0);
+ Common::fill(&objword_cache[0], &objword_cache[MAXWORDS], 0);
+ Common::fill(&oops[0], &oops[MAXBUFFER + 1], '\0');
+ Common::fill(&punc_string[0], &punc_string[64], '\0');
-// TODO: Proper method implementations
-void SetupDisplay() {}
-void LoadGame() {}
-void PlayGame() {}
-void hugo_closefiles() {}
+ // herun
+ Common::fill(&passlocal[0], &passlocal[MAXLOCALS], 0);
+}
void Hugo::runGame() {
hugo_init_screen();
SetupDisplay();
+ strcpy(gamefile, getFilename().c_str());
strcpy(pbuffer, "");
gameseg = 0;
diff --git a/engines/glk/hugo/hugo.h b/engines/glk/hugo/hugo.h
index 7d12646e7a..a33c5998c8 100644
--- a/engines/glk/hugo/hugo.h
+++ b/engines/glk/hugo/hugo.h
@@ -25,8 +25,10 @@
#include "common/scummsys.h"
#include "glk/glk_api.h"
+#include "glk/hugo/htokens.h"
#include "glk/hugo/hugo_defines.h"
#include "glk/hugo/hugo_types.h"
+#include "glk/hugo/stringfn.h"
namespace Glk {
namespace Hugo {
@@ -34,7 +36,7 @@ namespace Hugo {
/**
* Hugo game interpreter
*/
-class Hugo : public GlkAPI {
+class Hugo : public GlkAPI, public HTokens, public StringFunctions {
private:
winid_t mainwin, currentwin;
winid_t secondwin, auxwin;
@@ -46,9 +48,12 @@ private:
*/
int address_scale;
+ // hemisc
+ char gamefile[255];
int game_version;
int object_size;
- HUGO_FILE game;
+ //HUGO_FILE game;
+ Common::SeekableReadStream *game;
HUGO_FILE script;
HUGO_FILE save;
HUGO_FILE playback;
@@ -100,6 +105,7 @@ private:
int inwindow;
int charwidth, lineheight, FIXEDCHARWIDTH, FIXEDLINEHEIGHT;
int current_text_x, current_text_y;
+ bool skipping_more;
int undostack[MAXUNDO][5];
int undoptr;
int undoturn;
@@ -113,11 +119,83 @@ 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
+ int display_pointer_x, display_pointer_y;
+
// heparse
char buffer[MAXBUFFER + MAXWORDS];
+ char errbuf[MAXBUFFER + 1]; ///< last invalid input
+ char line[1025]; ///< line buffer
+
+ int words; ///< parsed word count
+ char *word[MAXWORDS + 1]; ///< breakdown into words
+ unsigned int wd[MAXWORDS + 1]; ///< " " dict. entries
+ unsigned int parsed_number; ///< needed for numbers in input
+
+ signed char remaining; ///< multiple commands in input
+ char parseerr[MAXBUFFER + 1]; ///< for passing to RunPrint, etc.
+ char parsestr[MAXBUFFER + 1]; ///< for passing quoted string
+ char xverb; ///< flag; 0 = regular verb
+ char starts_with_verb; ///< input line; 0 = no verb word
+ unsigned int grammaraddr; ///< address in grammar
+ char *obj_parselist; ///< objects with noun/adjective
+ int domain, odomain; ///< of object(s)
+ int objlist[MAXOBJLIST]; ///< for objects of verb
+ char objcount; ///< of objlist
+ char parse_allflag; ///< for "all" in MatchObject
+ pobject_structure pobjlist[MAXPOBJECTS]; ///< for possible objects
+ int pobjcount; ///< of pobjlist
+ int pobj; ///< last remaining suspect
+ int obj_match_state; ///< see MatchCommand() for details
+ char objword_cache[MAXWORDS]; ///< for MatchWord() xobject, etc.
+ char object_is_number; ///< number used in player command
+ unsigned int objgrammar; ///< for 2nd pass
+ int objstart; ///< " " "
+ int objfinish; ///< " " "
+ char addflag; ///< true if adding to objlist[]
+ int speaking; ///< if command is addressed to obj.
+
+ char oops[MAXBUFFER + 1]; ///< illegal word
+ int oopscount; ///< # of corrections in a row
+
+ char parse_called_twice;
+ char reparse_everything;
+ char punc_string[64]; ///< punctuation string
+
+ char full_buffer;
+ /**
+ * to MatchObject()
+ * Necessary for proper disambiguation when addressing a character;
+ * i.e., when 'held' doesn't refer to held by the player, etc.
+ */
+ char recursive_call;
+ int parse_location; ///< usually var[location]
+
+ // herun
+ int passlocal[MAXLOCALS]; ///< locals passed to routine
+ int arguments_passed; ///< when calling routine
+ int ret; ///< return value and returning flag
+ char retflag;
+ bool during_player_input;
+ char override_full;
+ bool game_reset; ///< for restore, undo, etc.
+
+ CODE_BLOCK code_block[MAXSTACKDEPTH];
+ int stack_depth;
+ int tail_recursion;
+ long tail_recursion_addr;
+
+ // Used by RunWindow for setting current window dimensions:
+ int last_window_top, last_window_bottom, last_window_left, last_window_right;
+ int lowest_windowbottom, ///< in text lines
+ physical_lowest_windowbottom; ///< in pixels or text lines
+ bool just_left_window;
- // heexpr
- int var[MAXLOCALS + MAXGLOBALS];
private:
/**
* \defgroup heglk
@@ -260,6 +338,161 @@ private:
void hugo_stopvideo();
/**@}*/
+
+ /**
+ * \defgroup hemisc
+ * @{
+ */
+
+ /**
+ * The all-purpose printing routine that takes care of word-wrapping.
+ */
+ void AP(const char *a);
+
+ /**
+ * Used whenever a routine is called, assumes the routine address and begins
+ * with the arguments (if any).
+ */
+ int CallRoutine(unsigned int addr);
+
+ /**
+ * Adds a command to the context command list. A zero value (i.e., an empty string)
+ * resets the list.
+ */
+ void ContextCommand();
+
+ /**
+ * Dynamically creates a new dictionary entry.
+ */
+ unsigned int Dict();
+
+ /**
+ * Generates a fatal error
+ */
+ void FatalError(int n);
+
+ void FileIO();
+
+ void Flushpbuffer();
+
+ void GetCommand();
+
+ /**
+ * From any address <addr>; the segment must be defined prior to calling the function.
+ */
+ char *GetString(long addr);
+
+ /**
+ * Get text block from position <textaddr> in the text bank. If the game was not fully loaded
+ * in memory, i.e., if loaded_in_memory is not true, the block is read from disk.
+ */
+ char *GetText(long textaddr);
+
+ /**
+ * From the dictionary table.
+ */
+ char *GetWord(unsigned int w);
+
+ void HandleTailRecursion(long addr);
+
+ void InitGame();
+
+ void LoadGame();
+
+ /**
+ * Must be called before running every new routine, i.e. before calling RunRoutine().
+ * Unfortunately, the current locals must be saved in a temp array prior to calling.
+ * The argument n gives the number of arguments passed.
+ */
+ void PassLocals(int n);
+
+ inline unsigned char Peek(long a) {
+ return MEM(defseg * 16L + a);
+ }
+
+ inline unsigned int PeekWord(long a) {
+ return (unsigned char)MEM(defseg * 16L + a) + (unsigned char)MEM(defseg * 16L + a + 1) * 256;
+ }
+
+ inline void Poke(unsigned int a, unsigned char v) {
+ SETMEM(defseg * 16L + a, v);
+ }
+
+ inline void PokeWord(unsigned int a, unsigned int v) {
+ SETMEM(defseg * 16L + a, (char)(v % 256));
+ SETMEM(defseg * 16L + a + 1, (char)(v / 256));
+ }
+
+ /**
+ * Returns <a> as a hex-number string in XXXXXX format.
+ */
+ static const char *PrintHex(long a);
+
+ /**
+ * Print to client display taking into account cursor relocation,
+ * font changes, color setting, and window scrolling.
+ */
+ void Printout(char *a);
+
+ int RecordCommands();
+
+ /**
+ * Formats:
+ *
+ * end of turn: (0, undoturn, 0, 0, 0)
+ * move obj.: (MOVE_T, obj., parent, 0, 0)
+ * property: (PROP_T, obj., prop., # or PROP_ROUTINE, val.)
+ * attribute: (ATTR_T, obj., attr., 0 or 1, 0)
+ * variable: (VAR_T, var., value, 0, 0)
+ * array: (ARRAYDATA_T, array addr., element, val., 0)
+ * dict: (DICT_T, entry length, 0, 0, 0)
+ * word setting: (WORD_T, word number, new word, 0, 0)
+ */
+ void SaveUndo(int a, int b, int c, int d, int e);
+
+ /**
+ * Properly sets up the code_block structure for the current stack depth depending
+ * on if this is a called block (RUNROUTINE_BLOCK) or otherwise.
+ */
+ void SetStackFrame(int depth, int type, long brk, long returnaddr);
+
+ void SetupDisplay();
+
+ /**
+ * The method is passed <a> as the string and <*i> as the position in the string.
+ * The character(s) at a[*i], a[*(i+1)], etc. are converted into a single Latin-1
+ * (i.e., greater than 127) character value.
+ *
+ * Assume that the AP() has already encountered a control character ('\'),
+ * and that a[*i]... is one of:
+ *
+ * `a accent grave on following character (e.g., 'a')
+ * 'a accent acute on following character (e.g., 'a')
+ * ~n tilde on following (e.g., 'n' or 'N')
+ * :a umlaut on following (e.g., 'a')
+ * ^a circumflex on following (e.g., 'a')
+ * ,c cedilla on following (e.g., 'c' or 'C')
+ * < Spanish left quotation marks
+ * > Spanish right quotation marks
+ * ! upside-down exclamation mark
+ * ? upside-down question mark
+ * ae ae ligature
+ * AE AE ligature
+ * c cents symbol
+ * L British pound
+ * Y Japanese Yen
+ * - em (long) dash
+ * #nnn character value given by nnn
+ *
+ * Note that the return value is a single character--which will be either unchanged
+ * or a Latin-1 character value.
+ */
+ char SpecialChar(const char *a, int *i);
+
+ int Undo();
+
+
+ /**@}*/
private:
/**
* Allocate memory block
@@ -292,6 +525,20 @@ public:
* Save the game to the passed stream
*/
virtual Common::Error saveGameData(strid_t file, const Common::String &desc) override;
+
+ // TODO: Stubs to be Properly implemented
+ int GetValue() { return 0; }
+ void PlayGame() {}
+ void hugo_closefiles() {}
+ void RunRoutine(long v) {}
+ unsigned int FindWord(const char *a) { return 0; }
+ 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) {}
};
} // End of namespace Hugo
diff --git a/engines/glk/hugo/hugo_defines.h b/engines/glk/hugo/hugo_defines.h
index caa1755b82..260586eab3 100644
--- a/engines/glk/hugo/hugo_defines.h
+++ b/engines/glk/hugo/hugo_defines.h
@@ -28,6 +28,11 @@
namespace Glk {
namespace Hugo {
+#define HEVERSION 3
+#define HEREVISION 3
+#define HEINTERIM ".0"
+
+#define MAXOBJLIST 32
#define MAX_CONTEXT_COMMANDS 32
#define MAXBUFFER 255
#define MAXUNDO 1024
@@ -121,6 +126,20 @@ browsing.
#define PROP_END 255
#define PROP_ROUTINE 255
+#define MEM(addr) (mem[addr])
+#define SETMEM(addr, n) (mem[addr] = n)
+#define GETMEMADDR(addr) (&mem[addr])
+#define HUGO_PTR
+
+#define RESET_STACK_DEPTH (-1)
+
+#define RUNROUTINE_BLOCK 1
+#define CONDITIONAL_BLOCK 2
+#define DOWHILE_BLOCK 3
+
+#define TAIL_RECURSION_ROUTINE (-1)
+#define TAIL_RECURSION_PROPERTY (-2)
+
} // 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 43b2899f93..9298510504 100644
--- a/engines/glk/hugo/hugo_types.h
+++ b/engines/glk/hugo/hugo_types.h
@@ -75,6 +75,42 @@ enum ObjectProperties {
pointer_y = 12
};
+/**
+ * Fatal errors
+ */
+enum ERROR_TYPE {
+ MEMORY_E = 1, ///< out of memory
+ OPEN_E, ///< error opening file
+ READ_E, ///< error reading from file
+ WRITE_E, ///< error writing to file
+ EXPECT_VAL_E, ///< expecting value
+ UNKNOWN_OP_E, ///< unknown operation
+ ILLEGAL_OP_E, ///< illegal operation
+ OVERFLOW_E, ///< overflow
+ DIVIDE_E ///< divide by zero
+};
+
+/**
+ * A structure used for disambiguation in MatchObject()
+ */
+struct pobject_structure {
+ int obj; ///< the actual object number
+ char type; ///< referred to by noun or adjective
+
+ pobject_structure() : obj(0), type(0) {}
+};
+
+/**
+ * Structure used for navigating {...} blocks:
+ */
+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
+
+ CODE_BLOCK() : type(0), brk(0), returnaddr(0) {}
+};
+
} // End of namespace Hugo
} // End of namespace Glk
diff --git a/engines/glk/hugo/stringfn.cpp b/engines/glk/hugo/stringfn.cpp
new file mode 100644
index 0000000000..79886aca87
--- /dev/null
+++ b/engines/glk/hugo/stringfn.cpp
@@ -0,0 +1,110 @@
+/* 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/stringfn.h"
+
+namespace Glk {
+namespace Hugo {
+
+
+/* LEFT */
+
+char *StringFunctions::Left(char a[], int l) {
+ static char *temp;
+ int i;
+
+ temp = GetTempString();
+
+ if (l > (int)strlen(a))
+ l = strlen(a);
+ for (i = 0; i<l; i++)
+ temp[i] = a[i];
+ temp[i] = '\0';
+ return temp;
+}
+
+char *StringFunctions::Ltrim(char a[]) {
+ static char *temp;
+
+ temp = GetTempString();
+ strcpy(temp, a);
+ while (temp[0]==' ' || temp[0]=='\t')
+ strcpy(temp, temp+1);
+ return temp;
+}
+
+char *StringFunctions::Mid(char a[], int pos, int n) {
+ static char *temp;
+ int i;
+
+ temp = GetTempString();
+ pos--;
+ if (pos+n > (int)strlen(a))
+ n = strlen(a)-pos;
+ for (i = 0; i<n; i++)
+ temp[i] = a[pos+i];
+ temp[i] = '\0';
+ return temp;
+}
+
+char *StringFunctions::Right(char a[], int l) {
+ static char *temp;
+ int i;
+
+ temp = GetTempString();
+ if (l > (int)strlen(a))
+ l = strlen(a);
+ for (i = 0; i<l; i++)
+ temp[i] = a[strlen(a)-l+i];
+ temp[i] = '\0';
+ return temp;
+}
+
+char *StringFunctions::Rtrim(char a[]) {
+ static char *temp;
+ int len;
+
+ temp = GetTempString();
+ strcpy(temp, a);
+ while (((len = strlen(temp))) && (temp[len-1]==' ' || temp[len-1]=='\t'))
+ strcpy(temp, Left(temp, len-1));
+ return temp;
+}
+
+char *StringFunctions::hugo_strcpy(char *s, const char *t) {
+ char *r = s;
+ while ((*s++ = *t++) != 0) ;
+ return r;
+}
+
+char *StringFunctions::GetTempString() {
+ static char *r;
+
+ r = &_tempString[_tempstringCount][0];
+ if (++_tempstringCount >= NUM_TEMPSTRINGS)
+ _tempstringCount = 0;
+
+ return r;
+}
+
+} // End of namespace Hugo
+} // End of namespace Glk
diff --git a/engines/glk/hugo/stringfn.h b/engines/glk/hugo/stringfn.h
new file mode 100644
index 0000000000..bb339fc047
--- /dev/null
+++ b/engines/glk/hugo/stringfn.h
@@ -0,0 +1,65 @@
+/* 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.
+ *
+ */
+
+#ifndef GLK_HUGO_STRINGFN
+#define GLK_HUGO_STRINGFN
+
+#include "common/algorithm.h"
+
+namespace Glk {
+namespace Hugo {
+
+#define NUM_TEMPSTRINGS 2
+
+/**
+ * The following string-manipulation functions closely mimic BASIC-language string functionality.
+ * They do not alter the provided string; instead, they return a pointer to a static (modified) copy.
+ */
+class StringFunctions {
+private:
+ char _tempString[NUM_TEMPSTRINGS][1025];
+ int _tempstringCount;
+
+ char *GetTempString();
+public:
+ StringFunctions() : _tempstringCount(0) {
+ Common::fill(&_tempString[0][0], &_tempString[NUM_TEMPSTRINGS][1025], '\0');
+ }
+
+ char *Left(char a[], int l);
+
+ char *Ltrim(char a[]);
+
+ char *Mid(char a[], int pos, int n);
+
+ char *Right(char a[], int l);
+
+ char *Rtrim(char a[]);
+
+ char *hugo_strcpy(char *s, const char *t);
+};
+
+
+} // End of namespace Hugo
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index dd12d23f12..74ab60f9a8 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -73,7 +73,10 @@ MODULE_OBJS := \
glulxe/vm.o \
hugo/detection.o \
hugo/heglk.o \
+ hugo/hemisc.o \
+ hugo/htokens.o \
hugo/hugo.o \
+ hugo/stringfn.o \
magnetic/detection.o \
magnetic/emu.o \
magnetic/graphics.o \