aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/glk/tads/tads2/play.cpp324
1 files changed, 323 insertions, 1 deletions
diff --git a/engines/glk/tads/tads2/play.cpp b/engines/glk/tads/tads2/play.cpp
index 115d2b6ac1..0c78c6dd09 100644
--- a/engines/glk/tads/tads2/play.cpp
+++ b/engines/glk/tads/tads2/play.cpp
@@ -21,13 +21,335 @@
*/
#include "glk/tads/tads2/play.h"
+#include "glk/tads/tads2/error.h"
+#include "glk/tads/tads2/file_io.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
void plygo(runcxdef *run, voccxdef *voc, tiocxdef *tio, objnum preinit, char *restore_fname) {
- // TODO
+ int err;
+ errcxdef *ec = run->runcxerr;
+ char filbuf[128];
+ int first_time;
+ int noreg inited = FALSE;
+
+ NOREG((&inited));
+
+ first_time = TRUE;
+
+ /*
+ * Write out the special <?T2> HTML sequence, in case we're on an HTML
+ * system. This tells the HTML parser to use the parsing rules for
+ * TADS 2 callers.
+ */
+ outformat("\\H+<?T2>\\H-");
+
+startover:
+ if (!inited)
+ {
+ /* use Me as the format-string actor for preinit and init */
+ tiosetactor(voc->voccxtio, voc->voccxme);
+
+ /*
+ * Run preinit, if it hasn't been run yet. Note that we only
+ * do this the first time through. If we come back here via the
+ * restart function, preinit will already have been run in the
+ * restart function itself, so we don't need to run it again.
+ */
+ if (first_time)
+ {
+ /* make a note that we've been through here once already */
+ first_time = FALSE;
+
+ /* remember the preinit function for later use in restarting */
+ voc->voccxpreinit = preinit;
+
+ /* run the preinit() function */
+ ERRBEGIN(ec)
+ {
+ /* reset the interpreter */
+ runrst(run);
+
+ /* reset the parser */
+ voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
+
+ /* run preinit */
+ if (preinit != MCMONINV)
+ runfn(run, preinit, 0);
+ }
+ ERRCATCH(ec, err)
+ {
+ /* if they restarted, go back and start over */
+ if (err == ERR_RUNRESTART)
+ goto startover;
+
+ /* resignal the error */
+ errrse(ec);
+ }
+ ERREND(ec);
+ }
+
+ /*
+ * Run the "init" function. Do NOT run init if we're restoring
+ * a game directly from the command line AND there's an
+ * initRestore function defined.
+ */
+ if (restore_fname == 0 || voc->voccxinitrestore == MCMONINV)
+ {
+ ERRBEGIN(ec)
+ {
+ /* reset the interpreter */
+ runrst(run);
+
+ /* reset the parser */
+ voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
+
+ /* run init */
+ runfn(run, (objnum)voc->voccxini, 0);
+ }
+ ERRCATCH(ec, err)
+ {
+ /* if they restarted, go back and start over */
+ if (err == ERR_RUNRESTART)
+ goto startover;
+
+ /* resignal the error */
+ errrse(ec);
+ }
+ ERREND(ec);
+ }
+ }
+
+ /* next time through, we'll need to run init again */
+ inited = FALSE;
+
+ /*
+ * check for startup parameter file to restore - if there's a
+ * system-specific parameter file specified, pretend that it was
+ * specified as the restore file
+ */
+ if (os_paramfile(filbuf))
+ restore_fname = filbuf;
+
+ /* check for a file to restore */
+ if (restore_fname != 0)
+ {
+ /*
+ * Check to see if the game file supports the initRestore
+ * function. If so, call it to restore the game. If not,
+ * restore the game directly.
+ */
+ if (voc->voccxinitrestore != MCMONINV)
+ {
+ char restore_buf[OSFNMAX*2];
+ char *src;
+ char *dst;
+
+ /* convert any backslashes to double backslashes */
+ for (src = restore_fname, dst = restore_buf ;
+ *src != '\0' && dst + 2 < restore_buf + sizeof(restore_buf) ;
+ ++src)
+ {
+ switch(*src)
+ {
+ case '\\':
+ /* it's a backslash - double it */
+ *dst++ = '\\';
+ *dst++ = '\\';
+ break;
+
+ default:
+ /* copy the character as-is */
+ *dst++ = *src;
+ }
+ }
+
+ /*
+ * all the game's initRestore function with the name of
+ * saved game file to restore as the argument
+ */
+
+ /* reset the interpreter */
+ runrst(run);
+
+ /* reset the parser */
+ voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
+
+ /* push the game file name and run initRestore */
+ runpstr(run, restore_buf, dst - restore_buf, 0);
+ runfn(run, (objnum)voc->voccxinitrestore, 1);
+ }
+ else
+ {
+ /* restore the game */
+ os_printz("\n\n[Restoring saved game]\n\n");
+ err = fiorso(voc, restore_fname);
+ if (err)
+ {
+ char buf[60 + OSFNMAX];
+
+ sprintf(buf, "\n\nError: unable to restore file \"%s\"\n\n",
+ restore_fname);
+ os_printz(buf);
+ }
+ }
+
+ /* forget the saved game name, in case we restore */
+ restore_fname = 0;
+ }
+
+ /* clear out the redo command buffer */
+ voc->voccxredobuf[0] = '\0';
+
+ /* read and execute commands */
+ for (;;)
+ {
+ char buf[128];
+
+ err = 0;
+ ERRBEGIN(ec)
+
+ /* read a new command if there's nothing to redo */
+ if (!voc->voccxredo)
+ {
+ /* reset hidden output so we're showing output */
+ tioshow(tio);
+ tioflush(tio);
+
+ /* clear the interpreter stack */
+ runrst(run);
+
+ /* read a command */
+ vocread(voc, MCMONINV, MCMONINV, buf, (int)sizeof(buf), 0);
+
+ /* special qa checking */
+ if (buf[0] == '@')
+ {
+ int quiet = FALSE;
+ char *p;
+
+ p = buf + 1;
+ if (*p == '@')
+ {
+ /* turn off MORE mode */
+ setmore(0);
+
+ /* set NONSTOP mode in the OS layer */
+ os_nonstop_mode(TRUE);
+
+ /* skip the extra '@' */
+ ++p;
+ }
+ else if (*p == '!')
+ {
+ quiet = TRUE;
+ ++p;
+ }
+ while (*p != '\0' && t_isspace(*p)) ++p;
+ if (*p != '\0')
+ {
+ /* open the named file */
+ qasopn(p, quiet);
+ }
+ else
+ {
+ char fname[256];
+
+ /* no file was named - ask the user to select a file */
+ if (tio_askfile("Read script file:", fname, sizeof(fname),
+ OS_AFP_OPEN, OSFTCMD) == 0)
+ qasopn(fname, quiet);
+ }
+ goto end_loop;
+ }
+ }
+
+ /*
+ * If there's redo in the redo buffer, use it now. If the
+ * buffer is empty and the redo flag is set, we'll just
+ * re-execute whatever's in our internal buffer.
+ */
+ if (voc->voccxredo && voc->voccxredobuf[0] != '\0')
+ {
+ /* copy the redo buffer into our internal buffer */
+ strcpy(buf, voc->voccxredobuf);
+
+ /* we've consumed it now, so clear it out */
+ voc->voccxredobuf[0] = '\0';
+ }
+
+ /* we've now consumed the redo */
+ voc->voccxredo = FALSE;
+
+ /* clear any pending break that's queued up */
+ (void)os_break();
+
+ /* execute the command */
+ (void)voccmd(voc, buf, (uint)sizeof(buf));
+
+ end_loop:
+ ERRCATCH(ec, err)
+ {
+ if (err != ERR_RUNQUIT
+ && err != ERR_RUNRESTART
+ && !(err == ERR_RUNABRT && voc->voccxredo))
+ errclog(ec);
+ }
+ ERREND(ec);
+
+ /* on interrupt, undo last command (which was partially executed) */
+ if (err == ERR_USRINT && voc->voccxundo)
+ {
+ ERRBEGIN(ec)
+ objundo(voc->voccxmem, voc->voccxundo);
+ ERRCATCH(ec, err)
+ if (err != ERR_NOUNDO && err != ERR_ICUNDO)
+ errrse(ec);
+ ERREND(ec)
+ }
+
+ /* if they want to quit, we're done */
+ if (err == ERR_RUNQUIT)
+ break;
+ else if (err == ERR_RUNRESTART)
+ goto startover;
+ }
+
+ /*
+ * If we're quitting, give the debugger one last chance at taking
+ * control. If it just returns, we can go ahead and terminate, but
+ * if it wants it can restart the game by calling bifrst() as
+ * normal.
+ */
+ ERRBEGIN(ec)
+ {
+ /* clear anything in the debugger stack trace */
+ run->runcxdbg->dbgcxfcn = 0;
+ run->runcxdbg->dbgcxdep = 0;
+
+ /* tell the debugger the game has exited */
+ dbguquitting(run->runcxdbg);
+ }
+ ERRCATCH(ec, err)
+ {
+ switch(err)
+ {
+ case ERR_RUNRESTART:
+ /* they restarted the game - re-enter the play loop */
+ goto startover;
+
+ case ERR_RUNQUIT:
+ /* quitting - proceed to return as normal */
+ break;
+
+ default:
+ /* resignal any other error */
+ errrse(ec);
+ }
+ }
+ ERREND(ec);
}
} // End of namespace TADS2