Initial commit
This commit is contained in:
356
engines/glk/tads/tads2/play.cpp
Normal file
356
engines/glk/tads/tads2/play.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user