1 /*
2  * fileio.c
3  *
4  * File manipulation routines. Should be generic.
5  *
6  */
7 
8 #include "ztypes.h"
9 #include "pickle.h"
10 
11 /* A couple of definitions for constants used to access PICKLE files.
12 	Both are Mac-style four-byte constants. You can read each as
13 	four characters, MSB first. (Don't worry about whether your
14 	system is big- or little-endian; the PICKLE library takes care
15 	of that stuff.) */
16 /* The "use" of executable chunks */
17 #define PICKLETYPE_exec (0x65786563)
18 /* The "format" of executable chunks containing Z-code */
19 #define PICKLETYPE_zcod (0x7a636f64)
20 
21 /* All of the following variables are set in open_story(). */
22 
23 /* is_pickle is a boolean flag; it will be TRUE if the open zcode is
24 	contained in a PICKLE file. It is not static because other modules
25 	may wish to check its value. */
26 int is_pickle;
27 
28 /* pickle_map is the opaque handle to the PICKLE file map in memory.
29 	It is not static because other modules will need it in order
30 	to load PICKLE chunks. */
31 pikMapPtr pickle_map;
32 
33 /* zcode_length is the length of the zcode. If is_pickle is FALSE,
34 	this is the actual file length. */
35 static unsigned long zcode_length;
36 
37 /* zcode_offset is the starting position of the zcode in its file. If
38 	is_pickle is FALSE, this is just 0. */
39 static unsigned long zcode_offset;
40 
41 /* Static data */
42 
43 static FILE *gfp = NULL; /* Game file pointer */
44 static FILE *sfp = NULL; /* Script file pointer */
45 static FILE *rfp = NULL; /* Record file pointer */
46 static char save_name[FILENAME_MAX + 1] = "";
47 static char savedata_name[FILENAME_MAX + 1] = "";
48 static char script_name[FILENAME_MAX + 1] = "";
49 static char record_name[FILENAME_MAX + 1] = "";
50 
51 static int undo_valid = FALSE;
52 static zword_t undo_stack[STACK_SIZE];
53 
54 static int script_file_valid = FALSE;
55 
56 #ifdef __STDC__
57 static int save_restore (const char *, int);
58 static int save_restore_data (const char *, int, int, zword_t *);
59 static void set_names (const char *);
60 static void swap_bytes (zword_t *, int);
61 #else
62 static int save_restore ();
63 static int save_restore_data ();
64 static void set_names ();
65 static void swap_bytes ();
66 #endif
67 
68 /*
69  * set_names
70  *
71  * Set up the story names intelligently.
72  * Taken from JZip, John Holder, 28 Sept 1995
73  */
74 #ifdef __STDC__
set_names(const char * storyname)75 static void set_names (const char *storyname)
76 #else
77 static void set_names (storyname)
78 const char *storyname;
79 #endif
80 {
81     char *per_pos=0;
82 
83     /* experimental setting of save_name, added by John Holder 26 July 1995 */
84     per_pos = strrchr(storyname,'.'); /* find last '.' in storyname. */
85     if (per_pos) /* The story file looks like "Odius.dat" or "odieus.z3" */
86     {
87       strcpy(save_name, storyname);
88       per_pos=strrchr(save_name,'.');
89       *(per_pos) = '\0';
90       strcat(save_name,".sav");
91 
92       strcpy(script_name, storyname);
93       per_pos=strrchr(script_name,'.');
94       *(per_pos) = '\0';
95       strcat(script_name,".scr");
96 
97       strcpy(savedata_name, storyname);
98       per_pos=strrchr(savedata_name,'.');
99       *(per_pos) = '\0';
100       strcat(savedata_name,".dat");
101 
102       strcpy(record_name, storyname);
103       per_pos=strrchr(record_name,'.');
104       *(per_pos) = '\0';
105       strcat(record_name,".rec");
106     }
107     else /* The story file looks like: "OdieusQuest" */
108     {
109       strcpy(save_name, storyname);
110       strcat(save_name,".sav");
111 
112       strcpy(script_name, storyname);
113       strcat(script_name,".scr");
114 
115       strcpy(savedata_name, storyname);
116       strcat(savedata_name,".dat");
117 
118       strcpy(record_name, storyname);
119       strcat(record_name,".rec");
120     }
121 } /* set_names */
122 
123 /*
124  * open_story
125  *
126  * Open game file for read.
127  *
128  */
129 
130 #ifdef __STDC__
open_story(const char * storyname)131 void open_story (const char *storyname)
132 #else
133 void open_story (storyname)
134 const char *storyname;
135 #endif
136 {
137     /* Most of this function is new. --Z */
138     int ix;
139     char firstfour[4];
140     pikErr err;
141     pikChunkID chunkid;
142     pikChunk chunk;
143 
144     /* This is the list of formats that the PICKLE library will check for.
145         You can extend it in the obvious way. */
146 #define NUMPICKLEFORMATS (4)
147     static pikFormat formatlist[NUMPICKLEFORMATS] = {
148         {PICKLETYPE_zcod, 3},
149         {PICKLETYPE_zcod, 4},
150         {PICKLETYPE_zcod, 5},
151         {PICKLETYPE_zcod, 8}
152     };
153 
154     /* The following three lines are what used to here. --Z */
155     gfp = fopen (storyname, "rb");
156     if (gfp == NULL && storyname[0] != '/') {
157         /* Try the magic path variable. */
158         char *classpath = getenv("INFOCOM_PATH");
159 	if (classpath && strlen(classpath) > 0) {
160 	    char *p;
161 	    p = strtok(classpath, ":");
162 	    while (p && !gfp) {
163 	        char classfile[FILENAME_MAX+1];
164 		sprintf(classfile, "%s/%s", p, storyname);
165 		gfp = fopen (classfile, "rb");
166 		p = strtok(NULL, ":");
167 	    }
168 	}
169     }
170     if (gfp == NULL)
171         fatal ("Game file not found");
172 
173     set_names(storyname);
174 
175     /* Now the new code... --Z */
176 
177     for (ix = 0; ix < 4; ix++) {
178         firstfour[ix] = fgetc (gfp);
179         if (firstfour[ix] == EOF)
180             break;
181     }
182 
183     /* The PICKLE library doesn't require the file to be rewound here,
184     and standard ZIP doesn't either, but it doesn't hurt to be sure. */
185     rewind(gfp);
186 
187     is_pickle = FALSE;
188     zcode_length = 0;
189     zcode_offset = 0;
190 
191     if (ix == 4) {
192         if (pikIsPickleHeader(firstfour)) {
193             /* Yes! It is indeed a PICKLE file! */
194             is_pickle = TRUE;
195             /* Open the PICKLE file and load the map into memory. */
196             err = pikCreateMap(gfp, &pickle_map);
197             if (err)
198                 fatal ("Unable to open PICKLE file");
199             /* Find and load the Z-code chunk. */
200             err = pikFindChunk(pickle_map, PICKLETYPE_exec, 0, NUMPICKLEFORMATS,
201                 formatlist, &chunkid);
202             if (err == pikerr_NotFound)
203                 fatal ("There is no Z-code chunk in this PICKLE file");
204             if (err)
205                 fatal ("Unable to search PICKLE file");
206             err = pikLoadChunk(pickle_map, chunkid, pikmethod_FilePos, &chunk);
207             if (err)
208                 fatal ("Unable to read Z-code chunk from PICKLE file");
209             zcode_offset = chunk.data.startpos;
210             zcode_length = chunk.length;
211         }
212     }
213 }/* open_story */
214 
215 /*
216  * close_story
217  *
218  * Close game file if open.
219  *
220  */
221 
222 #ifdef __STDC__
close_story(void)223 void close_story (void)
224 #else
225 void close_story ()
226 #endif
227 {
228     if (is_pickle) { /* --Z */
229         pikDestroyMap(pickle_map);
230     } /* --Z */
231 
232     /* The rest of this function is not changed. --Z */
233 
234     if (gfp != NULL)
235         fclose (gfp);
236 
237 }/* close_story */
238 
239 /*
240  * get_story_size
241  *
242  * Calculate the size of the game file. Only used for very old games that do not
243  * have the game file size in the header.
244  *
245  */
246 
247 #ifdef __STDC__
get_story_size(void)248 unsigned int get_story_size (void)
249 #else
250 unsigned int get_story_size ()
251 #endif
252 {
253     unsigned long file_length;
254 
255     if (!is_pickle) { /* --Z */
256         /* Read whole file to calculate file size */
257 
258         rewind (gfp);
259         for (file_length = 0; fgetc (gfp) != EOF; file_length++)
260             ;
261         rewind (gfp);
262     }
263     else {
264         /* Use the length loaded from the PICKLE file */
265         file_length = zcode_length;
266     } /* --Z */
267 
268     /* The rest of this function is not changed. --Z */
269 
270     /* Calculate length of file in game allocation units */
271 
272     file_length = (file_length + (unsigned long) (story_scaler - 1)) / (unsigned long) story_scaler;
273 
274     return ((unsigned int) file_length);
275 
276 }/* get_story_size */
277 
278 /*
279  * read_page
280  *
281  * Read one game file page.
282  *
283  */
284 
285 #ifdef __STDC__
read_page(int page,void * buffer)286 void read_page (int page, void *buffer)
287 #else
288 void read_page (page, buffer)
289 int page;
290 void *buffer;
291 #endif
292 {
293     unsigned long file_size;
294     unsigned int pages, offset;
295 
296     /* Seek to start of page */
297 
298     fseek (gfp, zcode_offset + (long) page * PAGE_SIZE, SEEK_SET);
299 
300     /* Read the page */
301 
302     if (fread (buffer, PAGE_SIZE, 1, gfp) != 1) {
303 
304         /* Read failed. Are we in the last page? */
305 
306         file_size = (unsigned long) h_file_size * story_scaler;
307         pages = (unsigned int) ((unsigned long) file_size / PAGE_SIZE);
308         offset = (unsigned int) ((unsigned long) file_size & PAGE_MASK);
309         if ((unsigned int) page == pages) {
310 
311             /* Read partial page if this is the last page in the game file */
312 
313             fseek (gfp, zcode_offset + (long) page * PAGE_SIZE, SEEK_SET);
314             if (fread (buffer, offset, 1, gfp) == 1)
315                 return;
316         }
317         fatal ("Game file read error");
318     }
319 
320 }/* read_page */
321 
322 /*
323  * verify
324  *
325  * Verify game ($verify verb). Add all bytes in game file except for bytes in
326  * the game file header.
327  *
328  */
329 
330 #ifdef __STDC__
verify(void)331 void verify (void)
332 #else
333 void verify ()
334 #endif
335 {
336     unsigned long file_size;
337     unsigned int pages, offset;
338     unsigned int start, end, i, j;
339     zword_t checksum = 0;
340     zbyte_t buffer[PAGE_SIZE];
341 
342     /* Print version banner */
343 
344     if (h_type < V4) {
345         write_string ("ZIP Interpreter ");
346         print_number (get_byte (H_INTERPRETER));
347         write_string (", Version ");
348         write_char (get_byte (H_INTERPRETER_VERSION));
349         write_string (".");
350         new_line ();
351     }
352 
353     /* Calculate game file dimensions */
354 
355     file_size = (unsigned long) h_file_size * story_scaler;
356     pages = (unsigned int) ((unsigned long) file_size / PAGE_SIZE);
357     offset = (unsigned int) file_size & PAGE_MASK;
358 
359     /* Sum all bytes in game file, except header bytes */
360 
361     for (i = 0; i <= pages; i++) {
362         read_page (i, buffer);
363         start = (i == 0) ? 64 : 0;
364         end = (i == pages) ? offset : PAGE_SIZE;
365         for (j = start; j < end; j++)
366             checksum += buffer[j];
367     }
368 
369     /* Make a conditional jump based on whether the checksum is equal */
370 
371     conditional_jump (checksum == h_checksum);
372 
373 }/* verify */
374 
375 /*
376  * save
377  *
378  * Save game state to disk. Returns:
379  *     0 = save failed
380  *     1 = save succeeded
381  *
382  */
383 
384 #ifdef __STDC__
save(int argc,zword_t * argv)385 int save (int argc, zword_t *argv)
386 #else
387 int save (argc, argv)
388 int argc;
389 zword_t *argv;
390 #endif
391 {
392     int status = 1; /* assume failure */
393     int storestatus = 0;
394 
395     if (argc == 0) {
396 
397         char new_save_name[FILENAME_MAX + 1];
398 
399 	/* Get the file name */
400 	if (get_file_name (new_save_name, save_name, GAME_SAVE) == 0) {
401 
402 	    /* Do a save operation */
403 	    if (save_restore (new_save_name, GAME_SAVE) == 0) {
404 
405 	        /* Cleanup file */
406 	        file_cleanup (new_save_name, GAME_SAVE);
407 
408 		/* Save the new name as the default file name */
409 		strcpy (save_name, new_save_name);
410 
411 		/* Indicate success */
412 		status = 0;
413 	    }
414         }
415 	storestatus = ((status == 0) ? 1 : 0);
416     }
417     else {
418 
419         char new_savedata_name[FILENAME_MAX + 1];
420         char defname[FILENAME_MAX + 1];
421 	char *defnameptr = NULL;
422 
423 	if (argc >= 3) {
424 	    unsigned char *cx = datap + argv[2];
425 	    int cxlen = cx[0];
426 	    memcpy(defname, cx+1, cxlen);
427 	    defname[cxlen] = '\0';
428 	    defnameptr = defname;
429 	}
430 
431 	/* Get the file name ### defname */
432 	if (get_file_name (new_savedata_name, savedata_name,
433 			   GAME_SAVEDATA) == 0) {
434 
435   	    /* Do a save operation */
436 	    storestatus = save_restore_data (new_savedata_name,
437 		GAME_SAVEDATA, argc, argv);
438 	    if (storestatus != 0) {
439 
440 	        /* Cleanup file */
441 	        file_cleanup (new_savedata_name, GAME_SAVEDATA);
442 
443 		/* Save the new name as the default file name */
444 		strcpy (savedata_name, new_savedata_name);
445 
446 		/* Indicate success */
447 		status = 0;
448 	    }
449 	}
450     }
451 
452     /* Return result of save to Z-code */
453 
454     if (h_type < V4)
455         conditional_jump (status == 0);
456     else
457         store_operand (storestatus);
458 
459     return (status);
460 
461 }/* save */
462 
463 /*
464  * restore
465  *
466  * Restore game state from disk. Returns:
467  *     0 = restore failed
468  *     2 = restore succeeded
469  *
470  */
471 
472 #ifdef __STDC__
restore(int argc,zword_t * argv)473 int restore (int argc, zword_t *argv)
474 #else
475 int restore (argc, argv)
476 int argc;
477 zword_t *argv;
478 #endif
479 {
480     int status = 1; /* assume failure */
481     int storestatus = 0;
482 
483     if (argc == 0) {
484         char new_save_name[FILENAME_MAX + 1];
485 
486 	/* Get the file name */
487 	if (get_file_name (new_save_name, save_name, GAME_RESTORE) == 0) {
488 
489 	    /* Do the restore operation */
490 	    if (save_restore (new_save_name, GAME_RESTORE) == 0) {
491 
492 	        /* Cleanup file */
493 	        file_cleanup (new_save_name, GAME_SAVE);
494 
495 		/* Save the new name as the default file name */
496 		strcpy (save_name, new_save_name);
497 
498 		/* Indicate success */
499 		status = 0;
500 
501 	    }
502 	}
503 	storestatus = ((status == 0) ? 2 : 0);
504     }
505     else {
506         char new_savedata_name[FILENAME_MAX + 1];
507         char defname[FILENAME_MAX + 1];
508 	char *defnameptr = NULL;
509 
510 	if (argc >= 3) {
511 	    unsigned char *cx = datap + argv[2];
512 	    int cxlen = cx[0];
513 	    memcpy(defname, cx+1, cxlen);
514 	    defname[cxlen] = '\0';
515 	    defnameptr = defname;
516 	}
517 
518 	/* Get the file name ### defname */
519 	if (get_file_name (new_savedata_name, savedata_name,
520 			   GAME_RESTOREDATA) == 0) {
521 
522 	    /* Do the restore operation */
523 	    storestatus = save_restore_data (new_savedata_name,
524 	        GAME_RESTOREDATA, argc, argv);
525 	    if (storestatus != 0) {
526 
527   	        /* Cleanup file */
528 	        file_cleanup (new_savedata_name, GAME_RESTOREDATA);
529 
530 		/* Save the new name as the default file name */
531 		strcpy (savedata_name, new_savedata_name);
532 
533 		/* Indicate success */
534 		status = 0;
535 	    }
536 	}
537     }
538 
539     /* Return result of save to Z-code */
540 
541     if (h_type < V4)
542         conditional_jump (status == 0);
543     else
544         store_operand (storestatus);
545 
546     return (status);
547 
548 }/* restore */
549 
550 /*
551  * undo_save
552  *
553  * Save the current Z machine state in memory for a future undo. Returns:
554  *    -1 = feature unavailable
555  *     0 = save failed
556  *     1 = save succeeded
557  *
558  */
559 
560 #ifdef __STDC__
undo_save(void)561 void undo_save (void)
562 #else
563 void undo_save ()
564 #endif
565 {
566 
567     /* Check if undo is available first */
568 
569     if (undo_datap != NULL) {
570 
571         /* Save the undo data and return success */
572 
573         save_restore (NULL, UNDO_SAVE);
574 
575         undo_valid = TRUE;
576 
577         store_operand (1);
578 
579     } else
580 
581         /* If no memory for data area then say undo is not available */
582 
583         store_operand ((zword_t) -1);
584 
585 }/* undo_save */
586 
587 /*
588  * undo_restore
589  *
590  * Restore the current Z machine state from memory. Returns:
591  *    -1 = feature unavailable
592  *     0 = restore failed
593  *     2 = restore succeeded
594  *
595  */
596 
597 #ifdef __STDC__
undo_restore(void)598 void undo_restore (void)
599 #else
600 void undo_restore ()
601 #endif
602 {
603 
604     /* Check if undo is available first */
605 
606     if (undo_datap != NULL) {
607 
608         /* If no undo save done then return an error */
609 
610         if (undo_valid == TRUE) {
611 
612             /* Restore the undo data and return success */
613 
614             save_restore (NULL, UNDO_RESTORE);
615 
616             store_operand (2);
617 
618         } else
619 
620             store_operand (0);
621 
622     } else
623 
624         /* If no memory for data area then say undo is not available */
625 
626         store_operand ((zword_t) -1);
627 
628 }/* undo_restore */
629 
630 /*
631  * swap_bytes
632  *
633  * Swap the low and high bytes in every word of the specified array.
634  * The length is specified in BYTES!
635  *
636  * This routine added by Mark Phillips(msp@bnr.co.uk), Thanks Mark!
637  */
638 
639 #ifdef __STDC__
swap_bytes(zword_t * ptr,int len)640 static void swap_bytes(zword_t *ptr, int len)
641 #else
642 static void swap_bytes(ptr, len)
643 zword_t *ptr;
644 int len;
645 #endif
646 {
647   unsigned char *pbyte;
648   unsigned char tmp;
649 
650   len/=2; /* convert len into number of 2 byte words */
651 
652   pbyte=(unsigned char*)ptr;
653 
654   while (len)
655   {
656     tmp=pbyte[0];
657     pbyte[0]=pbyte[1];
658     pbyte[1]=tmp;
659     pbyte+=2;
660     len--;
661   }
662   return;
663 }
664 
665 /*
666  * save_restore
667  *
668  * Common save and restore code. Just save or restore the game stack and the
669  * writeable data area.
670  *
671  */
672 
673 #ifdef __STDC__
save_restore(const char * file_name,int flag)674 static int save_restore (const char *file_name, int flag)
675 #else
676 static int save_restore (file_name, flag)
677 const char *file_name;
678 int flag;
679 #endif
680 {
681     FILE *tfp = NULL;
682     int scripting_flag = 0, status = 0;
683 #ifdef BIG_END_MODE
684     int little_endian=0;
685 #else
686     int little_endian=1;
687 #endif
688 
689     /* Open the save file and disable scripting */
690 
691     if (flag == GAME_SAVE || flag == GAME_RESTORE) {
692         if ((tfp = fopen (file_name, (flag == GAME_SAVE) ? "wb" : "rb")) == NULL) {
693             output_line ("Cannot open SAVE file");
694             return (1);
695         }
696         scripting_flag = get_word (H_FLAGS) & SCRIPTING_FLAG;
697         set_word (H_FLAGS, get_word (H_FLAGS) & (~SCRIPTING_FLAG));
698     }
699 
700 #if defined(USE_QUETZAL)
701     if (flag == GAME_SAVE)
702         status = !save_quetzal (tfp, gfp, zcode_offset);
703     else if (flag == GAME_RESTORE)
704         status = !restore_quetzal (tfp, gfp, zcode_offset);
705     else {
706 #endif  /* defined(USE_QUETZAL) */
707 
708     /* Push PC, FP, version and store SP in special location */
709 
710     stack[--sp] = (zword_t) (pc / PAGE_SIZE);
711     stack[--sp] = (zword_t) (pc % PAGE_SIZE);
712     stack[--sp] = fp;
713     stack[--sp] = h_version;
714     stack[0] = sp;
715 
716     /* Save or restore stack */
717 
718 #if !defined(USE_QUETZAL)
719     if (flag == GAME_SAVE) {
720 	if (little_endian) swap_bytes(stack, sizeof(stack));
721         if (status == 0 && fwrite (stack, sizeof (stack), 1, tfp) != 1)
722             status = 1;
723 	if (little_endian) swap_bytes(stack, sizeof(stack));
724     } else if (flag == GAME_RESTORE) {
725 	if (little_endian) swap_bytes(stack, sizeof(stack));
726         if (status == 0 && fread (stack, sizeof (stack), 1, tfp) != 1)
727             status = 1;
728 	if (little_endian) swap_bytes(stack, sizeof(stack));
729     } else
730 #endif/* !defined(USE_QUETZAL) */
731     if (flag == UNDO_SAVE) {
732         memmove (undo_stack, stack, sizeof (stack));
733     } else /* if (flag == UNDO_RESTORE) */
734         memmove (stack, undo_stack, sizeof (stack));
735 
736     /* Restore SP, check version, restore FP and PC */
737 
738     sp = stack[0];
739     if (stack[sp++] != h_version)
740         fatal ("Wrong game or version");
741     fp = stack[sp++];
742     pc = stack[sp++];
743     pc += (unsigned long) stack[sp++] * PAGE_SIZE;
744 
745     /* Save or restore writeable game data area */
746 
747 #if !defined(USE_QUETZAL)
748     if (flag == GAME_SAVE) {
749         if (status == 0 && fwrite (datap, h_restart_size, 1, tfp) != 1)
750             status = 1;
751     } else if (flag == GAME_RESTORE) {
752         if (status == 0 && fread (datap, h_restart_size, 1, tfp) != 1)
753             status = 1;
754     } else
755 #endif  /* !defined(USE_QUETZAL) */
756     if (flag == UNDO_SAVE) {
757         memmove (undo_datap, datap, h_restart_size);
758     } else /* if (flag == UNDO_RESTORE) */
759         memmove (datap, undo_datap, h_restart_size);
760 
761 #if defined(USE_QUETZAL)
762     }
763 #endif  /* defined(USE_QUETZAL) */
764 
765     /* Close the save file and restore scripting */
766 
767     if (flag == GAME_SAVE) {
768         fclose (tfp);
769         if (scripting_flag)
770             set_word (H_FLAGS, get_word (H_FLAGS) | SCRIPTING_FLAG);
771     }
772     else if (flag == GAME_RESTORE) {
773         fclose (tfp);
774         restart_screen();
775         restart_interp(scripting_flag);
776     }
777 
778     /* Handle read or write errors */
779 
780     if (status) {
781         if (flag == GAME_SAVE) {
782             output_line ("Write to SAVE file failed");
783             remove (file_name);
784         } else {
785             output_line ("Read from SAVE file failed");
786         }
787     }
788 
789     return (status);
790 
791 }/* save_restore */
792 
793 /*
794  * open_script
795  *
796  * Open the scripting file.
797  *
798  */
799 
800 #ifdef __STDC__
open_script(void)801 void open_script (void)
802 #else
803 void open_script ()
804 #endif
805 {
806     char new_script_name[FILENAME_MAX + 1];
807 
808     /* Open scripting file if closed */
809 
810     if (scripting == OFF) {
811 
812         if (script_file_valid == TRUE) {
813 
814             sfp = fopen (script_name, "a");
815 
816             /* Turn on scripting if open succeeded */
817 
818             if (sfp != NULL)
819                 scripting = ON;
820             else
821                 output_line ("Script file open failed");
822 
823         } else {
824 
825             /* Get scripting file name and record it */
826 
827             if (get_file_name (new_script_name, script_name, GAME_SCRIPT) == 0) {
828 
829                 /* Open scripting file */
830 
831                 sfp = fopen (new_script_name, "w");
832 
833                 /* Turn on scripting if open succeeded */
834 
835                 if (sfp != NULL) {
836 
837                     script_file_valid = TRUE;
838 
839                     /* Make file name the default name */
840 
841                     strcpy (script_name, new_script_name);
842 
843                     /* Turn on scripting */
844 
845                     scripting = ON;
846 
847                 } else
848 
849                     output_line ("Script file create failed");
850 
851             }
852 
853         }
854 
855     }
856 
857     /* Set the scripting flag in the game file flags */
858 
859     if (datap) {
860         if (scripting == ON)
861             set_word (H_FLAGS, get_word (H_FLAGS) | SCRIPTING_FLAG);
862 	else
863             set_word (H_FLAGS, get_word (H_FLAGS) & (~SCRIPTING_FLAG));
864     }
865 
866 }/* open_script */
867 
868 /*
869  * close_script
870  *
871  * Close the scripting file.
872  *
873  */
874 
875 #ifdef __STDC__
close_script(void)876 void close_script (void)
877 #else
878 void close_script ()
879 #endif
880 {
881 
882     /* Close scripting file if open */
883 
884     if (scripting == ON) {
885 
886         fclose (sfp);
887 #if 0
888         /* Cleanup */
889 
890         file_cleanup (script_name, GAME_SCRIPT);
891 #endif
892         /* Turn off scripting */
893 
894         scripting = OFF;
895 
896     }
897 
898     /* Set the scripting flag in the game file flags */
899 
900     if (datap) {
901         if (scripting == OFF)
902             set_word (H_FLAGS, get_word (H_FLAGS) & (~SCRIPTING_FLAG));
903         else
904             set_word (H_FLAGS, get_word (H_FLAGS) | SCRIPTING_FLAG);
905     }
906 
907 }/* close_script */
908 
909 /*
910  * script_char
911  *
912  * Write one character to scripting file.
913  *
914  * Check the state of the scripting flag first. Older games only set the
915  * scripting flag in the game flags instead of calling the set_print_modes
916  * function. This is because they expect a physically attached printer that
917  * doesn't need opening like a file.
918  */
919 
920 #ifdef __STDC__
script_char(int c)921 void script_char (int c)
922 #else
923 void script_char (c)
924 int c;
925 #endif
926 {
927 
928     /* Check the state of the scripting flag in the game flags. If it is on
929        then check to see if the scripting file is open as well */
930 
931     if ((get_word (H_FLAGS) & SCRIPTING_FLAG) != 0 && scripting == OFF)
932         open_script ();
933 
934     /* Check the state of the scripting flag in the game flags. If it is off
935        then check to see if the scripting file is closed as well */
936 
937     if ((get_word (H_FLAGS) & SCRIPTING_FLAG) == 0 && scripting == ON)
938         close_script ();
939 
940     /* If scripting file is open, we are in the text window and the character is
941        printable then write the character */
942 
943     if (scripting == ON && scripting_disable == OFF && (c == '\n' || (isprint (c))))
944         putc (c, sfp);
945 
946 }/* script_char */
947 
948 /*
949  * script_string
950  *
951  * Write a string to the scripting file.
952  *
953  */
954 
955 #ifdef __STDC__
script_string(const char * s)956 void script_string (const char *s)
957 #else
958 void script_string (s)
959 const char *s;
960 #endif
961 {
962 
963     /* Write string */
964 
965     while (*s)
966         script_char (*s++);
967 
968 }/* script_string */
969 
970 /*
971  * script_line
972  *
973  * Write a string followed by a new line to the scripting file.
974  *
975  */
976 
977 #ifdef __STDC__
script_line(const char * s,int len)978 void script_line (const char *s, int len)
979 #else
980 void script_line (s, len)
981 const char *s;
982 int len;
983 #endif
984 {
985     int ix;
986 
987     /* Write string */
988 
989     for (ix=0; ix<len; ix++)
990         script_char(s[ix]);
991 
992     /* Write new line */
993 
994     script_new_line ();
995 
996 }/* script_line */
997 
998 /*
999  * script_new_line
1000  *
1001  * Write a new line to the scripting file.
1002  *
1003  */
1004 
1005 #ifdef __STDC__
script_new_line(void)1006 void script_new_line (void)
1007 #else
1008 void script_new_line ()
1009 #endif
1010 {
1011 
1012     script_char ('\n');
1013 
1014 }/* script_new_line */
1015 
1016 /*
1017  * open_record
1018  *
1019  * Turn on recording of all input to an output file.
1020  *
1021  */
1022 
1023 #ifdef __STDC__
open_record(void)1024 void open_record (void)
1025 #else
1026 void open_record ()
1027 #endif
1028 {
1029     char new_record_name[FILENAME_MAX + 1];
1030 
1031     /* If recording or playback is already on then complain */
1032 
1033     if (recording == ON || replaying == ON) {
1034 
1035         output_line ("Recording or playback are already active.");
1036 
1037     } else {
1038 
1039         /* Get recording file name */
1040 
1041         if (get_file_name (new_record_name, record_name, GAME_RECORD) == 0) {
1042 
1043             /* Open recording file */
1044 
1045             rfp = fopen (new_record_name, "w");
1046 
1047             /* Turn on recording if open succeeded */
1048 
1049             if (rfp != NULL) {
1050 
1051                 /* Make file name the default name */
1052 
1053                 strcpy (record_name, new_record_name);
1054 
1055                 /* Set recording on */
1056 
1057                 recording = ON;
1058 
1059             } else
1060 
1061                 output_line ("Record file create failed");
1062 
1063         }
1064 
1065     }
1066 
1067 }/* open_record */
1068 
1069 /*
1070  * record_line
1071  *
1072  * Write a string followed by a new line to the recording file.
1073  *
1074  */
1075 
1076 #ifdef __STDC__
record_line(const char * s,int len)1077 void record_line (const char *s, int len)
1078 #else
1079 void record_line (s, len)
1080 const char *s;
1081 int len;
1082 #endif
1083 {
1084     int ix;
1085 
1086     if (recording == ON && replaying == OFF) {
1087 
1088         /* Write string */
1089         for (ix=0; ix<len; ix++)
1090 	    putc(s[ix], rfp);
1091 
1092 	putc('\n', rfp);
1093 
1094     }
1095 
1096 }/* record_line */
1097 
1098 /*
1099  * record_key
1100  *
1101  * Write a key followed by a new line to the recording file.
1102  *
1103  */
1104 
1105 #ifdef __STDC__
record_key(int c)1106 void record_key (int c)
1107 #else
1108 void record_key (c)
1109 int c;
1110 #endif
1111 {
1112 
1113     if (recording == ON && replaying == OFF) {
1114 
1115         /* Write the key */
1116 
1117         fprintf (rfp, "<%0o>\n", c);
1118 
1119     }
1120 
1121 }/* record_key */
1122 
1123 /*
1124  * close_record
1125  *
1126  * Turn off recording of all input to an output file.
1127  *
1128  */
1129 
1130 #ifdef __STDC__
close_record(void)1131 void close_record (void)
1132 #else
1133 void close_record ()
1134 #endif
1135 {
1136 
1137     /* Close recording file */
1138 
1139     if (rfp != NULL) {
1140         fclose (rfp);
1141         rfp = NULL;
1142 
1143         /* Cleanup */
1144 
1145         if (recording == ON)
1146             file_cleanup (record_name, GAME_RECORD);
1147         else /* (replaying == ON) */
1148             file_cleanup (record_name, GAME_PLAYBACK);
1149     }
1150 
1151     /* Set recording and replaying off */
1152 
1153     recording = OFF;
1154     replaying = OFF;
1155 
1156 }/* close_record */
1157 
1158 /*
1159  * open_playback
1160  *
1161  * Take input from command file instead of keyboard.
1162  *
1163  */
1164 
1165 #ifdef __STDC__
open_playback(int arg)1166 void open_playback (int arg)
1167 #else
1168 void open_playback (arg)
1169 int arg;
1170 #endif
1171 {
1172     char new_record_name[FILENAME_MAX + 1];
1173 
1174     /* If recording or replaying is already on then complain */
1175 
1176     if (recording == ON || replaying == ON) {
1177 
1178         output_line ("Recording or replaying is already active.");
1179 
1180     } else {
1181 
1182         /* Get recording file name */
1183 
1184         if (get_file_name (new_record_name, record_name, GAME_PLAYBACK) == 0) {
1185 
1186             /* Open recording file */
1187 
1188             rfp = fopen (new_record_name, "r");
1189 
1190             /* Turn on recording if open succeeded */
1191 
1192             if (rfp != NULL) {
1193 
1194                 /* Make file name the default name */
1195 
1196                 strcpy (record_name, new_record_name);
1197 
1198                 /* Set replaying on */
1199 
1200                 replaying = ON;
1201 
1202             } else
1203 
1204                 output_line ("Record file open failed");
1205 
1206         }
1207 
1208     }
1209 
1210 }/* open_playback */
1211 
1212 /*
1213  * playback_line
1214  *
1215  * Get a line of input from the command file.
1216  *
1217  */
1218 
1219 #ifdef __STDC__
playback_line(int buflen,char * buffer,int * read_size)1220 int playback_line (int buflen, char *buffer, int *read_size)
1221 #else
1222 int playback_line (buflen, buffer, read_size)
1223 int buflen;
1224 char *buffer;
1225 int *read_size;
1226 #endif
1227 {
1228     char *cp;
1229 
1230     if (recording == ON || replaying == OFF)
1231         return (-1);
1232 
1233     if (fgets (buffer, buflen, rfp) == NULL) {
1234         close_record ();
1235         return (-1);
1236     } else {
1237         cp = strrchr (buffer, '\n');
1238         if (cp != NULL)
1239             *cp = '\0';
1240         *read_size = strlen (buffer);
1241         output_line (buffer);
1242     }
1243 
1244     return ('\n');
1245 
1246 }/* playback_line */
1247 
1248 /*
1249  * playback_key
1250  *
1251  * Get a key from the command file.
1252  *
1253  */
1254 
1255 #ifdef __STDC__
playback_key(void)1256 int playback_key (void)
1257 #else
1258 int playback_key ()
1259 #endif
1260 {
1261     int c;
1262 
1263     if (recording == ON || replaying == OFF)
1264         return (-1);
1265 
1266     if (fscanf (rfp, "<%o>\n", &c) == EOF) {
1267         close_record ();
1268         c = -1;
1269     }
1270 
1271     return (c);
1272 
1273 }/* playback_key */
1274 
1275 /*
1276  * save_restore_data. This returns the actual z-machine status: number of bytes
1277  * loaded for a restore; 0 for failure / 1 for success, for a save.
1278  *
1279  */
1280 
1281 #ifdef __STDC__
save_restore_data(const char * file_name,int flag,int argc,zword_t * argv)1282 static int save_restore_data (const char *file_name, int flag, int argc, zword_t *argv)
1283 #else
1284 static int save_restore_data (file_name, flag, argc, argv)
1285 const char *file_name;
1286 int flag;
1287 int argc;
1288 zword_t *argv;
1289 #endif
1290 {
1291     FILE *tfp;
1292     long dat_begin, dat_len;
1293     int status, val;
1294     long lx;
1295     char *cx;
1296 
1297     if (argc < 2)
1298         return (1);
1299 
1300     dat_begin = argv[0];
1301     dat_len = argv[1];
1302 
1303     /* Open the save file */
1304 
1305     if (flag == GAME_SAVEDATA)
1306         tfp = fopen (file_name, "wb");
1307     else
1308         tfp = fopen (file_name, "rb");
1309 
1310     if (tfp == NULL) {
1311         output_line ("Cannot open DATA file");
1312         return (1);
1313     }
1314 
1315     /* Save or restore writeable game data area */
1316     if (flag == GAME_SAVEDATA) {
1317         lx = fwrite (datap+dat_begin, 1, dat_len, tfp);
1318 	if (lx != dat_len) {
1319             output_line ("Write to DATA file failed");
1320             status = 0;
1321 	}
1322 	else {
1323 	    status = 1;
1324 	}
1325     }
1326     else if (flag == GAME_RESTOREDATA) {
1327         lx = fread (datap+dat_begin, 1, dat_len, tfp);
1328 	status = lx; /* number of bytes read */
1329     }
1330 
1331     /* Close the save file */
1332 
1333     fclose (tfp);
1334 
1335     return (status);
1336 
1337 } /* save_restore_data */
1338