1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/TADS2/PLY.C,v 1.3 1999/05/29 15:51:02 MJRoberts Exp $";
4 #endif
5 
6 /*
7  *   Copyright (c) 1992, 2002 Michael J. Roberts.  All Rights Reserved.
8  *
9  *   Please see the accompanying license file, LICENSE.TXT, for information
10  *   on using and copying this software.
11  */
12 /*
13 Name
14   ply.c - play game
15 Function
16   executes a game, starting with the 'init' function
17 Notes
18   none
19 Modified
20   04/04/92 MJRoberts     - creation
21 */
22 
23 #include "os.h"
24 #include "std.h"
25 #include "run.h"
26 #include "voc.h"
27 #include "err.h"
28 #include "obj.h"
29 #include "fio.h"
30 #include <ctype.h>
31 
32 #include "ply.h"
33 
34 /*
35  *   Play the game
36  */
plygo(runcxdef * run,voccxdef * voc,tiocxdef * tio,objnum preinit,char * restore_fname)37 void plygo(runcxdef *run, voccxdef *voc, tiocxdef *tio, objnum preinit,
38            char *restore_fname)
39 {
40     int       err;
41     errcxdef *ec = run->runcxerr;
42     char      filbuf[128];
43     int       first_time;
44     int noreg inited = FALSE;
45 
46     NOREG((&inited));
47 
48     first_time = TRUE;
49 
50     /*
51      *   Write out the special <?T2> HTML sequence, in case we're on an HTML
52      *   system.  This tells the HTML parser to use the parsing rules for
53      *   TADS 2 callers.
54      */
55     outformat("\\H+<?T2>\\H-");
56 
57 startover:
58     if (!inited)
59     {
60         /* use Me as the format-string actor for preinit and init */
61         tiosetactor(voc->voccxtio, voc->voccxme);
62 
63         /*
64          *   Run preinit, if it hasn't been run yet.  Note that we only
65          *   do this the first time through.  If we come back here via the
66          *   restart function, preinit will already have been run in the
67          *   restart function itself, so we don't need to run it again.
68          */
69         if (first_time)
70         {
71             /* make a note that we've been through here once already */
72             first_time = FALSE;
73 
74             /* remember the preinit function for later use in restarting */
75             voc->voccxpreinit = preinit;
76 
77             /* run the preinit() function */
78             ERRBEGIN(ec)
79             {
80                 /* reset the interpreter */
81                 runrst(run);
82 
83                 /* reset the parser */
84                 voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
85 
86                 /* run preinit */
87                 if (preinit != MCMONINV)
88                     runfn(run, preinit, 0);
89             }
90             ERRCATCH(ec, err)
91             {
92                 /* if they restarted, go back and start over */
93                 if (err == ERR_RUNRESTART)
94                     goto startover;
95 
96                 /* resignal the error */
97                 errrse(ec);
98             }
99             ERREND(ec);
100         }
101 
102         /*
103          *   Run the "init" function.  Do NOT run init if we're restoring
104          *   a game directly from the command line AND there's an
105          *   initRestore function defined.
106          */
107         if (restore_fname == 0 || voc->voccxinitrestore == MCMONINV)
108         {
109             ERRBEGIN(ec)
110             {
111                 /* reset the interpreter */
112                 runrst(run);
113 
114                 /* reset the parser */
115                 voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
116 
117                 /* run init */
118                 runfn(run, (objnum)voc->voccxini, 0);
119             }
120             ERRCATCH(ec, err)
121             {
122                 /* if they restarted, go back and start over */
123                 if (err == ERR_RUNRESTART)
124                     goto startover;
125 
126                 /* resignal the error */
127                 errrse(ec);
128             }
129             ERREND(ec);
130         }
131     }
132 
133     /* next time through, we'll need to run init again */
134     inited = FALSE;
135 
136     /*
137      *   check for startup parameter file to restore - if there's a
138      *   system-specific parameter file specified, pretend that it was
139      *   specified as the restore file
140      */
141     if (os_paramfile(filbuf))
142         restore_fname = filbuf;
143 
144     /* check for a file to restore */
145     if (restore_fname != 0)
146     {
147         /*
148          *   Check to see if the game file supports the initRestore
149          *   function.  If so, call it to restore the game.  If not,
150          *   restore the game directly.
151          */
152         if (voc->voccxinitrestore != MCMONINV)
153         {
154             char restore_buf[OSFNMAX*2];
155             char *src;
156             char *dst;
157 
158             /* convert any backslashes to double backslashes */
159             for (src = restore_fname, dst = restore_buf ;
160                  *src != '\0' && dst + 2 < restore_buf + sizeof(restore_buf) ;
161                  ++src)
162             {
163                 switch(*src)
164                 {
165                 case '\\':
166                     /* it's a backslash - double it */
167                     *dst++ = '\\';
168                     *dst++ = '\\';
169                     break;
170 
171                 default:
172                     /* copy the character as-is */
173                     *dst++ = *src;
174                 }
175             }
176 
177             /*
178              *   all the game's initRestore function with the name of
179              *   saved game file to restore as the argument
180              */
181 
182             /* reset the interpreter */
183             runrst(run);
184 
185             /* reset the parser */
186             voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
187 
188             /* push the game file name and run initRestore */
189             runpstr(run, restore_buf, dst - restore_buf, 0);
190             runfn(run, (objnum)voc->voccxinitrestore, 1);
191         }
192         else
193         {
194             /* restore the game */
195             os_printz("\n\n[Restoring saved game]\n\n");
196             err = fiorso(voc, restore_fname);
197             if (err)
198             {
199                 char buf[60 + OSFNMAX];
200 
201                 sprintf(buf, "\n\nError: unable to restore file \"%s\"\n\n",
202                         restore_fname);
203                 os_printz(buf);
204             }
205         }
206 
207         /* forget the saved game name, in case we restore */
208         restore_fname = 0;
209     }
210 
211     /* clear out the redo command buffer */
212     voc->voccxredobuf[0] = '\0';
213 
214     /* read and execute commands */
215     for (;;)
216     {
217         char buf[128];
218 
219         err = 0;
220         ERRBEGIN(ec)
221 
222         /* read a new command if there's nothing to redo */
223         if (!voc->voccxredo)
224         {
225             /* reset hidden output so we're showing output */
226             tioshow(tio);
227             tioflush(tio);
228 
229             /* clear the interpreter stack */
230             runrst(run);
231 
232             /* read a command */
233             vocread(voc, MCMONINV, MCMONINV, buf, (int)sizeof(buf), 0);
234 
235             /* special qa checking */
236             if (buf[0] == '@')
237             {
238                 int   quiet = FALSE;
239                 char *p;
240 
241                 p = buf + 1;
242                 if (*p == '@')
243                 {
244                     /* turn off MORE mode */
245                     setmore(0);
246 
247                     /* set NONSTOP mode in the OS layer */
248                     os_nonstop_mode(TRUE);
249 
250                     /* skip the extra '@' */
251                     ++p;
252                 }
253                 else if (*p == '!')
254                 {
255                     quiet = TRUE;
256                     ++p;
257                 }
258                 while (*p != '\0' && t_isspace(*p)) ++p;
259                 if (*p != '\0')
260                 {
261                     /* open the named file */
262                     qasopn(p, quiet);
263                 }
264                 else
265                 {
266                     char fname[256];
267 
268                     /* no file was named - ask the user to select a file */
269                     if (tio_askfile("Read script file:", fname, sizeof(fname),
270                                     OS_AFP_OPEN, OSFTCMD) == 0)
271                         qasopn(fname, quiet);
272                 }
273                 goto end_loop;
274             }
275         }
276 
277         /*
278          *   If there's redo in the redo buffer, use it now.  If the
279          *   buffer is empty and the redo flag is set, we'll just
280          *   re-execute whatever's in our internal buffer.
281          */
282         if (voc->voccxredo && voc->voccxredobuf[0] != '\0')
283         {
284             /* copy the redo buffer into our internal buffer */
285             strcpy(buf, voc->voccxredobuf);
286 
287             /* we've consumed it now, so clear it out */
288             voc->voccxredobuf[0] = '\0';
289         }
290 
291         /* we've now consumed the redo */
292         voc->voccxredo = FALSE;
293 
294         /* clear any pending break that's queued up */
295         (void)os_break();
296 
297         /* execute the command */
298         (void)voccmd(voc, buf, (uint)sizeof(buf));
299 
300     end_loop:
301         ERRCATCH(ec, err)
302         {
303             if (err != ERR_RUNQUIT && err != ERR_RUNRESTART)
304                 errclog(ec);
305         }
306         ERREND(ec);
307 
308         /* on interrupt, undo last command (which was partially executed) */
309         if (err == ERR_USRINT && voc->voccxundo)
310         {
311             ERRBEGIN(ec)
312                 objundo(voc->voccxmem, voc->voccxundo);
313             ERRCATCH(ec, err)
314                 if (err != ERR_NOUNDO && err != ERR_ICUNDO)
315                     errrse(ec);
316             ERREND(ec)
317         }
318 
319         /* if they want to quit, we're done */
320         if (err == ERR_RUNQUIT)
321             break;
322         else if (err == ERR_RUNRESTART)
323             goto startover;
324     }
325 
326     /*
327      *   If we're quitting, give the debugger one last chance at taking
328      *   control.  If it just returns, we can go ahead and terminate, but
329      *   if it wants it can restart the game by calling bifrst() as
330      *   normal.
331      */
332     ERRBEGIN(ec)
333     {
334         /* clear anything in the debugger stack trace */
335         run->runcxdbg->dbgcxfcn = 0;
336         run->runcxdbg->dbgcxdep = 0;
337 
338         /* tell the debugger the game has exited */
339         dbguquitting(run->runcxdbg);
340     }
341     ERRCATCH(ec, err)
342     {
343         switch(err)
344         {
345         case ERR_RUNRESTART:
346             /* they restarted the game - re-enter the play loop */
347             goto startover;
348 
349         case ERR_RUNQUIT:
350             /* quitting - proceed to return as normal */
351             break;
352 
353         default:
354             /* resignal any other error */
355             errrse(ec);
356         }
357     }
358     ERREND(ec);
359 }
360 
361