diff options
-rw-r--r-- | engines/glk/tads/tads2/play.cpp | 324 |
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 |