914 lines
24 KiB
C++
914 lines
24 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/built_in.h"
|
|
#include "glk/tads/tads2/character_map.h"
|
|
#include "glk/tads/tads2/command_line.h"
|
|
#include "glk/tads/tads2/error.h"
|
|
#include "glk/tads/tads2/file_io.h"
|
|
#include "glk/tads/tads2/memory_cache_heap.h"
|
|
#include "glk/tads/tads2/os.h"
|
|
#include "glk/tads/tads2/play.h"
|
|
#include "glk/tads/tads2/post_compilation.h"
|
|
#include "glk/tads/tads2/run.h"
|
|
#include "glk/tads/tads2/runtime_app.h"
|
|
#include "glk/tads/tads2/tokenizer.h"
|
|
#include "glk/tads/tads2/tads2.h"
|
|
#include "glk/tads/tads2/vocabulary.h"
|
|
#include "glk/tads/os_frob_tads.h"
|
|
|
|
namespace Glk {
|
|
namespace TADS {
|
|
namespace TADS2 {
|
|
|
|
/* dummy setup function */
|
|
void supgnam(char *buf, tokthdef *tab, objnum sc)
|
|
{
|
|
Common::strcpy_s(buf, TOKNAMMAX + 1, "???");
|
|
}
|
|
|
|
/* dummy file read functions */
|
|
void tok_read_defines(tokcxdef *tctx, osfildef *fp, errcxdef *ec)
|
|
{
|
|
errsig(ec, ERR_UNKRSC);
|
|
}
|
|
|
|
/* dummy debugger functions */
|
|
|
|
int dbgbpset(dbgcxdef *ctx, char *addr, int *bpnum) { return 0; }
|
|
|
|
int dbgbpat(dbgcxdef *ctx, objnum objn, objnum self,
|
|
uint ofs, int *bpnum, char *bpname, int toggle,
|
|
char *condition, int *did_set) { return 0; }
|
|
|
|
int dbgbpatid(dbgcxdef *ctx, int bpnum, objnum target, objnum self,
|
|
uint ofs, char *bpname, int toggle, char *cond,
|
|
int *did_set) { return 0; }
|
|
|
|
int dbgisbp(dbgcxdef *ctx, objnum target, objnum self, uint ofs, int *bpnum) { return 0; }
|
|
|
|
int dbgisbpena(dbgcxdef *ctx, int bpnum) { return 0; }
|
|
|
|
int dbgbpdel(dbgcxdef *ctx, int bpnum) { return 0; }
|
|
|
|
int dbgbpdis(dbgcxdef *ctx, int bpnum, int disable) { return 0; }
|
|
|
|
int dbgbpsetcond(dbgcxdef *ctx, int bpnum, char *cond) { return 0; }
|
|
|
|
void dbgbplist(dbgcxdef *ctx, void(*dispfn)(void *ctx, const char *str, int len), void *dispctx) {}
|
|
|
|
void dbgbpenum(dbgcxdef *ctx, void(*cbfunc)(void *cbctx, int bpnum, const char *desc,
|
|
const char *cond, int disabled), void *cbctx) {}
|
|
|
|
void dbgbpeach(dbgcxdef *ctx, void(*fn)(void *, int, uchar *, uint), void *fnctx) {}
|
|
|
|
int dbgbpgetinfo(dbgcxdef *ctx, int bpnum, char *descbuf, size_t descbuflen,
|
|
char *condbuf, size_t condbuflen) { return 0; }
|
|
|
|
int dbgeval(dbgcxdef *ctx, char *expr,
|
|
void(*dispfn)(void *dispctx, const char *str, int strl),
|
|
void *dispctx, int level, int showtype) { return 0; }
|
|
|
|
int dbgevalext(dbgcxdef *ctx, char *expr,
|
|
void(*dispfn)(void *dispctx, const char *str, int strl),
|
|
void *dispctx, int level, int showtype, dattyp *dat,
|
|
void(*aggcb)(void *aggctx, const char *subname,
|
|
int subnamelen, const char *relationship),
|
|
void *aggctx, int speculative) { return 0 ;}
|
|
|
|
void dbgenumlcl(dbgcxdef *ctx, int level,
|
|
void(*func)(void *ctx, const char *lclnam, size_t lclnamlen),
|
|
void *cbctx) {}
|
|
|
|
int dbgcompile(dbgcxdef *ctx, char *expr, dbgfdef *fr, objnum *objn,
|
|
int speculative) { return 0; }
|
|
|
|
void dbgwhere(dbgcxdef *ctx, char *buf) {}
|
|
|
|
int dbgwxset(dbgcxdef *ctx, char *expr, int *wxnum, int level) { return 0; }
|
|
|
|
int dbgwxdel(dbgcxdef *ctx, int wxnum) { return 0; }
|
|
|
|
void dbgwxupd(dbgcxdef *ctx,
|
|
void(*dispfn)(void *dispctx, const char *txt, int len),
|
|
void *dispctx) {}
|
|
|
|
void dbgswitch(struct lindef **linp, struct lindef *newlin) {}
|
|
|
|
void dbguini(dbgcxdef *ctx, const char *game_filename) {}
|
|
|
|
void dbguini2(dbgcxdef *ctx) {}
|
|
|
|
int dbgu_err_resume(dbgcxdef *ctx) { return 0; }
|
|
|
|
int dbgu_find_src(const char *origname, int origlen,
|
|
char *fullname, size_t full_len, int must_find_file) { return 0; }
|
|
|
|
void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs) {}
|
|
|
|
void dbguquitting(dbgcxdef *ctx) {}
|
|
|
|
void dbguterm(dbgcxdef *ctx) {}
|
|
|
|
void dbguerr(dbgcxdef *ctx, int errnum, char *msg) {}
|
|
|
|
void trchid(void) {}
|
|
void trcsho(void) {}
|
|
|
|
struct runsdef *dbgfrfind(dbgcxdef *ctx, objnum frobj, uint frofs)
|
|
{
|
|
VARUSED(frobj);
|
|
VARUSED(frofs);
|
|
errsig(ctx->dbgcxerr, ERR_INACTFR);
|
|
return nullptr;
|
|
}
|
|
|
|
void dbgss(struct dbgcxdef *ctx, uint ofs, int instr, int err,
|
|
uchar *noreg *p)
|
|
{
|
|
VARUSED(ctx);
|
|
VARUSED(ofs);
|
|
VARUSED(instr);
|
|
VARUSED(err);
|
|
VARUSED(p);
|
|
}
|
|
|
|
int dbgstart(struct dbgcxdef *ctx)
|
|
{
|
|
VARUSED(ctx);
|
|
return TRUE;
|
|
}
|
|
|
|
/* printf-style formatting */
|
|
static void trdptf(const char *fmt, ...)
|
|
{
|
|
char buf[256];
|
|
va_list va;
|
|
|
|
/* format the string */
|
|
va_start(va, fmt);
|
|
Common::vsprintf_s(buf, fmt, va);
|
|
va_end(va);
|
|
|
|
/* print the formatted buffer */
|
|
os_printz(buf);
|
|
}
|
|
|
|
|
|
/*
|
|
* display a range of usage messages
|
|
*/
|
|
static void trdusage_show_range(errcxdef *ec, int msg_first, int msg_last)
|
|
{
|
|
int i;
|
|
char buf[128];
|
|
|
|
for (i = msg_first ; i <= msg_last ; ++i)
|
|
{
|
|
errmsg(ec, buf, (uint)sizeof(buf), i);
|
|
trdptf("%s\n", buf);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* display a range of usage messages, then throw the usage error
|
|
*/
|
|
static void trdusage_range(errcxdef *ec, int msg_first, int msg_last)
|
|
{
|
|
/* show the message range */
|
|
trdusage_show_range(ec, msg_first, msg_last);
|
|
|
|
/* signal the usage error */
|
|
errsig(ec, ERR_USAGE);
|
|
}
|
|
|
|
/*
|
|
* display general run-time usage information
|
|
*/
|
|
static void trdusage(errcxdef *ec)
|
|
{
|
|
int first;
|
|
|
|
/*
|
|
* if we have an app display name, display it instead of the
|
|
* hard-coded text in the message identifying the app
|
|
*/
|
|
first = ERR_TRUS1;
|
|
if (ec->errcxappctx != nullptr && ec->errcxappctx->usage_app_name != nullptr)
|
|
{
|
|
char buf[128];
|
|
char buf2[128];
|
|
erradef argv[1];
|
|
|
|
/* get the parameterized usage message */
|
|
errmsg(ec, buf, (uint)sizeof(buf), ERR_TRUSPARM);
|
|
|
|
/* format in the application name */
|
|
argv[0].errastr = ec->errcxappctx->usage_app_name;
|
|
errfmt(buf2, (int)sizeof(buf2), buf, 1, argv);
|
|
|
|
/* display it */
|
|
trdptf("%s\n", buf2);
|
|
|
|
/* start at the next message */
|
|
++first;
|
|
}
|
|
|
|
/* display the main option list messages */
|
|
trdusage_show_range(ec, first, ERR_TRUSL);
|
|
|
|
/* display the OS-specific option messages, if any */
|
|
trdusage_show_range(ec, ERR_TRUS_OS_FIRST, ERR_TRUS_OS_LAST);
|
|
|
|
/* display the usage footer messages */
|
|
trdusage_range(ec, ERR_TRUSFT1, ERR_TRUSFTL);
|
|
}
|
|
|
|
/*
|
|
* display -s suboptions
|
|
*/
|
|
static void trdusage_s(errcxdef *ec)
|
|
{
|
|
trdusage_range(ec, ERR_TRSUS1, ERR_TRSUSL);
|
|
}
|
|
|
|
|
|
static void trdmain1(errcxdef *ec, int argc, char *argv[],
|
|
appctxdef *appctx, const char *save_ext)
|
|
{
|
|
osfildef *swapfp = (osfildef *)nullptr;
|
|
runcxdef runctx;
|
|
bifcxdef bifctx;
|
|
voccxdef vocctx;
|
|
void (*bif[100])(struct bifcxdef *, int);
|
|
mcmcxdef *mctx = nullptr;
|
|
mcmcx1def *globalctx = nullptr;
|
|
dbgcxdef dbg;
|
|
supcxdef supctx;
|
|
char *swapname = nullptr;
|
|
char swapbuf[OSFNMAX];
|
|
char **argp;
|
|
char *arg;
|
|
char *infile;
|
|
char infile_abs[OSFNMAX]; /* fully-qualified input file name */
|
|
char infile_path[OSFNMAX]; /* absolute path to input file */
|
|
const char *exefile; /* try with executable file if no infile */
|
|
ulong swapsize = 0xffffffffL; /* allow unlimited swap space */
|
|
int swapena = OS_DEFAULT_SWAP_ENABLED; /* swapping enabled? */
|
|
int i;
|
|
int pause = FALSE; /* pause after finishing game */
|
|
fiolcxdef fiolctx;
|
|
noreg int loadopen = FALSE;
|
|
char inbuf[OSFNMAX];
|
|
ulong cachelimit = 0xffffffff;
|
|
ushort undosiz = TRD_UNDOSIZ; /* default undo context size 16k */
|
|
objucxdef *undoptr = nullptr;
|
|
uint flags; /* flags used to write the file we're reading */
|
|
objnum preinit; /* preinit object, if we need to execute it */
|
|
uint heapsiz = TRD_HEAPSIZ;
|
|
uint stksiz = TRD_STKSIZ;
|
|
runsdef *mystack;
|
|
uchar *myheap;
|
|
extern osfildef *cmdfile; /* hacky v1 qa interface - command log fp */
|
|
extern osfildef *logfp; /* hacky v1 qa interface - output log fp */
|
|
int preload = FALSE; /* TRUE => preload all objects */
|
|
ulong totsize;
|
|
extern voccxdef *main_voc_ctx;
|
|
int safety_read, safety_write; /* file I/O safety level */
|
|
char *restore_file = nullptr; /* .SAV file to restore */
|
|
char *charmap = nullptr; /* character map file */
|
|
int charmap_none; /* explicitly do not use a character set */
|
|
int doublespace = TRUE; /* formatter double-space setting */
|
|
|
|
NOREG((&loadopen))
|
|
|
|
/* initialize the output formatter */
|
|
out_init();
|
|
|
|
/* set safety level to 2 by default - read any/write current dir only */
|
|
safety_read = safety_write = 2;
|
|
|
|
/* no -ctab- yet */
|
|
charmap_none = FALSE;
|
|
|
|
/* parse arguments */
|
|
for (i = 1, argp = argv + 1 ; i < argc ; ++argp, ++i)
|
|
{
|
|
arg = *argp;
|
|
if (*arg == '-')
|
|
{
|
|
switch(*(arg+1))
|
|
{
|
|
case 'c':
|
|
if (!strcmp(arg+1, "ctab"))
|
|
{
|
|
/* get the character mapping table */
|
|
charmap = cmdarg(ec, &argp, &i, argc, 4, trdusage);
|
|
}
|
|
else if (!strcmp(arg+1, "ctab-"))
|
|
{
|
|
/* use the default mapping */
|
|
charmap_none = TRUE;
|
|
}
|
|
else
|
|
trdusage(ec);
|
|
break;
|
|
|
|
case 'r':
|
|
/* restore a game */
|
|
restore_file = cmdarg(ec, &argp, &i, argc, 1, trdusage);
|
|
break;
|
|
|
|
case 'i':
|
|
qasopn(cmdarg(ec, &argp, &i, argc, 1, trdusage), TRUE);
|
|
break;
|
|
|
|
case 'o':
|
|
cmdfile = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage),
|
|
OSFTCMD);
|
|
break;
|
|
|
|
case 'l':
|
|
logfp = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage),
|
|
OSFTCMD);
|
|
break;
|
|
|
|
case 'p':
|
|
if (!scumm_stricmp(arg, "-plain"))
|
|
{
|
|
os_plain();
|
|
break;
|
|
}
|
|
pause = cmdtog(ec, pause, arg, 1, trdusage);
|
|
break;
|
|
|
|
case 'd':
|
|
if (!scumm_strnicmp(arg, "-double", 7))
|
|
{
|
|
/* get the argument value */
|
|
doublespace = cmdtog(ec, doublespace, arg, 6, trdusage);
|
|
|
|
/* set the double-space mode in the formatter */
|
|
out_set_doublespace(doublespace);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
{
|
|
char *p;
|
|
|
|
/* get the option */
|
|
p = cmdarg(ec, &argp, &i, argc, 1, trdusage);
|
|
|
|
/* if they're asking for help, display detailed usage */
|
|
if (*p == '?')
|
|
trdusage_s(ec);
|
|
|
|
/* get the safety level from the argument */
|
|
safety_read = *p - '0';
|
|
safety_write = (*(p+1) != '\0' ? *(p+1) - '0' :
|
|
safety_read);
|
|
|
|
/* range-check the values */
|
|
if (safety_read < 0 || safety_read > 4
|
|
|| safety_write < 0 || safety_write > 4)
|
|
trdusage_s(ec);
|
|
|
|
/* tell the host system about the setting */
|
|
if (appctx != nullptr && appctx->set_io_safety_level != nullptr)
|
|
(*appctx->set_io_safety_level)
|
|
(appctx->io_safety_level_ctx,
|
|
safety_read, safety_write);
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
switch(*(arg + 2))
|
|
{
|
|
case 's':
|
|
stksiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage));
|
|
break;
|
|
|
|
case 'h':
|
|
heapsiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage));
|
|
break;
|
|
|
|
default:
|
|
cachelimit = atol(cmdarg(ec, &argp, &i, argc, 1,
|
|
trdusage));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
/* swap file options: -tf file, -ts size, -t- (no swap) */
|
|
switch(*(arg+2))
|
|
{
|
|
case 'f':
|
|
swapname = cmdarg(ec, &argp, &i, argc, 2, trdusage);
|
|
break;
|
|
|
|
case 's':
|
|
swapsize = atol(cmdarg(ec, &argp, &i, argc, 2, trdusage));
|
|
break;
|
|
|
|
case 'p':
|
|
preload = cmdtog(ec, preload, arg, 2, trdusage);
|
|
break;
|
|
|
|
default:
|
|
swapena = cmdtog(ec, swapena, arg, 1, trdusage);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
undosiz = atoi(cmdarg(ec, &argp, &i, argc, 1, trdusage));
|
|
break;
|
|
|
|
default:
|
|
trdusage(ec);
|
|
}
|
|
}
|
|
else break;
|
|
}
|
|
|
|
/* presume we won't take the .gam from the application executable */
|
|
exefile = nullptr;
|
|
|
|
/* get input name argument, and make sure it's the last argument */
|
|
if (i == argc)
|
|
{
|
|
osfildef *fp;
|
|
ulong curpos;
|
|
ulong endpos;
|
|
int use_exe;
|
|
|
|
/*
|
|
* There's no input name argument, so we need to find the game
|
|
* to play some other way. First, check to see if we have a
|
|
* game to restore, and if so whether it has the .GAM name
|
|
* encoded into it. Next, look to see if there's a game
|
|
* attached to the executable file; if so, use it. If not, see
|
|
* if the host system wants to provide a name through its
|
|
* callback.
|
|
*/
|
|
|
|
/* presume we won't find a game attached to the executable file */
|
|
infile = nullptr;
|
|
use_exe = FALSE;
|
|
|
|
/*
|
|
* see if we have a saved game to restore, and it specifies the
|
|
* GAM file that saved it
|
|
*/
|
|
if (restore_file != nullptr)
|
|
{
|
|
/* try getting the game name from the restore file */
|
|
if (fiorso_getgame(restore_file, inbuf, sizeof(inbuf)))
|
|
{
|
|
/* got it - use this file */
|
|
infile = inbuf;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* it that didn't work, try to read from os-dependent part of
|
|
* program being executed
|
|
*/
|
|
if (infile == nullptr)
|
|
{
|
|
/* try opening the executable file */
|
|
exefile = (argv && argv[0] ? argv[0] : "TRX");
|
|
fp = os_exeseek(exefile, "TGAM");
|
|
if (fp != nullptr)
|
|
{
|
|
/* see if there's a game file attached to the executable */
|
|
curpos = osfpos(fp);
|
|
osfseek(fp, 0L, OSFSK_END);
|
|
endpos = osfpos(fp);
|
|
osfcls(fp);
|
|
|
|
/* if we found it, use it */
|
|
if (endpos != curpos)
|
|
use_exe = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if we didn't find a game in the executable, try the host
|
|
* system callback
|
|
*/
|
|
if (infile == nullptr && !use_exe)
|
|
{
|
|
/*
|
|
* ask the host system callback what to do - if we don't
|
|
* have a host system callback, or the callback
|
|
*/
|
|
if (appctx != nullptr && appctx->get_game_name != nullptr)
|
|
{
|
|
/* call the host system callback */
|
|
if ((*appctx->get_game_name)(appctx->get_game_name_ctx,
|
|
inbuf, sizeof(inbuf)))
|
|
{
|
|
/* the host system provided a name - use it */
|
|
infile = inbuf;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* the host didn't provide a name - simply display a
|
|
* message indicating that no game file has been
|
|
* chosen, and return
|
|
*/
|
|
trdptf("\n");
|
|
trdptf("(No game has been selected.)\n");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* we've run out of ways to get a filename - give the
|
|
* user the usage message and quit
|
|
*/
|
|
trdusage(ec);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
infile = *argp;
|
|
if (i + 1 != argc)
|
|
trdusage(ec);
|
|
|
|
#ifndef OS_HATES_EXTENSIONS
|
|
/*
|
|
* If original name exists, use it; otherwise, try adding .GAM.
|
|
* Note that this code is ifdef'd so that platforms that don't
|
|
* use filename extensions in the manner conventional for DOS
|
|
* and Unix won't use this code.
|
|
*/
|
|
if (osfacc(infile))
|
|
{
|
|
Common::strcpy_s(inbuf, infile);
|
|
os_defext(inbuf, "gam");
|
|
infile = inbuf;
|
|
}
|
|
#endif /* !defined(OS_HATES_EXTENSIONS) */
|
|
}
|
|
|
|
/* open up the swap file */
|
|
if (swapena && swapsize)
|
|
{
|
|
swapfp = os_create_tempfile(swapname, swapbuf);
|
|
if (swapname == nullptr) swapname = swapbuf;
|
|
if (swapfp == nullptr) errsig(ec, ERR_OPSWAP);
|
|
}
|
|
|
|
/* load the character map */
|
|
if (charmap_none)
|
|
cmap_override();
|
|
else if (cmap_load(charmap))
|
|
errsig(ec, ERR_INVCMAP);
|
|
|
|
ERRBEGIN(ec)
|
|
|
|
/* initialize cache manager context */
|
|
globalctx = mcmini(cachelimit, 128, swapsize, swapfp, swapname, ec);
|
|
mctx = mcmcini(globalctx, 128, fioldobj, &fiolctx,
|
|
objrevert, (void *)nullptr);
|
|
mctx->mcmcxrvc = mctx;
|
|
|
|
/* set up an undo context */
|
|
if (undosiz)
|
|
undoptr = objuini(mctx, undosiz, vocdundo, vocdusz, &vocctx);
|
|
else
|
|
undoptr = (objucxdef *)nullptr;
|
|
|
|
/* set up vocabulary context */
|
|
vocini(&vocctx, ec, mctx, &runctx, undoptr, 100, 100, 200);
|
|
|
|
/*
|
|
* save a pointer to the voc context globally, so that certain
|
|
* external routines (such as Unix-style signal handlers) can reach
|
|
* it
|
|
*/
|
|
main_voc_ctx = &vocctx;
|
|
|
|
/* allocate stack and heap */
|
|
totsize = (ulong)stksiz * (ulong)sizeof(runsdef);
|
|
if (totsize != (size_t)totsize)
|
|
errsig1(ec, ERR_STKSIZE, ERRTINT, (uint)(65535/sizeof(runsdef)));
|
|
mystack = (runsdef *)mchalo(ec, (size_t)totsize, "runtime stack");
|
|
myheap = mchalo(ec, heapsiz, "runtime heap");
|
|
|
|
/* get the absolute path for the input file */
|
|
if (infile != nullptr)
|
|
os_get_abs_filename(infile_abs, sizeof(infile_abs), infile);
|
|
else if (exefile != nullptr)
|
|
os_get_abs_filename(infile_abs, sizeof(infile_abs), exefile);
|
|
else
|
|
infile_abs[0] = '\0';
|
|
os_get_path_name(infile_path, sizeof(infile_path), infile_abs);
|
|
|
|
/* set up execution context */
|
|
runctx.runcxerr = ec;
|
|
runctx.runcxmem = mctx;
|
|
runctx.runcxstk = mystack;
|
|
runctx.runcxstop = &mystack[stksiz];
|
|
runctx.runcxsp = mystack;
|
|
runctx.runcxbp = mystack;
|
|
runctx.runcxheap = myheap;
|
|
runctx.runcxhp = myheap;
|
|
runctx.runcxhtop = &myheap[heapsiz];
|
|
runctx.runcxundo = undoptr;
|
|
runctx.runcxbcx = &bifctx;
|
|
runctx.runcxbi = bif;
|
|
runctx.runcxtio = (tiocxdef *)nullptr;
|
|
runctx.runcxdbg = &dbg;
|
|
runctx.runcxvoc = &vocctx;
|
|
runctx.runcxdmd = supcont;
|
|
runctx.runcxdmc = &supctx;
|
|
runctx.runcxext = nullptr;
|
|
runctx.runcxgamename = infile;
|
|
runctx.runcxgamepath = infile_path;
|
|
|
|
/* set up setup context */
|
|
supctx.supcxerr = ec;
|
|
supctx.supcxmem = mctx;
|
|
supctx.supcxtab = (tokthdef *)nullptr;
|
|
supctx.supcxbuf = (uchar *)nullptr;
|
|
supctx.supcxlen = 0;
|
|
supctx.supcxvoc = &vocctx;
|
|
supctx.supcxrun = &runctx;
|
|
|
|
/* set up debug context */
|
|
dbg.dbgcxtio = (tiocxdef *)nullptr;
|
|
dbg.dbgcxmem = mctx;
|
|
dbg.dbgcxerr = ec;
|
|
dbg.dbgcxtab = (tokthdef *)nullptr;
|
|
dbg.dbgcxfcn = 0;
|
|
dbg.dbgcxdep = 0;
|
|
dbg.dbgcxflg = 0;
|
|
dbg.dbgcxlin = (lindef *)nullptr; /* no line sources yet */
|
|
|
|
/* set up built-in function context */
|
|
bifctx.bifcxerr = ec;
|
|
bifctx.bifcxrun = &runctx;
|
|
bifctx.bifcxtio = (tiocxdef *)nullptr;
|
|
bifctx.bifcxrnd = 0;
|
|
bifctx.bifcxrndset = FALSE;
|
|
bifctx.bifcxappctx = appctx;
|
|
bifctx.bifcxsafetyr = safety_read;
|
|
bifctx.bifcxsafetyw = safety_write;
|
|
bifctx.bifcxsavext = save_ext;
|
|
|
|
/* initialize the regular expression parser context */
|
|
re_init(&bifctx.bifcxregex, ec);
|
|
|
|
/* add the built-in functions, keywords, etc */
|
|
supbif(&supctx, bif, (int)(sizeof(bif)/sizeof(bif[0])));
|
|
|
|
/* set up status line hack */
|
|
runistat(&vocctx, &runctx, (tiocxdef *)nullptr);
|
|
|
|
/* turn on the "busy" cursor before loading */
|
|
os_csr_busy(TRUE);
|
|
|
|
/* read the game from the binary file */
|
|
fiord(mctx, &vocctx, (struct tokcxdef *)nullptr,
|
|
infile, exefile, &fiolctx, &preinit, &flags,
|
|
(struct tokpdef *)nullptr, (uchar **)nullptr, (uint *)nullptr, (uint *)nullptr,
|
|
(preload ? 2 : 0), appctx, argv[0]);
|
|
loadopen = TRUE;
|
|
|
|
/* turn off the "busy" cursor */
|
|
os_csr_busy(FALSE);
|
|
|
|
/* play the game */
|
|
plygo(&runctx, &vocctx, (tiocxdef *)nullptr, preinit, restore_file);
|
|
|
|
/* close load file */
|
|
fiorcls(&fiolctx);
|
|
|
|
if (pause)
|
|
{
|
|
trdptf("[press a key to exit]");
|
|
os_waitc();
|
|
trdptf("\n");
|
|
}
|
|
|
|
/* close and delete swapfile, if one was opened */
|
|
trd_close_swapfile(&runctx);
|
|
|
|
/* make sure the script file is closed, if we have one */
|
|
qasclose();
|
|
|
|
ERRCLEAN(ec)
|
|
/* close and delete swapfile, if one was opened */
|
|
trd_close_swapfile(&runctx);
|
|
|
|
/* close the load file if one was opened */
|
|
if (loadopen)
|
|
fiorcls(&fiolctx);
|
|
|
|
/* vocctx is going out of scope - forget the global reference to it */
|
|
main_voc_ctx = nullptr;
|
|
|
|
/* delete the voc context */
|
|
vocterm(&vocctx);
|
|
|
|
/* delete the undo context */
|
|
if (undoptr != nullptr)
|
|
objuterm(undoptr);
|
|
|
|
/* release the object cache structures */
|
|
if (mctx != nullptr)
|
|
mcmcterm(mctx);
|
|
if (globalctx != nullptr)
|
|
mcmterm(globalctx);
|
|
ERRENDCLN(ec)
|
|
|
|
/* vocctx is going out of scope - forget the global reference to it */
|
|
main_voc_ctx = nullptr;
|
|
|
|
/* delete the voc contxt */
|
|
vocterm(&vocctx);
|
|
|
|
/* delete the undo context */
|
|
if (undoptr != nullptr)
|
|
objuterm(undoptr);
|
|
|
|
/* release the object cache structures */
|
|
mcmcterm(mctx);
|
|
mcmterm(globalctx);
|
|
}
|
|
|
|
/*
|
|
* If the OS configuration so desires, use a less technical format for
|
|
* run-time error messages by leaving out the numeric error code. Note
|
|
* that we'll only do this if the error messages are linked directly
|
|
* into the run-time, since we need the numeric code as a last resort
|
|
* when the error message may not be present.
|
|
*/
|
|
#ifdef OS_SKIP_ERROR_CODES
|
|
# ifdef ERR_LINK_MESSAGES
|
|
# define TRDLOGERR_PREFIX "\n[An error has occurred within TADS: "
|
|
# endif
|
|
#endif
|
|
|
|
/*
|
|
* If we didn't define a different error prefix format, use the default
|
|
* format with the numeric error code.
|
|
*/
|
|
#ifndef TRDLOGERR_PREFIX
|
|
# define TRDLOGERR_PREFIX "\n[%s-%d: "
|
|
#endif
|
|
|
|
/* log an error */
|
|
static void trdlogerr(void *ctx0, const char *fac, int err, int argc, erradef *argv) {
|
|
errcxdef *ctx = (errcxdef *)ctx0;
|
|
char buf[256];
|
|
char msg[256];
|
|
|
|
/* display the prefix message to the console and log file */
|
|
Common::sprintf_s(buf, TRDLOGERR_PREFIX, fac, err);
|
|
trdptf("%s", buf);
|
|
out_logfile_print(buf, FALSE);
|
|
|
|
/* display the error message text to the console and log file */
|
|
errmsg(ctx, msg, (uint)sizeof(msg), err);
|
|
errfmt(buf, (int)sizeof(buf), msg, argc, argv);
|
|
trdptf("%s]\n", buf);
|
|
out_logfile_print(buf, FALSE);
|
|
out_logfile_print("]", TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* close and delete the swap file
|
|
*/
|
|
void trd_close_swapfile(runcxdef *runctx)
|
|
{
|
|
extern voccxdef *main_voc_ctx;
|
|
mcmcxdef *mctx;
|
|
mcmcx1def *globalctx;
|
|
mcscxdef *mcsctx;
|
|
|
|
/* if no run context was supplied, find it from the main voc context */
|
|
if (runctx == nullptr)
|
|
{
|
|
/* if there is no main voc context, we're out of luck */
|
|
if (main_voc_ctx == nullptr)
|
|
return;
|
|
|
|
/* get the run context */
|
|
runctx = main_voc_ctx->voccxrun;
|
|
}
|
|
|
|
/* get the other relevant contexts */
|
|
mctx = runctx->runcxmem;
|
|
globalctx = mctx->mcmcxgl;
|
|
mcsctx = &globalctx->mcmcxswc;
|
|
|
|
/* if we have a swap file open, close it */
|
|
if (mcsctx->mcscxfp != nullptr)
|
|
{
|
|
/* close the file */
|
|
osfcls(mcsctx->mcscxfp);
|
|
|
|
/* forget about the file, so we don't try to close it again */
|
|
mcsctx->mcscxfp = (osfildef *)nullptr;
|
|
}
|
|
|
|
/* if we have a filename, delete the file */
|
|
if (mcsctx->mcscxfname != nullptr)
|
|
{
|
|
/* delete the file */
|
|
osfdel_temp(mcsctx->mcscxfname);
|
|
|
|
/* forget the filename, so we don't try to delete the file again */
|
|
mchfre(mcsctx->mcscxfname);
|
|
mcsctx->mcscxfname = nullptr;
|
|
}
|
|
}
|
|
|
|
/* main - called by os main after setting up arguments */
|
|
int trdmain(int argc, char *argv[], appctxdef *appctx, const char *save_ext)
|
|
{
|
|
errcxdef errctx;
|
|
int err;
|
|
osfildef *fp;
|
|
|
|
errctx.errcxlog = trdlogerr;
|
|
errctx.errcxlgc = &errctx;
|
|
errctx.errcxfp = (osfildef *)nullptr;
|
|
errctx.errcxofs = 0;
|
|
errctx.errcxappctx = appctx;
|
|
fp = oserrop(argv[0]);
|
|
errini(&errctx, fp);
|
|
|
|
/* copyright-date-string */
|
|
#ifndef NO_T2_COPYRIGHT_NOTICE
|
|
trdptf("%s - A %s TADS %s Interpreter.\n",
|
|
G_tads_oem_app_name, G_tads_oem_display_mode,
|
|
TADS_RUNTIME_VERSION);
|
|
trdptf("%sopyright (c) 1993, 2012 by Michael J. Roberts.\n",
|
|
G_tads_oem_copyright_prefix ? "TADS c" : "C");
|
|
trdptf("%s\n", G_tads_oem_author);
|
|
#endif
|
|
|
|
ERRBEGIN(&errctx)
|
|
trdmain1(&errctx, argc, argv, appctx, save_ext);
|
|
ERRCATCH(&errctx, err)
|
|
/*
|
|
* log the error, unless it's usage (in which case we logged it
|
|
* already) or we're simply quitting the game
|
|
*/
|
|
if (err != ERR_USAGE && err != ERR_RUNQUIT)
|
|
errclog(&errctx);
|
|
|
|
/* close the error file */
|
|
if (errctx.errcxfp != nullptr)
|
|
osfcls(errctx.errcxfp);
|
|
|
|
/* pause before exiting if the OS desires it */
|
|
os_expause();
|
|
|
|
/* return failure unless we're simply quitting the game */
|
|
return (err == ERR_RUNQUIT ? OSEXSUCC : OSEXFAIL);
|
|
ERREND(&errctx)
|
|
|
|
/* close the error file if we opened it */
|
|
if (errctx.errcxfp != nullptr)
|
|
osfcls(errctx.errcxfp);
|
|
|
|
/* successful completion */
|
|
return(OSEXSUCC);
|
|
}
|
|
|
|
} // End of namespace TADS2
|
|
} // End of namespace TADS
|
|
} // End of namespace Glk
|