1 /* files.c - Transcription, recording and playback
2  *	Copyright (c) 1995-1997 Stefan Jokisch
3  *
4  * This file is part of Frotz.
5  *
6  * Frotz is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Frotz is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "frotz.h"
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 
26 #ifndef SEEK_SET
27 #define SEEK_SET 0
28 #define SEEK_CUR 1
29 #define SEEK_END 2
30 #endif
31 
32 extern void set_more_prompts (bool);
33 
34 extern bool is_terminator (zchar);
35 
36 extern bool read_yes_or_no (const char *);
37 
38 /* char script_name[MAX_FILE_NAME + 1] = DEFAULT_SCRIPT_NAME; */
39 /* char command_name[MAX_FILE_NAME + 1] = DEFAULT_COMMAND_NAME; */
40 
41 #ifdef __MSDOS__
42 extern char latin1_to_ibm[];
43 #endif
44 
45 static int script_width = 0;
46 
47 static FILE *sfp = NULL;
48 static FILE *rfp = NULL;
49 static FILE *pfp = NULL;
50 
51 /*
52  * script_open
53  *
54  * Open the transcript file. 'AMFV' makes this more complicated as it
55  * turns transcription on/off several times to exclude some text from
56  * the transcription file. This wasn't a problem for the original V4
57  * interpreters which always sent transcription to the printer, but it
58  * means a problem to modern interpreters that offer to open a new file
59  * every time transcription is turned on. Our solution is to append to
60  * the old transcription file in V1 to V4, and to ask for a new file
61  * name in V5+.
62  *
63  */
script_open(void)64 void script_open(void)
65 {
66 	static bool script_valid = FALSE;
67 
68 	char *new_name;
69 
70 	z_header.flags &= ~SCRIPTING_FLAG;
71 
72 	if (z_header.version >= V5 || !script_valid) {
73 		new_name = os_read_file_name(f_setup.script_name, FILE_SCRIPT);
74 		if (new_name == NULL)
75 			goto done;
76 		free(f_setup.script_name);
77 		f_setup.script_name = strdup(new_name);
78 	}
79 
80 	/* Opening in "at" mode doesn't work for script_erase_input... */
81 	if ((sfp = fopen (f_setup.script_name, "r+t")) != NULL ||
82 	    (sfp = fopen(f_setup.script_name, "w+t")) != NULL) {
83 		fseek (sfp, 0, SEEK_END);
84 		z_header.flags |= SCRIPTING_FLAG;
85 		script_valid = TRUE;
86 		ostream_script = TRUE;
87 		script_width = 0;
88 	} else
89 		print_string ("Cannot open file\n");
90 
91 done:
92 	SET_WORD(H_FLAGS, z_header.flags);
93 } /* script_open */
94 
95 
96 /*
97  * script_close
98  *
99  * Stop transcription.
100  *
101  */
script_close(void)102 void script_close(void)
103 {
104 	z_header.flags &= ~SCRIPTING_FLAG;
105 	SET_WORD(H_FLAGS, z_header.flags);
106 	fclose (sfp);
107 	ostream_script = FALSE;
108 } /* script_close */
109 
110 
111 /*
112  * script_new_line
113  *
114  * Write a newline to the transcript file.
115  *
116  */
script_new_line(void)117 void script_new_line(void)
118 {
119 	if (fputc ('\n', sfp) == EOF)
120 		script_close ();
121 	script_width = 0;
122 } /* script_new_line */
123 
124 
125 /*
126  * script_char
127  *
128  * Write a single character to the transcript file.
129  *
130  */
script_char(zchar c)131 void script_char(zchar c)
132 {
133 	if (c == ZC_INDENT && script_width != 0)
134 		c = ' ';
135 
136 	if (c == ZC_INDENT) {
137 		script_char(' ');
138 		script_char (' ');
139 		script_char (' ');
140 		return;
141 	}
142 
143 	if (c == ZC_GAP) {
144 		script_char (' ');
145 		script_char (' ');
146 		return;
147 	}
148 
149 #ifdef __MSDOS__
150 	if (c > 0xff)	/* Should always be false */
151 		c = '?';	/* Unreachable */
152 	if (c >= ZC_LATIN1_MIN)
153 		c = latin1_to_ibm[c - ZC_LATIN1_MIN];
154 	fputc(c, sfp);
155 	script_width++;
156 #else
157 
158 
159 #ifdef USE_UTF8
160 	if (c > 0x7ff) {	/* Encode as UTF-8 */
161 		fputc(0xe0 | ((c >> 12) & 0xf), sfp);
162 		fputc(0x80 | ((c >> 6) & 0x3f), sfp);
163 		fputc(0x80 | (c & 0x3f), sfp);
164 	} else if (c > 0x7f) {
165 		fputc(0xc0 | ((c >> 6) & 0x1f), sfp);
166 		fputc(0x80 | (c & 0x3f), sfp);
167 	} else
168 		fputc(c, sfp);
169 #else
170 	if (c > 0x7f) {
171 		fputc(0xc0 | ((c >> 6) & 0x1f), sfp);
172 		fputc(0x80 | (c & 0x3f), sfp);
173 	} else
174 		fputc(c, sfp);
175 #endif
176 
177 
178 	script_width++;
179 #endif
180 } /* script_char */
181 
182 
183 /*
184  * script_word
185  *
186  * Write a string to the transcript file.
187  *
188  */
script_word(const zchar * s)189 void script_word(const zchar *s)
190 {
191 	int width;
192 	int i;
193 
194 	if (*s == ZC_INDENT && script_width != 0)
195 		script_char (*s++);
196 
197 	for (i = 0, width = 0; s[i] != 0; i++) {
198 		if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT)
199 			i++;
200 		else if (s[i] == ZC_GAP)
201 			width += 3;
202 		else if (s[i] == ZC_INDENT)
203 			width += 2;
204 		else
205 			width += 1;
206 	}
207 
208 	if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols) {
209 		if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
210 			s++;
211 		script_new_line ();
212 	}
213 	for (i = 0; s[i] != 0; i++) {
214 		if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
215 			i++;
216 		else
217 			script_char (s[i]);
218 	}
219 } /* script_word */
220 
221 
222 /*
223  * script_write_input
224  *
225  * Send an input line to the transcript file.
226  *
227  */
script_write_input(const zchar * buf,zchar key)228 void script_write_input(const zchar *buf, zchar key)
229 {
230 	int width;
231 	int i;
232 
233 	for (i = 0, width = 0; buf[i] != 0; i++)
234 		width++;
235 
236 	if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols)
237 		script_new_line();
238 
239 	for (i = 0; buf[i] != 0; i++)
240 		script_char(buf[i]);
241 
242 	if (key == ZC_RETURN)
243 		script_new_line();
244 } /* script_write_input */
245 
246 
247 /*
248  * script_erase_input
249  *
250  * Remove an input line from the transcript file.
251  *
252  */
script_erase_input(const zchar * buf)253 void script_erase_input(const zchar *buf)
254 {
255 	int width;
256 	int i;
257 
258 	for (i = 0, width = 0; buf[i] != 0; i++)
259 		width++;
260 
261 	fseek(sfp, -width, SEEK_CUR); script_width -= width;
262 } /* script_erase_input */
263 
264 
265 /*
266  * script_mssg_on
267  *
268  * Start sending a "debugging" message to the transcript file.
269  *
270  */
script_mssg_on(void)271 void script_mssg_on(void)
272 {
273 	if (script_width != 0)
274 		script_new_line();
275 
276 	script_char(ZC_INDENT);
277 } /* script_mssg_on */
278 
279 
280 /*
281  * script_mssg_off
282  *
283  * Stop writing a "debugging" message.
284  *
285  */
script_mssg_off(void)286 void script_mssg_off(void)
287 {
288 	script_new_line();
289 
290 } /* script_mssg_off */
291 
292 
293 /*
294  * record_open
295  *
296  * Open a file to record the player's input.
297  *
298  */
record_open(void)299 void record_open(void)
300 {
301 	char *new_name;
302 
303 	new_name = os_read_file_name(f_setup.command_name, FILE_RECORD);
304 	if (new_name != NULL) {
305 		free(f_setup.command_name);
306 		f_setup.command_name = strdup(new_name);
307 
308 		if ((rfp = fopen(new_name, "wt")) != NULL)
309 			ostream_record = TRUE;
310 		else
311 			print_string("Cannot open file\n");
312 	}
313 } /* record_open */
314 
315 
316 /*
317  * record_close
318  *
319  * Stop recording the player's input.
320  *
321  */
record_close(void)322 void record_close (void)
323 {
324     fclose (rfp); ostream_record = FALSE;
325 
326 }/* record_close */
327 
328 
329 /*
330  * record_code
331  *
332  * Helper function for record_char.
333  *
334  */
record_code(int c,bool force_encoding)335 static void record_code(int c, bool force_encoding)
336 {
337 	if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) {
338 		int i;
339 
340 		fputc('[', rfp);
341 
342 		for (i = 10000; i != 0; i /= 10) {
343 			if (c >= i || i == 1)
344 				fputc('0' + (c / i) % 10, rfp);
345 		}
346 		fputc(']', rfp);
347 	} else
348 		fputc(c, rfp);
349 
350 } /* record_code */
351 
352 
353 /*
354  * record_char
355  *
356  * Write a character to the command file.
357  *
358  */
record_char(zchar c)359 static void record_char(zchar c)
360 {
361 	if (c != ZC_RETURN) {
362 		if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) {
363 			record_code (translate_to_zscii (c), FALSE);
364 			if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
365 				record_code (mouse_x, TRUE);
366 				record_code (mouse_y, TRUE);
367 			}
368 		} else
369 			record_code (1000 + c - ZC_HKEY_MIN, TRUE);
370 	}
371 } /* record_char */
372 
373 
374 /*
375  * record_write_key
376  *
377  * Copy a keystroke to the command file.
378  *
379  */
record_write_key(zchar key)380 void record_write_key(zchar key)
381 {
382 	record_char(key);
383 	if (fputc('\n', rfp) == EOF)
384 		record_close();
385 } /* record_write_key */
386 
387 
388 /*
389  * record_write_input
390  *
391  * Copy a line of input to a command file.
392  *
393  */
record_write_input(const zchar * buf,zchar key)394 void record_write_input(const zchar *buf, zchar key)
395 {
396 	zchar c;
397 
398 	while ((c = *buf++) != 0)
399 		record_char(c);
400 	record_char(key);
401 	if (fputc('\n', rfp) == EOF)
402 		record_close();
403 } /* record_write_input */
404 
405 
406 /*
407  * replay_open
408  *
409  * Open a file of commands for playback.
410  *
411  */
replay_open(void)412 void replay_open(void)
413 {
414 	char *new_name;
415 
416 	new_name = os_read_file_name(f_setup.command_name, FILE_PLAYBACK);
417 	if (new_name != NULL) {
418 		free(f_setup.command_name);
419 		f_setup.command_name = strdup(new_name);
420 
421 		if ((pfp = fopen(new_name, "rt")) != NULL) {
422 			set_more_prompts(read_yes_or_no("Do you want MORE prompts"));
423 			istream_replay = TRUE;
424 		} else
425 			print_string("Cannot open file\n");
426 	}
427 } /* replay_open */
428 
429 
430 /*
431  * replay_close
432  *
433  * Stop playback of commands.
434  *
435  */
replay_close(void)436 void replay_close(void)
437 {
438 	set_more_prompts(TRUE);
439 	fclose (pfp);
440 	istream_replay = FALSE;
441 } /* replay_close */
442 
443 
444 /*
445  * replay_code
446  *
447  * Helper function for replay_key and replay_line.
448  *
449  */
replay_code(void)450 static int replay_code(void)
451 {
452 	int c;
453 
454 	if ((c = fgetc(pfp)) == '[') {
455 		int c2;
456 
457 		c = 0;
458 		while ((c2 = fgetc(pfp)) != EOF && c2 >= '0' && c2 <= '9')
459 			c = 10 * c + c2 - '0';
460 
461 		return (c2 == ']') ? c : EOF;
462 	} else
463 		return c;
464 } /* replay_code */
465 
466 
467 /*
468  * replay_char
469  *
470  * Read a character from the command file.
471  *
472  */
replay_char(void)473 static zchar replay_char (void)
474 {
475 	int c;
476 
477 	if ((c = replay_code()) != EOF) {
478 		if (c != '\n') {
479 			 if (c < 1000) {
480 				c = translate_from_zscii (c);
481 				if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
482 					mouse_x = replay_code();
483 					mouse_y = replay_code();
484 				}
485 				return c;
486 
487 			} else
488 				return ZC_HKEY_MIN + c - 1000;
489 		}
490 		ungetc('\n', pfp);
491 		return ZC_RETURN;
492 	} else
493 		return ZC_BAD;
494 } /* replay_char */
495 
496 
497 /*
498  * replay_read_key
499  *
500  * Read a keystroke from a command file.
501  *
502  */
replay_read_key(void)503 zchar replay_read_key (void)
504 {
505 	zchar key;
506 
507 	key = replay_char();
508 
509 	if (fgetc(pfp) != '\n') {
510 		replay_close();
511 		return ZC_BAD;
512 	} else
513 		return key;
514 } /* replay_read_key */
515 
516 
517 /*
518  * replay_read_input
519  *
520  * Read a line of input from a command file.
521  *
522  */
replay_read_input(zchar * buf)523 zchar replay_read_input(zchar *buf)
524 {
525 	zchar c;
526 
527 	for (;;) {
528 		c = replay_char();
529 		if (c == ZC_BAD || is_terminator(c))
530 			break;
531 		*buf++ = c;
532 	}
533 	*buf = 0;
534 
535 	if (fgetc(pfp) != '\n') {
536 		replay_close();
537 		return ZC_BAD;
538 	} else
539 		return c;
540 
541 } /* replay_read_input */
542