diff options
Diffstat (limited to 'engines/glk/tads/tads2/tads2.cpp')
-rw-r--r-- | engines/glk/tads/tads2/tads2.cpp | 523 |
1 files changed, 517 insertions, 6 deletions
diff --git a/engines/glk/tads/tads2/tads2.cpp b/engines/glk/tads/tads2/tads2.cpp index a772b44aed..b391857613 100644 --- a/engines/glk/tads/tads2/tads2.cpp +++ b/engines/glk/tads/tads2/tads2.cpp @@ -21,13 +21,18 @@ */ #include "glk/tads/tads2/tads2.h" +#include "glk/tads/tads2/built_in.h" +#include "glk/tads/tads2/debug.h" +#include "glk/tads/tads2/file_io.h" +#include "glk/tads/tads2/post_compilation.h" +#include "glk/tads/tads2/run.h" +#include "glk/tads/tads2/vocabulary.h" namespace Glk { namespace TADS { namespace TADS2 { -TADS2::TADS2(OSystem *syst, const GlkGameDescription &gameDesc) : OS(syst, gameDesc) { - cmap_init_default(); +TADS2::TADS2(OSystem *syst, const GlkGameDescription &gameDesc) : TADS(syst, gameDesc) { } void TADS2::runGame() { @@ -35,7 +40,7 @@ void TADS2::runGame() { errctx.errcxlgc = &errctx; errctx.errcxfp = nullptr; errctx.errcxofs = 0; - errctx.errcxappctx = this; + errctx.errcxappctx = nullptr; /* copyright-date-string */ #ifdef T2_COPYRIGHT_NOTICE @@ -48,13 +53,519 @@ void TADS2::runGame() { #endif trdmain1(&errctx); - - // pause before exiting if the OS desires it - os_expause(); } void TADS2::trdmain1(errcxdef *errctx) { + osfildef *swapfp = (osfildef *)0; + runcxdef runctx; + bifcxdef bifctx; + voccxdef vocctx; + void(*bif[100])(bifcxdef *, int); + mcmcxdef *mctx = 0; + mcmcx1def *globalctx = 0; + dbgcxdef dbg; + supcxdef supctx; + char *swapname = 0; + 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 */ + 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 = 0; + 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 = 0; /* .SAV file to restore */ + char *charmap = 0; /* character map file */ + int charmap_none; /* explicitly do not use a character set */ + int doublespace = TRUE; /* formatter double-space setting */ +#ifdef TODO + 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 (!stricmp(arg, "-plain")) + { + os_plain(); + break; + } + pause = cmdtog(ec, pause, arg, 1, trdusage); + break; + + case 'd': + if (!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 != 0 && appctx->set_io_safety_level != 0) + (*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 = 0; + + /* 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 = 0; + use_exe = FALSE; + + /* + * see if we have a saved game to restore, and it specifies the + * GAM file that saved it + */ + if (restore_file != 0) + { + /* 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 == 0) + { + /* try opening the executable file */ + exefile = (argv && argv[0] ? argv[0] : "TRX"); + fp = os_exeseek(exefile, "TGAM"); + if (fp != 0) + { + /* 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 == 0 && !use_exe) + { + /* + * ask the host system callback what to do - if we don't + * have a host system callback, or the callback + */ + if (appctx != 0 && appctx->get_game_name != 0) + { + /* 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)) + { + strcpy(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 == 0) swapname = swapbuf; + if (swapfp == 0) 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 *)0); + mctx->mcmcxrvc = mctx; + + /* set up an undo context */ + if (undosiz) + undoptr = objuini(mctx, undosiz, vocdundo, vocdusz, &vocctx); + else + undoptr = (objucxdef *)0; + + /* 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 != 0) + os_get_abs_filename(infile_abs, sizeof(infile_abs), infile); + else if (exefile != 0) + 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 *)0; + runctx.runcxdbg = &dbg; + runctx.runcxvoc = &vocctx; + runctx.runcxdmd = supcont; + runctx.runcxdmc = &supctx; + runctx.runcxext = 0; + runctx.runcxgamename = infile; + runctx.runcxgamepath = infile_path; + + /* set up setup context */ + supctx.supcxerr = ec; + supctx.supcxmem = mctx; + supctx.supcxtab = (tokthdef *)0; + supctx.supcxbuf = (uchar *)0; + supctx.supcxlen = 0; + supctx.supcxvoc = &vocctx; + supctx.supcxrun = &runctx; + + /* set up debug context */ + dbg.dbgcxtio = (tiocxdef *)0; + dbg.dbgcxmem = mctx; + dbg.dbgcxerr = ec; + dbg.dbgcxtab = (tokthdef *)0; + dbg.dbgcxfcn = 0; + dbg.dbgcxdep = 0; + dbg.dbgcxflg = 0; + dbg.dbgcxlin = (lindef *)0; /* no line sources yet */ + + /* set up built-in function context */ + CLRSTRUCT(bifctx); + bifctx.bifcxerr = ec; + bifctx.bifcxrun = &runctx; + bifctx.bifcxtio = (tiocxdef *)0; + 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 *)0); + + /* turn on the "busy" cursor before loading */ + os_csr_busy(TRUE); + + /* read the game from the binary file */ + fiord(mctx, &vocctx, (struct tokcxdef *)0, + infile, exefile, &fiolctx, &preinit, &flags, + (struct tokpdef *)0, (uchar **)0, (uint *)0, (uint *)0, + (preload ? 2 : 0), appctx, argv[0]); + loadopen = TRUE; + + /* turn off the "busy" cursor */ + os_csr_busy(FALSE); + + /* play the game */ + plygo(&runctx, &vocctx, (tiocxdef *)0, 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 = 0; + + /* delete the voc context */ + vocterm(&vocctx); + + /* delete the undo context */ + if (undoptr != 0) + objuterm(undoptr); + + /* release the object cache structures */ + if (mctx != 0) + mcmcterm(mctx); + if (globalctx != 0) + mcmterm(globalctx); + ERRENDCLN(ec) + + /* vocctx is going out of scope - forget the global reference to it */ + main_voc_ctx = 0; + + /* delete the voc contxt */ + vocterm(&vocctx); + + /* delete the undo context */ + if (undoptr != 0) + objuterm(undoptr); + + /* release the object cache structures */ + mcmcterm(mctx); + mcmterm(globalctx); +#endif } void TADS2::trdptf(const char *fmt, ...) { |