357 lines
8.2 KiB
C++
357 lines
8.2 KiB
C++
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#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) {
|
|
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 == nullptr || 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 != nullptr)
|
|
{
|
|
/*
|
|
* 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];
|
|
|
|
Common::sprintf_s(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 = nullptr;
|
|
}
|
|
|
|
/* 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 */
|
|
Common::strcpy_s(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
|
|
} // End of namespace TADS
|
|
} // End of namespace Glk
|