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