Files
2026-02-02 04:50:13 +01:00

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