1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "glk/tads/tads2/built_in.h"
24 #include "glk/tads/tads2/character_map.h"
25 #include "glk/tads/tads2/command_line.h"
26 #include "glk/tads/tads2/error.h"
27 #include "glk/tads/tads2/file_io.h"
28 #include "glk/tads/tads2/memory_cache_heap.h"
29 #include "glk/tads/tads2/os.h"
30 #include "glk/tads/tads2/play.h"
31 #include "glk/tads/tads2/post_compilation.h"
32 #include "glk/tads/tads2/run.h"
33 #include "glk/tads/tads2/runtime_app.h"
34 #include "glk/tads/tads2/tokenizer.h"
35 #include "glk/tads/tads2/tads2.h"
36 #include "glk/tads/tads2/vocabulary.h"
37 #include "glk/tads/os_frob_tads.h"
38
39 namespace Glk {
40 namespace TADS {
41 namespace TADS2 {
42
43 /* dummy setup function */
supgnam(char * buf,tokthdef * tab,objnum sc)44 void supgnam(char *buf, tokthdef *tab, objnum sc)
45 {
46 strcpy(buf, "???");
47 }
48
49 /* dummy file read functions */
tok_read_defines(tokcxdef * tctx,osfildef * fp,errcxdef * ec)50 void tok_read_defines(tokcxdef *tctx, osfildef *fp, errcxdef *ec)
51 {
52 errsig(ec, ERR_UNKRSC);
53 }
54
55 /* dummy debugger functions */
56
dbgbpset(dbgcxdef * ctx,char * addr,int * bpnum)57 int dbgbpset(dbgcxdef *ctx, char *addr, int *bpnum) { return 0; }
58
dbgbpat(dbgcxdef * ctx,objnum objn,objnum self,uint ofs,int * bpnum,char * bpname,int toggle,char * condition,int * did_set)59 int dbgbpat(dbgcxdef *ctx, objnum objn, objnum self,
60 uint ofs, int *bpnum, char *bpname, int toggle,
61 char *condition, int *did_set) { return 0; }
62
dbgbpatid(dbgcxdef * ctx,int bpnum,objnum target,objnum self,uint ofs,char * bpname,int toggle,char * cond,int * did_set)63 int dbgbpatid(dbgcxdef *ctx, int bpnum, objnum target, objnum self,
64 uint ofs, char *bpname, int toggle, char *cond,
65 int *did_set) { return 0; }
66
dbgisbp(dbgcxdef * ctx,objnum target,objnum self,uint ofs,int * bpnum)67 int dbgisbp(dbgcxdef *ctx, objnum target, objnum self, uint ofs, int *bpnum) { return 0; }
68
dbgisbpena(dbgcxdef * ctx,int bpnum)69 int dbgisbpena(dbgcxdef *ctx, int bpnum) { return 0; }
70
dbgbpdel(dbgcxdef * ctx,int bpnum)71 int dbgbpdel(dbgcxdef *ctx, int bpnum) { return 0; }
72
dbgbpdis(dbgcxdef * ctx,int bpnum,int disable)73 int dbgbpdis(dbgcxdef *ctx, int bpnum, int disable) { return 0; }
74
dbgbpsetcond(dbgcxdef * ctx,int bpnum,char * cond)75 int dbgbpsetcond(dbgcxdef *ctx, int bpnum, char *cond) { return 0; }
76
dbgbplist(dbgcxdef * ctx,void (* dispfn)(void * ctx,const char * str,int len),void * dispctx)77 void dbgbplist(dbgcxdef *ctx, void(*dispfn)(void *ctx, const char *str, int len), void *dispctx) {}
78
dbgbpenum(dbgcxdef * ctx,void (* cbfunc)(void * cbctx,int bpnum,const char * desc,const char * cond,int disabled),void * cbctx)79 void dbgbpenum(dbgcxdef *ctx, void(*cbfunc)(void *cbctx, int bpnum, const char *desc,
80 const char *cond, int disabled), void *cbctx) {}
81
dbgbpeach(dbgcxdef * ctx,void (* fn)(void *,int,uchar *,uint),void * fnctx)82 void dbgbpeach(dbgcxdef *ctx, void(*fn)(void *, int, uchar *, uint), void *fnctx) {}
83
dbgbpgetinfo(dbgcxdef * ctx,int bpnum,char * descbuf,size_t descbuflen,char * condbuf,size_t condbuflen)84 int dbgbpgetinfo(dbgcxdef *ctx, int bpnum, char *descbuf, size_t descbuflen,
85 char *condbuf, size_t condbuflen) { return 0; }
86
dbgeval(dbgcxdef * ctx,char * expr,void (* dispfn)(void * dispctx,const char * str,int strl),void * dispctx,int level,int showtype)87 int dbgeval(dbgcxdef *ctx, char *expr,
88 void(*dispfn)(void *dispctx, const char *str, int strl),
89 void *dispctx, int level, int showtype) { return 0; }
90
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)91 int dbgevalext(dbgcxdef *ctx, char *expr,
92 void(*dispfn)(void *dispctx, const char *str, int strl),
93 void *dispctx, int level, int showtype, dattyp *dat,
94 void(*aggcb)(void *aggctx, const char *subname,
95 int subnamelen, const char *relationship),
96 void *aggctx, int speculative) { return 0 ;}
97
dbgenumlcl(dbgcxdef * ctx,int level,void (* func)(void * ctx,const char * lclnam,size_t lclnamlen),void * cbctx)98 void dbgenumlcl(dbgcxdef *ctx, int level,
99 void(*func)(void *ctx, const char *lclnam, size_t lclnamlen),
100 void *cbctx) {}
101
dbgcompile(dbgcxdef * ctx,char * expr,dbgfdef * fr,objnum * objn,int speculative)102 int dbgcompile(dbgcxdef *ctx, char *expr, dbgfdef *fr, objnum *objn,
103 int speculative) { return 0; }
104
dbgwhere(dbgcxdef * ctx,char * buf)105 void dbgwhere(dbgcxdef *ctx, char *buf) {}
106
dbgwxset(dbgcxdef * ctx,char * expr,int * wxnum,int level)107 int dbgwxset(dbgcxdef *ctx, char *expr, int *wxnum, int level) { return 0; }
108
dbgwxdel(dbgcxdef * ctx,int wxnum)109 int dbgwxdel(dbgcxdef *ctx, int wxnum) { return 0; }
110
dbgwxupd(dbgcxdef * ctx,void (* dispfn)(void * dispctx,const char * txt,int len),void * dispctx)111 void dbgwxupd(dbgcxdef *ctx,
112 void(*dispfn)(void *dispctx, const char *txt, int len),
113 void *dispctx) {}
114
dbgswitch(struct lindef ** linp,struct lindef * newlin)115 void dbgswitch(struct lindef **linp, struct lindef *newlin) {}
116
dbguini(dbgcxdef * ctx,const char * game_filename)117 void dbguini(dbgcxdef *ctx, const char *game_filename) {}
118
dbguini2(dbgcxdef * ctx)119 void dbguini2(dbgcxdef *ctx) {}
120
dbgu_err_resume(dbgcxdef * ctx)121 int dbgu_err_resume(dbgcxdef *ctx) { return 0; }
122
dbgu_find_src(const char * origname,int origlen,char * fullname,size_t full_len,int must_find_file)123 int dbgu_find_src(const char *origname, int origlen,
124 char *fullname, size_t full_len, int must_find_file) { return 0; }
125
dbgucmd(dbgcxdef * ctx,int bphit,int err,unsigned int * exec_ofs)126 void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs) {}
127
dbguquitting(dbgcxdef * ctx)128 void dbguquitting(dbgcxdef *ctx) {}
129
dbguterm(dbgcxdef * ctx)130 void dbguterm(dbgcxdef *ctx) {}
131
dbguerr(dbgcxdef * ctx,int errnum,char * msg)132 void dbguerr(dbgcxdef *ctx, int errnum, char *msg) {}
133
trchid(void)134 void trchid(void) {}
trcsho(void)135 void trcsho(void) {}
136
dbgfrfind(dbgcxdef * ctx,objnum frobj,uint frofs)137 struct runsdef *dbgfrfind(dbgcxdef *ctx, objnum frobj, uint frofs)
138 {
139 VARUSED(frobj);
140 VARUSED(frofs);
141 errsig(ctx->dbgcxerr, ERR_INACTFR);
142 return 0;
143 }
144
dbgss(struct dbgcxdef * ctx,uint ofs,int instr,int err,uchar * noreg * p)145 void dbgss(struct dbgcxdef *ctx, uint ofs, int instr, int err,
146 uchar *noreg *p)
147 {
148 VARUSED(ctx);
149 VARUSED(ofs);
150 VARUSED(instr);
151 VARUSED(err);
152 VARUSED(p);
153 }
154
dbgstart(struct dbgcxdef * ctx)155 int dbgstart(struct dbgcxdef *ctx)
156 {
157 VARUSED(ctx);
158 return TRUE;
159 }
160
161 /* printf-style formatting */
trdptf(const char * fmt,...)162 static void trdptf(const char *fmt, ...)
163 {
164 char buf[256];
165 va_list va;
166
167 /* format the string */
168 va_start(va, fmt);
169 vsprintf(buf, fmt, va);
170 va_end(va);
171
172 /* print the formatted buffer */
173 os_printz(buf);
174 }
175
176
177 /*
178 * display a range of usage messages
179 */
trdusage_show_range(errcxdef * ec,int msg_first,int msg_last)180 static void trdusage_show_range(errcxdef *ec, int msg_first, int msg_last)
181 {
182 int i;
183 char buf[128];
184
185 for (i = msg_first ; i <= msg_last ; ++i)
186 {
187 errmsg(ec, buf, (uint)sizeof(buf), i);
188 trdptf("%s\n", buf);
189 }
190 }
191
192
193 /*
194 * display a range of usage messages, then throw the usage error
195 */
trdusage_range(errcxdef * ec,int msg_first,int msg_last)196 static void trdusage_range(errcxdef *ec, int msg_first, int msg_last)
197 {
198 /* show the message range */
199 trdusage_show_range(ec, msg_first, msg_last);
200
201 /* signal the usage error */
202 errsig(ec, ERR_USAGE);
203 }
204
205 /*
206 * display general run-time usage information
207 */
trdusage(errcxdef * ec)208 static void trdusage(errcxdef *ec)
209 {
210 int first;
211
212 /*
213 * if we have an app display name, display it instead of the
214 * hard-coded text in the message identifying the app
215 */
216 first = ERR_TRUS1;
217 if (ec->errcxappctx != 0 && ec->errcxappctx->usage_app_name != 0)
218 {
219 char buf[128];
220 char buf2[128];
221 erradef argv[1];
222
223 /* get the parameterized usage message */
224 errmsg(ec, buf, (uint)sizeof(buf), ERR_TRUSPARM);
225
226 /* format in the application name */
227 argv[0].errastr = ec->errcxappctx->usage_app_name;
228 errfmt(buf2, (int)sizeof(buf2), buf, 1, argv);
229
230 /* display it */
231 trdptf("%s\n", buf2);
232
233 /* start at the next message */
234 ++first;
235 }
236
237 /* display the main option list messages */
238 trdusage_show_range(ec, first, ERR_TRUSL);
239
240 /* display the OS-specific option messages, if any */
241 trdusage_show_range(ec, ERR_TRUS_OS_FIRST, ERR_TRUS_OS_LAST);
242
243 /* display the usage footer messages */
244 trdusage_range(ec, ERR_TRUSFT1, ERR_TRUSFTL);
245 }
246
247 /*
248 * display -s suboptions
249 */
trdusage_s(errcxdef * ec)250 static void trdusage_s(errcxdef *ec)
251 {
252 trdusage_range(ec, ERR_TRSUS1, ERR_TRSUSL);
253 }
254
255
trdmain1(errcxdef * ec,int argc,char * argv[],appctxdef * appctx,const char * save_ext)256 static void trdmain1(errcxdef *ec, int argc, char *argv[],
257 appctxdef *appctx, const char *save_ext)
258 {
259 osfildef *swapfp = (osfildef *)0;
260 runcxdef runctx;
261 bifcxdef bifctx;
262 voccxdef vocctx;
263 void (*bif[100])(struct bifcxdef *, int);
264 mcmcxdef *mctx = 0;
265 mcmcx1def *globalctx = 0;
266 dbgcxdef dbg;
267 supcxdef supctx;
268 char *swapname = 0;
269 char swapbuf[OSFNMAX];
270 char **argp;
271 char *arg;
272 char *infile;
273 char infile_abs[OSFNMAX]; /* fully-qualified input file name */
274 char infile_path[OSFNMAX]; /* absolute path to input file */
275 const char *exefile; /* try with executable file if no infile */
276 ulong swapsize = 0xffffffffL; /* allow unlimited swap space */
277 int swapena = OS_DEFAULT_SWAP_ENABLED; /* swapping enabled? */
278 int i;
279 int pause = FALSE; /* pause after finishing game */
280 fiolcxdef fiolctx;
281 noreg int loadopen = FALSE;
282 char inbuf[OSFNMAX];
283 ulong cachelimit = 0xffffffff;
284 ushort undosiz = TRD_UNDOSIZ; /* default undo context size 16k */
285 objucxdef *undoptr = 0;
286 uint flags; /* flags used to write the file we're reading */
287 objnum preinit; /* preinit object, if we need to execute it */
288 uint heapsiz = TRD_HEAPSIZ;
289 uint stksiz = TRD_STKSIZ;
290 runsdef *mystack;
291 uchar *myheap;
292 extern osfildef *cmdfile; /* hacky v1 qa interface - command log fp */
293 extern osfildef *logfp; /* hacky v1 qa interface - output log fp */
294 int preload = FALSE; /* TRUE => preload all objects */
295 ulong totsize;
296 extern voccxdef *main_voc_ctx;
297 int safety_read, safety_write; /* file I/O safety level */
298 char *restore_file = 0; /* .SAV file to restore */
299 char *charmap = 0; /* character map file */
300 int charmap_none; /* explicitly do not use a character set */
301 int doublespace = TRUE; /* formatter double-space setting */
302
303 NOREG((&loadopen))
304
305 /* initialize the output formatter */
306 out_init();
307
308 /* set safety level to 2 by default - read any/write current dir only */
309 safety_read = safety_write = 2;
310
311 /* no -ctab- yet */
312 charmap_none = FALSE;
313
314 /* parse arguments */
315 for (i = 1, argp = argv + 1 ; i < argc ; ++argp, ++i)
316 {
317 arg = *argp;
318 if (*arg == '-')
319 {
320 switch(*(arg+1))
321 {
322 case 'c':
323 if (!strcmp(arg+1, "ctab"))
324 {
325 /* get the character mapping table */
326 charmap = cmdarg(ec, &argp, &i, argc, 4, trdusage);
327 }
328 else if (!strcmp(arg+1, "ctab-"))
329 {
330 /* use the default mapping */
331 charmap_none = TRUE;
332 }
333 else
334 trdusage(ec);
335 break;
336
337 case 'r':
338 /* restore a game */
339 restore_file = cmdarg(ec, &argp, &i, argc, 1, trdusage);
340 break;
341
342 case 'i':
343 qasopn(cmdarg(ec, &argp, &i, argc, 1, trdusage), TRUE);
344 break;
345
346 case 'o':
347 cmdfile = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage),
348 OSFTCMD);
349 break;
350
351 case 'l':
352 logfp = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage),
353 OSFTCMD);
354 break;
355
356 case 'p':
357 if (!scumm_stricmp(arg, "-plain"))
358 {
359 os_plain();
360 break;
361 }
362 pause = cmdtog(ec, pause, arg, 1, trdusage);
363 break;
364
365 case 'd':
366 if (!scumm_strnicmp(arg, "-double", 7))
367 {
368 /* get the argument value */
369 doublespace = cmdtog(ec, doublespace, arg, 6, trdusage);
370
371 /* set the double-space mode in the formatter */
372 out_set_doublespace(doublespace);
373 break;
374 }
375 break;
376
377 case 's':
378 {
379 char *p;
380
381 /* get the option */
382 p = cmdarg(ec, &argp, &i, argc, 1, trdusage);
383
384 /* if they're asking for help, display detailed usage */
385 if (*p == '?')
386 trdusage_s(ec);
387
388 /* get the safety level from the argument */
389 safety_read = *p - '0';
390 safety_write = (*(p+1) != '\0' ? *(p+1) - '0' :
391 safety_read);
392
393 /* range-check the values */
394 if (safety_read < 0 || safety_read > 4
395 || safety_write < 0 || safety_write > 4)
396 trdusage_s(ec);
397
398 /* tell the host system about the setting */
399 if (appctx != 0 && appctx->set_io_safety_level != 0)
400 (*appctx->set_io_safety_level)
401 (appctx->io_safety_level_ctx,
402 safety_read, safety_write);
403 }
404 break;
405
406 case 'm':
407 switch(*(arg + 2))
408 {
409 case 's':
410 stksiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage));
411 break;
412
413 case 'h':
414 heapsiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage));
415 break;
416
417 default:
418 cachelimit = atol(cmdarg(ec, &argp, &i, argc, 1,
419 trdusage));
420 break;
421 }
422 break;
423
424 case 't':
425 /* swap file options: -tf file, -ts size, -t- (no swap) */
426 switch(*(arg+2))
427 {
428 case 'f':
429 swapname = cmdarg(ec, &argp, &i, argc, 2, trdusage);
430 break;
431
432 case 's':
433 swapsize = atol(cmdarg(ec, &argp, &i, argc, 2, trdusage));
434 break;
435
436 case 'p':
437 preload = cmdtog(ec, preload, arg, 2, trdusage);
438 break;
439
440 default:
441 swapena = cmdtog(ec, swapena, arg, 1, trdusage);
442 break;
443 }
444 break;
445
446 case 'u':
447 undosiz = atoi(cmdarg(ec, &argp, &i, argc, 1, trdusage));
448 break;
449
450 default:
451 trdusage(ec);
452 }
453 }
454 else break;
455 }
456
457 /* presume we won't take the .gam from the application executable */
458 exefile = 0;
459
460 /* get input name argument, and make sure it's the last argument */
461 if (i == argc)
462 {
463 osfildef *fp;
464 ulong curpos;
465 ulong endpos;
466 int use_exe;
467
468 /*
469 * There's no input name argument, so we need to find the game
470 * to play some other way. First, check to see if we have a
471 * game to restore, and if so whether it has the .GAM name
472 * encoded into it. Next, look to see if there's a game
473 * attached to the executable file; if so, use it. If not, see
474 * if the host system wants to provide a name through its
475 * callback.
476 */
477
478 /* presume we won't find a game attached to the executable file */
479 infile = 0;
480 use_exe = FALSE;
481
482 /*
483 * see if we have a saved game to restore, and it specifies the
484 * GAM file that saved it
485 */
486 if (restore_file != 0)
487 {
488 /* try getting the game name from the restore file */
489 if (fiorso_getgame(restore_file, inbuf, sizeof(inbuf)))
490 {
491 /* got it - use this file */
492 infile = inbuf;
493 }
494 }
495
496 /*
497 * it that didn't work, try to read from os-dependent part of
498 * program being executed
499 */
500 if (infile == 0)
501 {
502 /* try opening the executable file */
503 exefile = (argv && argv[0] ? argv[0] : "TRX");
504 fp = os_exeseek(exefile, "TGAM");
505 if (fp != 0)
506 {
507 /* see if there's a game file attached to the executable */
508 curpos = osfpos(fp);
509 osfseek(fp, 0L, OSFSK_END);
510 endpos = osfpos(fp);
511 osfcls(fp);
512
513 /* if we found it, use it */
514 if (endpos != curpos)
515 use_exe = TRUE;
516 }
517 }
518
519 /*
520 * if we didn't find a game in the executable, try the host
521 * system callback
522 */
523 if (infile == 0 && !use_exe)
524 {
525 /*
526 * ask the host system callback what to do - if we don't
527 * have a host system callback, or the callback
528 */
529 if (appctx != 0 && appctx->get_game_name != 0)
530 {
531 /* call the host system callback */
532 if ((*appctx->get_game_name)(appctx->get_game_name_ctx,
533 inbuf, sizeof(inbuf)))
534 {
535 /* the host system provided a name - use it */
536 infile = inbuf;
537 }
538 else
539 {
540 /*
541 * the host didn't provide a name - simply display a
542 * message indicating that no game file has been
543 * chosen, and return
544 */
545 trdptf("\n");
546 trdptf("(No game has been selected.)\n");
547 return;
548 }
549 }
550 else
551 {
552 /*
553 * we've run out of ways to get a filename - give the
554 * user the usage message and quit
555 */
556 trdusage(ec);
557 }
558 }
559 }
560 else
561 {
562 infile = *argp;
563 if (i + 1 != argc)
564 trdusage(ec);
565
566 #ifndef OS_HATES_EXTENSIONS
567 /*
568 * If original name exists, use it; otherwise, try adding .GAM.
569 * Note that this code is ifdef'd so that platforms that don't
570 * use filename extensions in the manner conventional for DOS
571 * and Unix won't use this code.
572 */
573 if (osfacc(infile))
574 {
575 strcpy(inbuf, infile);
576 os_defext(inbuf, "gam");
577 infile = inbuf;
578 }
579 #endif /* !defined(OS_HATES_EXTENSIONS) */
580 }
581
582 /* open up the swap file */
583 if (swapena && swapsize)
584 {
585 swapfp = os_create_tempfile(swapname, swapbuf);
586 if (swapname == 0) swapname = swapbuf;
587 if (swapfp == 0) errsig(ec, ERR_OPSWAP);
588 }
589
590 /* load the character map */
591 if (charmap_none)
592 cmap_override();
593 else if (cmap_load(charmap))
594 errsig(ec, ERR_INVCMAP);
595
596 ERRBEGIN(ec)
597
598 /* initialize cache manager context */
599 globalctx = mcmini(cachelimit, 128, swapsize, swapfp, swapname, ec);
600 mctx = mcmcini(globalctx, 128, fioldobj, &fiolctx,
601 objrevert, (void *)0);
602 mctx->mcmcxrvc = mctx;
603
604 /* set up an undo context */
605 if (undosiz)
606 undoptr = objuini(mctx, undosiz, vocdundo, vocdusz, &vocctx);
607 else
608 undoptr = (objucxdef *)0;
609
610 /* set up vocabulary context */
611 vocini(&vocctx, ec, mctx, &runctx, undoptr, 100, 100, 200);
612
613 /*
614 * save a pointer to the voc context globally, so that certain
615 * external routines (such as Unix-style signal handlers) can reach
616 * it
617 */
618 main_voc_ctx = &vocctx;
619
620 /* allocate stack and heap */
621 totsize = (ulong)stksiz * (ulong)sizeof(runsdef);
622 if (totsize != (size_t)totsize)
623 errsig1(ec, ERR_STKSIZE, ERRTINT, (uint)(65535/sizeof(runsdef)));
624 mystack = (runsdef *)mchalo(ec, (size_t)totsize, "runtime stack");
625 myheap = mchalo(ec, heapsiz, "runtime heap");
626
627 /* get the absolute path for the input file */
628 if (infile != 0)
629 os_get_abs_filename(infile_abs, sizeof(infile_abs), infile);
630 else if (exefile != 0)
631 os_get_abs_filename(infile_abs, sizeof(infile_abs), exefile);
632 else
633 infile_abs[0] = '\0';
634 os_get_path_name(infile_path, sizeof(infile_path), infile_abs);
635
636 /* set up execution context */
637 runctx.runcxerr = ec;
638 runctx.runcxmem = mctx;
639 runctx.runcxstk = mystack;
640 runctx.runcxstop = &mystack[stksiz];
641 runctx.runcxsp = mystack;
642 runctx.runcxbp = mystack;
643 runctx.runcxheap = myheap;
644 runctx.runcxhp = myheap;
645 runctx.runcxhtop = &myheap[heapsiz];
646 runctx.runcxundo = undoptr;
647 runctx.runcxbcx = &bifctx;
648 runctx.runcxbi = bif;
649 runctx.runcxtio = (tiocxdef *)0;
650 runctx.runcxdbg = &dbg;
651 runctx.runcxvoc = &vocctx;
652 runctx.runcxdmd = supcont;
653 runctx.runcxdmc = &supctx;
654 runctx.runcxext = 0;
655 runctx.runcxgamename = infile;
656 runctx.runcxgamepath = infile_path;
657
658 /* set up setup context */
659 supctx.supcxerr = ec;
660 supctx.supcxmem = mctx;
661 supctx.supcxtab = (tokthdef *)0;
662 supctx.supcxbuf = (uchar *)0;
663 supctx.supcxlen = 0;
664 supctx.supcxvoc = &vocctx;
665 supctx.supcxrun = &runctx;
666
667 /* set up debug context */
668 dbg.dbgcxtio = (tiocxdef *)0;
669 dbg.dbgcxmem = mctx;
670 dbg.dbgcxerr = ec;
671 dbg.dbgcxtab = (tokthdef *)0;
672 dbg.dbgcxfcn = 0;
673 dbg.dbgcxdep = 0;
674 dbg.dbgcxflg = 0;
675 dbg.dbgcxlin = (lindef *)0; /* no line sources yet */
676
677 /* set up built-in function context */
678 bifctx.bifcxerr = ec;
679 bifctx.bifcxrun = &runctx;
680 bifctx.bifcxtio = (tiocxdef *)0;
681 bifctx.bifcxrnd = 0;
682 bifctx.bifcxrndset = FALSE;
683 bifctx.bifcxappctx = appctx;
684 bifctx.bifcxsafetyr = safety_read;
685 bifctx.bifcxsafetyw = safety_write;
686 bifctx.bifcxsavext = save_ext;
687
688 /* initialize the regular expression parser context */
689 re_init(&bifctx.bifcxregex, ec);
690
691 /* add the built-in functions, keywords, etc */
692 supbif(&supctx, bif, (int)(sizeof(bif)/sizeof(bif[0])));
693
694 /* set up status line hack */
695 runistat(&vocctx, &runctx, (tiocxdef *)0);
696
697 /* turn on the "busy" cursor before loading */
698 os_csr_busy(TRUE);
699
700 /* read the game from the binary file */
701 fiord(mctx, &vocctx, (struct tokcxdef *)0,
702 infile, exefile, &fiolctx, &preinit, &flags,
703 (struct tokpdef *)0, (uchar **)0, (uint *)0, (uint *)0,
704 (preload ? 2 : 0), appctx, argv[0]);
705 loadopen = TRUE;
706
707 /* turn off the "busy" cursor */
708 os_csr_busy(FALSE);
709
710 /* play the game */
711 plygo(&runctx, &vocctx, (tiocxdef *)0, preinit, restore_file);
712
713 /* close load file */
714 fiorcls(&fiolctx);
715
716 if (pause)
717 {
718 trdptf("[press a key to exit]");
719 os_waitc();
720 trdptf("\n");
721 }
722
723 /* close and delete swapfile, if one was opened */
724 trd_close_swapfile(&runctx);
725
726 /* make sure the script file is closed, if we have one */
727 qasclose();
728
729 ERRCLEAN(ec)
730 /* close and delete swapfile, if one was opened */
731 trd_close_swapfile(&runctx);
732
733 /* close the load file if one was opened */
734 if (loadopen)
735 fiorcls(&fiolctx);
736
737 /* vocctx is going out of scope - forget the global reference to it */
738 main_voc_ctx = 0;
739
740 /* delete the voc context */
741 vocterm(&vocctx);
742
743 /* delete the undo context */
744 if (undoptr != 0)
745 objuterm(undoptr);
746
747 /* release the object cache structures */
748 if (mctx != 0)
749 mcmcterm(mctx);
750 if (globalctx != 0)
751 mcmterm(globalctx);
752 ERRENDCLN(ec)
753
754 /* vocctx is going out of scope - forget the global reference to it */
755 main_voc_ctx = 0;
756
757 /* delete the voc contxt */
758 vocterm(&vocctx);
759
760 /* delete the undo context */
761 if (undoptr != 0)
762 objuterm(undoptr);
763
764 /* release the object cache structures */
765 mcmcterm(mctx);
766 mcmterm(globalctx);
767 }
768
769 /*
770 * If the OS configuration so desires, use a less technical format for
771 * run-time error messages by leaving out the numeric error code. Note
772 * that we'll only do this if the error messages are linked directly
773 * into the run-time, since we need the numeric code as a last resort
774 * when the error message may not be present.
775 */
776 #ifdef OS_SKIP_ERROR_CODES
777 # ifdef ERR_LINK_MESSAGES
778 # define TRDLOGERR_PREFIX "\n[An error has occurred within TADS: "
779 # endif
780 #endif
781
782 /*
783 * If we didn't define a different error prefix format, use the default
784 * format with the numeric error code.
785 */
786 #ifndef TRDLOGERR_PREFIX
787 # define TRDLOGERR_PREFIX "\n[%s-%d: "
788 #endif
789
790 /* log an error */
trdlogerr(void * ctx0,const char * fac,int err,int argc,erradef * argv)791 static void trdlogerr(void *ctx0, const char *fac, int err, int argc, erradef *argv) {
792 errcxdef *ctx = (errcxdef *)ctx0;
793 char buf[256];
794 char msg[256];
795
796 /* display the prefix message to the console and log file */
797 sprintf(buf, TRDLOGERR_PREFIX, fac, err);
798 trdptf("%s", buf);
799 out_logfile_print(buf, FALSE);
800
801 /* display the error message text to the console and log file */
802 errmsg(ctx, msg, (uint)sizeof(msg), err);
803 errfmt(buf, (int)sizeof(buf), msg, argc, argv);
804 trdptf("%s]\n", buf);
805 out_logfile_print(buf, FALSE);
806 out_logfile_print("]", TRUE);
807 }
808
809
810 /*
811 * close and delete the swap file
812 */
trd_close_swapfile(runcxdef * runctx)813 void trd_close_swapfile(runcxdef *runctx)
814 {
815 extern voccxdef *main_voc_ctx;
816 mcmcxdef *mctx;
817 mcmcx1def *globalctx;
818 mcscxdef *mcsctx;
819
820 /* if no run context was supplied, find it from the main voc context */
821 if (runctx == 0)
822 {
823 /* if there is no main voc context, we're out of luck */
824 if (main_voc_ctx == 0)
825 return;
826
827 /* get the run context */
828 runctx = main_voc_ctx->voccxrun;
829 }
830
831 /* get the other relevant contexts */
832 mctx = runctx->runcxmem;
833 globalctx = mctx->mcmcxgl;
834 mcsctx = &globalctx->mcmcxswc;
835
836 /* if we have a swap file open, close it */
837 if (mcsctx->mcscxfp != 0)
838 {
839 /* close the file */
840 osfcls(mcsctx->mcscxfp);
841
842 /* forget about the file, so we don't try to close it again */
843 mcsctx->mcscxfp = (osfildef *)0;
844 }
845
846 /* if we have a filename, delete the file */
847 if (mcsctx->mcscxfname != 0)
848 {
849 /* delete the file */
850 osfdel_temp(mcsctx->mcscxfname);
851
852 /* forget the filename, so we don't try to delete the file again */
853 mchfre(mcsctx->mcscxfname);
854 mcsctx->mcscxfname = 0;
855 }
856 }
857
858 /* main - called by os main after setting up arguments */
trdmain(int argc,char * argv[],appctxdef * appctx,const char * save_ext)859 int trdmain(int argc, char *argv[], appctxdef *appctx, const char *save_ext)
860 {
861 errcxdef errctx;
862 int err;
863 osfildef *fp;
864
865 errctx.errcxlog = trdlogerr;
866 errctx.errcxlgc = &errctx;
867 errctx.errcxfp = (osfildef *)0;
868 errctx.errcxofs = 0;
869 errctx.errcxappctx = appctx;
870 fp = oserrop(argv[0]);
871 errini(&errctx, fp);
872
873 /* copyright-date-string */
874 #ifndef NO_T2_COPYRIGHT_NOTICE
875 trdptf("%s - A %s TADS %s Interpreter.\n",
876 G_tads_oem_app_name, G_tads_oem_display_mode,
877 TADS_RUNTIME_VERSION);
878 trdptf("%sopyright (c) 1993, 2012 by Michael J. Roberts.\n",
879 G_tads_oem_copyright_prefix ? "TADS c" : "C");
880 trdptf("%s\n", G_tads_oem_author);
881 #endif
882
883 ERRBEGIN(&errctx)
884 trdmain1(&errctx, argc, argv, appctx, save_ext);
885 ERRCATCH(&errctx, err)
886 /*
887 * log the error, unless it's usage (in which case we logged it
888 * already) or we're simply quitting the game
889 */
890 if (err != ERR_USAGE && err != ERR_RUNQUIT)
891 errclog(&errctx);
892
893 /* close the error file */
894 if (errctx.errcxfp != 0)
895 osfcls(errctx.errcxfp);
896
897 /* pause before exiting if the OS desires it */
898 os_expause();
899
900 /* return failure unless we're simply quitting the game */
901 return (err == ERR_RUNQUIT ? OSEXSUCC : OSEXFAIL);
902 ERREND(&errctx)
903
904 /* close the error file if we opened it */
905 if (errctx.errcxfp != 0)
906 osfcls(errctx.errcxfp);
907
908 /* successful completion */
909 return(OSEXSUCC);
910 }
911
912 } // End of namespace TADS2
913 } // End of namespace TADS
914 } // End of namespace Glk
915