Files
scummvm-cursorfix/engines/glk/tads/tads2/runtime_driver.cpp
2026-02-02 04:50:13 +01:00

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