c5598180b354ea091ba2fb82e20377f798ca7538
[projects/chimara/chimara.git] / interpreters / frotz / glkmisc.c
1 /******************************************************************************
2  *                                                                            *
3  * Copyright (C) 2006-2009 by Tor Andersson.                                  *
4  *                                                                            *
5  * This file is part of Gargoyle.                                             *
6  *                                                                            *
7  * Gargoyle is free software; you can redistribute it and/or modify           *
8  * it under the terms of the GNU General Public License as published by       *
9  * the Free Software Foundation; either version 2 of the License, or          *
10  * (at your option) any later version.                                        *
11  *                                                                            *
12  * Gargoyle 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 Gargoyle; if not, write to the Free Software                    *
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA *
20  *                                                                            *
21  *****************************************************************************/
22
23 /* glkstuff.c -- non-screen related glk stuff */
24
25 #include "glkfrotz.h"
26
27 #define VERSION "2.50"
28
29 int curr_status_ht = 0;
30 int mach_status_ht = 0;
31
32 winid_t gos_upper = NULL;
33 winid_t gos_lower = NULL;
34 winid_t gos_curwin = NULL;
35
36 int gos_linepending = 0;
37 zchar *gos_linebuf = NULL;
38 winid_t gos_linewin = NULL;
39
40 schanid_t gos_channel = NULL;
41
42 #define INFORMATION ""\
43 "An interpreter for Infocom and other Z-Machine games.\n"\
44 "Complies with standard 1.0 of Graham Nelson's specification.\n"\
45 "Plays Z-code versions 1-5 and 8.\n"\
46 "\n"\
47 "Syntax: frotz [options] story-file\n"\
48 "    -a   watch attribute setting\n"\
49 "    -A   watch attribute testing\n"\
50 "    -i   ignore fatal errors\n"\
51 "    -o   watch object movement\n"\
52 "    -O   watch object locating\n"\
53 "    -P   alter piracy opcode\n"\
54 "    -Q   use old-style save format\n"\
55 "    -s # random number seed value\n"\
56 "    -S # transscript width\n"\
57 "    -t   set Tandy bit\n"\
58 "    -u # slots for multiple undo\n"\
59 "    -x   expand abbreviations g/x/z\n"
60
61 /* A unix-like getopt, but with the names changed to avoid any problems.  */
62 static int zoptind = 1;
63 static int zoptopt = 0;
64 static char *zoptarg = NULL;
65 static int zgetopt (int argc, char *argv[], const char *options)
66 {
67         static int pos = 1;
68         const char *p;
69         if (zoptind >= argc || argv[zoptind][0] != '-' || argv[zoptind][1] == 0)
70                 return EOF;
71         zoptopt = argv[zoptind][pos++];
72         zoptarg = NULL;
73         if (argv[zoptind][pos] == 0)
74         {
75                 pos = 1;
76                 zoptind++;
77         }
78         p = strchr (options, zoptopt);
79         if (zoptopt == ':' || p == NULL)
80         {
81                 fputs ("illegal option -- ", stderr);
82                 goto error;
83         }
84         else if (p[1] == ':')
85         {
86                 if (zoptind >= argc) {
87                         fputs ("option requires an argument -- ", stderr);
88                         goto error;
89                 } else {
90                         zoptarg = argv[zoptind];
91                         if (pos != 1)
92                                 zoptarg += pos;
93                         pos = 1; zoptind++;
94                 }
95         }
96         return zoptopt;
97 error:
98         fputc (zoptopt, stderr);
99         fputc ('\n', stderr);
100         return '?';
101 }
102
103 static int user_random_seed = -1;
104 static int user_tandy_bit = 0;
105 static char *graphics_filename = NULL;
106
107 void os_process_arguments(int argc, char *argv[]) 
108 {
109         int c;
110
111 #ifdef GARGLK
112         garglk_set_program_name("Frotz " VERSION);
113         garglk_set_program_info(
114                         "Glk Frotz " VERSION "\n"
115                         "Original Frotz by Stefan Jokisch\n"
116                         "Unix port by Jim Dunleavy and David Griffith\n"
117                         "Glk port by Tor Andersson\n");
118 #endif
119
120         /* Parse the options */
121         do {
122                 c = zgetopt(argc, argv, "aAioOPQs:S:tu:xZ:");
123                 switch (c)
124                 {
125                         case 'a': option_attribute_assignment = 1; break;
126                         case 'A': option_attribute_testing = 1; break;
127                         case 'i': option_ignore_errors = 1; break;
128                         case 'o': option_object_movement = 1; break;
129                         case 'O': option_object_locating = 1; break;
130                         case 'P': option_piracy = 1; break;
131                         case 'Q': option_save_quetzal = 0; break;
132                         case 's': user_random_seed = atoi(zoptarg); break;
133                         case 'S': option_script_cols = atoi(zoptarg); break;
134                         case 't': user_tandy_bit = 1; break;
135                         case 'u': option_undo_slots = atoi(zoptarg); break;
136                         case 'x': option_expand_abbreviations = 1; break;
137                         case 'Z': option_err_report_mode = atoi(zoptarg);
138                                           if ((option_err_report_mode < ERR_REPORT_NEVER) ||
139                                                           (option_err_report_mode > ERR_REPORT_FATAL))
140                                                   option_err_report_mode = ERR_DEFAULT_REPORT_MODE;
141                                           break;
142                 }
143         } while (c != EOF);
144
145         if (((argc - zoptind) != 1) && ((argc - zoptind) != 2))
146         {
147                 winid_t win;
148                 char buf[256];
149                 win = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
150                 glk_set_window(win);
151                 glk_put_string("FROTZ V" VERSION " -- Glk 0.7.0 interface.\n");
152                 glk_put_string(INFORMATION);
153                 sprintf(buf,
154                                 "    -Z # error checking mode (default = %d)\n"
155                                 "         %d = don't report errors.  "
156                                 "%d = report first error.\n"
157                                 "         %d = report all errors.  "
158                                 "%d = exit after any error.\n",
159                                 ERR_DEFAULT_REPORT_MODE, ERR_REPORT_NEVER,
160                                 ERR_REPORT_ONCE, ERR_REPORT_ALWAYS, ERR_REPORT_FATAL);
161                 glk_put_string(buf);
162                 glk_exit();
163         }
164         else
165         {
166                 char *s;
167
168                 story_name = argv[zoptind++];
169                 if (zoptind < argc)
170                         graphics_filename = argv[zoptind++];
171
172                 #ifdef GARGLK
173                 s = strrchr(story_name, '\\');
174                 if (!s) s = strrchr(story_name, '/');
175                 garglk_set_story_name(s ? s + 1 : story_name);
176                 #endif
177         }
178 }
179
180 void os_init_screen(void)
181 {
182         glui32 width, height;
183
184         /*
185          * Init glk stuff
186          */
187
188         /* monor */
189         glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Proportional, 0);
190         glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Weight, 0);
191         glk_stylehint_set(wintype_AllTypes,   style_Preformatted, stylehint_Oblique, 0);
192         glk_stylehint_set(wintype_TextGrid,   style_Preformatted, stylehint_ReverseColor, 1);
193
194         /* monob */
195         glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Proportional, 0);
196         glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Weight, 1);
197         glk_stylehint_set(wintype_AllTypes,   style_Subheader,    stylehint_Oblique, 0);
198         glk_stylehint_set(wintype_TextGrid,   style_Subheader,    stylehint_ReverseColor, 1);
199
200         /* monoi */
201         glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Proportional, 0);
202         glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Weight, 0);
203         glk_stylehint_set(wintype_AllTypes,   style_Alert,        stylehint_Oblique, 1);
204         glk_stylehint_set(wintype_TextGrid,   style_Alert,        stylehint_ReverseColor, 1);
205
206         /* monoz */
207         glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Proportional, 0);
208         glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Weight, 1);
209         glk_stylehint_set(wintype_AllTypes,   style_BlockQuote,   stylehint_Oblique, 1);
210         glk_stylehint_set(wintype_TextGrid,   style_BlockQuote,   stylehint_ReverseColor, 1);
211
212         /* propr */
213         glk_stylehint_set(wintype_TextBuffer, style_Normal,       stylehint_Proportional, 1);
214         glk_stylehint_set(wintype_TextGrid,   style_Normal,       stylehint_Proportional, 0);
215         glk_stylehint_set(wintype_AllTypes,   style_Normal,       stylehint_Weight, 0);
216         glk_stylehint_set(wintype_AllTypes,   style_Normal,       stylehint_Oblique, 0);
217         glk_stylehint_set(wintype_TextGrid,   style_Normal,       stylehint_ReverseColor, 1);
218
219         /* propb */
220         glk_stylehint_set(wintype_TextBuffer, style_Header,       stylehint_Proportional, 1);
221         glk_stylehint_set(wintype_TextGrid,   style_Header,       stylehint_Proportional, 0);
222         glk_stylehint_set(wintype_AllTypes,   style_Header,       stylehint_Weight, 1);
223         glk_stylehint_set(wintype_AllTypes,   style_Header,       stylehint_Oblique, 0);
224         glk_stylehint_set(wintype_TextGrid,   style_Header,       stylehint_ReverseColor, 1);
225
226         /* propi */
227         glk_stylehint_set(wintype_TextBuffer, style_Emphasized,   stylehint_Proportional, 1);
228         glk_stylehint_set(wintype_TextGrid,   style_Emphasized,   stylehint_Proportional, 0);
229         glk_stylehint_set(wintype_AllTypes,   style_Emphasized,   stylehint_Weight, 0);
230         glk_stylehint_set(wintype_AllTypes,   style_Emphasized,   stylehint_Oblique, 1);
231         glk_stylehint_set(wintype_TextGrid,   style_Emphasized,   stylehint_ReverseColor, 1);
232
233         /* propi */
234         glk_stylehint_set(wintype_TextBuffer, style_Note,         stylehint_Proportional, 1);
235         glk_stylehint_set(wintype_TextGrid,   style_Note,         stylehint_Proportional, 0);
236         glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Weight, 1);
237         glk_stylehint_set(wintype_AllTypes,   style_Note,         stylehint_Oblique, 1);
238         glk_stylehint_set(wintype_TextGrid,   style_Note,         stylehint_ReverseColor, 1);
239
240         gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
241         if (!gos_lower)
242                 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
243         glk_window_get_size(gos_lower, &width, &height);
244         glk_window_close(gos_lower, NULL);
245
246         gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
247         gos_upper = glk_window_open(gos_lower,
248                         winmethod_Above | winmethod_Fixed,
249                         0,
250                         wintype_TextGrid, 0);
251
252         gos_channel = NULL;
253
254         glk_set_window(gos_lower);
255         gos_curwin = gos_lower;
256
257         /*
258          * Icky magic bit setting
259          */
260
261         if (h_version == V3 && user_tandy_bit)
262                 h_config |= CONFIG_TANDY;
263
264         if (h_version == V3 && gos_upper)
265                 h_config |= CONFIG_SPLITSCREEN;
266
267         if (h_version == V3 && !gos_upper)
268                 h_config |= CONFIG_NOSTATUSLINE;
269
270         if (h_version >= V4)
271                 h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
272                         CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
273
274         if (h_version >= V5)
275                 h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
276
277         if ((h_version >= 5) && (h_flags & SOUND_FLAG))
278                 h_flags |= SOUND_FLAG;
279
280         if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
281                 h_flags |= OLD_SOUND_FLAG;
282
283         if ((h_version == 6) && (option_sound != 0)) 
284                 h_config |= CONFIG_SOUND;
285
286         if (h_version >= V5 && (h_flags & UNDO_FLAG))
287                 if (option_undo_slots == 0)
288                         h_flags &= ~UNDO_FLAG;
289
290         h_screen_cols = width;
291         h_screen_rows = height;
292
293         h_screen_height = h_screen_rows;
294         h_screen_width = h_screen_cols;
295
296         h_font_width = 1;
297         h_font_height = 1;
298
299         /* Must be after screen dimensions are computed.  */
300         if (h_version == V6) {
301                 h_flags &= ~GRAPHICS_FLAG;
302         }
303
304         /* Use the ms-dos interpreter number for v6, because that's the
305          * kind of graphics files we understand.  Otherwise, use DEC.  */
306         h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
307         h_interpreter_version = 'F';
308         {
309                 /* Set these per spec 8.3.2. */
310                 h_default_foreground = WHITE_COLOUR;
311                 h_default_background = BLACK_COLOUR;
312                 if (h_flags & COLOUR_FLAG) h_flags &= ~COLOUR_FLAG;
313         }
314 }
315
316 int os_random_seed (void)
317 {
318     if (user_random_seed == -1)
319         /* Use the epoch as seed value */
320         return (time(0) & 0x7fff);
321     return user_random_seed;
322 }
323
324 void os_restart_game (int stage) {}
325
326 void os_fatal (const char *s)
327 {
328         char err[256];
329         sprintf(err,"%s",s);
330
331         if (!gos_lower)
332                 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
333
334         glk_set_window(gos_lower);
335         glk_set_style(style_Normal);
336         glk_put_string("\n\nFatal error: ");
337         glk_put_string(err);
338         glk_put_string("\n");
339         glk_exit();
340 }
341
342 void os_init_setup(void)
343 {
344         option_attribute_assignment = 0;
345         option_attribute_testing = 0;
346         option_context_lines = 0;
347         option_object_locating = 0;
348         option_object_movement = 0;
349         option_left_margin = 0;
350         option_right_margin = 0;
351         option_ignore_errors = 0;
352         option_piracy = 0;
353         option_undo_slots = MAX_UNDO_SLOTS;
354         option_expand_abbreviations = 0;
355         option_script_cols = 80;
356         option_save_quetzal = 1;
357         option_sound = 1;
358         option_err_report_mode = ERR_DEFAULT_REPORT_MODE;
359 }
360
361 void gos_cancel_pending_line(void)
362 {
363         event_t ev;
364         glk_cancel_line_event(gos_linewin, &ev);
365         gos_linebuf[ev.val1] = '\0';
366         gos_linepending = 0;
367 }
368
369 zchar os_read_key (int timeout, bool show_cursor)
370 {
371         event_t ev;
372         winid_t win = gos_curwin ? gos_curwin : gos_lower;
373
374         if (gos_linepending)
375                 gos_cancel_pending_line();
376
377         glk_request_char_event_uni(win);
378         if (timeout != 0)
379                 glk_request_timer_events(timeout * 100);
380
381         while (1)
382         {
383                 glk_select(&ev);
384                 if (ev.type == evtype_Arrange) {
385                         gos_update_height();
386                         gos_update_width();
387                 }
388                 else if (ev.type == evtype_Timer)
389                 {
390                         glk_cancel_char_event(win);
391                         glk_request_timer_events(0);
392                         return ZC_TIME_OUT;
393                 }
394                 else if (ev.type == evtype_CharInput)
395                         break;
396         }
397
398         glk_request_timer_events(0);
399
400         if (gos_upper && mach_status_ht < curr_status_ht)
401                 reset_status_ht();
402         curr_status_ht = 0;
403
404         switch (ev.val1)
405         {
406                 case keycode_Escape: return ZC_ESCAPE;
407                 case keycode_PageUp: return ZC_ARROW_MIN;
408                 case keycode_PageDown: return ZC_ARROW_MAX;
409                 case keycode_Left: return ZC_ARROW_LEFT;
410                 case keycode_Right: return ZC_ARROW_RIGHT;
411                 case keycode_Up: return ZC_ARROW_UP;
412                 case keycode_Down: return ZC_ARROW_DOWN;
413                 case keycode_Return: return ZC_RETURN;
414                 case keycode_Delete: return ZC_BACKSPACE;
415                 case keycode_Tab: return ZC_INDENT;
416                 default:
417                         return ev.val1;
418         }
419 }
420
421 zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued)
422 {
423         event_t ev;
424         winid_t win = gos_curwin ? gos_curwin : gos_lower;
425
426         if (!continued && gos_linepending)
427                 gos_cancel_pending_line(); 
428
429         if (!continued || !gos_linepending)
430         {
431                 glk_request_line_event_uni(win, buf, max, os_string_length(buf));
432                 if (timeout != 0)
433                         glk_request_timer_events(timeout * 100);
434         }
435
436         gos_linepending = 0;
437
438         while (1)
439         {
440                 glk_select(&ev);
441                 if (ev.type == evtype_Arrange) {
442                         gos_update_height();
443                         gos_update_width();
444                 }
445                 else if (ev.type == evtype_Timer)
446                 {
447                         gos_linewin = win;
448                         gos_linepending = 1;
449                         gos_linebuf = buf;
450                         return ZC_TIME_OUT;
451                 }
452                 else if (ev.type == evtype_LineInput)
453                         break;
454         }
455
456         glk_request_timer_events(0);
457         buf[ev.val1] = '\0';
458
459         if (gos_upper && mach_status_ht < curr_status_ht)
460                 reset_status_ht();
461         curr_status_ht = 0;
462
463         return ZC_RETURN;
464 }
465
466 zword os_read_mouse(void)
467 {
468         /* NOT IMPLEMENTED */
469         return 0;
470 }
471
472 void os_scrollback_char (zchar z)
473 {
474         /* NOT IMPLEMENTED */
475 }
476
477 void os_scrollback_erase (int amount)
478 {
479         /* NOT IMPLEMENTED */
480 }
481
482 static glui32 flag2usage(int flag)
483 {
484         switch (flag)
485         {       
486                 case FILE_RESTORE:
487                         return fileusage_SavedGame | fileusage_BinaryMode;
488                 case FILE_SAVE:
489                         return fileusage_SavedGame | fileusage_BinaryMode;
490                 case FILE_SCRIPT:
491                         return fileusage_Transcript | fileusage_TextMode;
492                 case FILE_PLAYBACK:
493                         return fileusage_InputRecord | fileusage_TextMode;
494                 case FILE_RECORD:
495                         return fileusage_InputRecord | fileusage_TextMode;
496                 case FILE_LOAD_AUX:
497                         return fileusage_Data | fileusage_BinaryMode;
498                 case FILE_SAVE_AUX:
499                         return fileusage_Data | fileusage_BinaryMode;
500         }
501         return 0;
502 }
503
504 static glui32 flag2mode(int flag)
505 {
506         switch (flag)
507         {       
508         case FILE_RESTORE:
509                 return filemode_Read;
510         case FILE_SAVE:
511                 return filemode_Write;
512         case FILE_SCRIPT:
513                 return filemode_ReadWrite;      /* append really, but with erase option */
514         case FILE_PLAYBACK:
515                 return filemode_Read;
516         case FILE_RECORD:
517                 return filemode_Write;
518         case FILE_LOAD_AUX:
519                 return filemode_Read;
520         case FILE_SAVE_AUX:
521                 return filemode_Write;
522         }
523         return filemode_ReadWrite;
524 }
525
526 frefid_t script_fref = NULL;
527 int script_exists = 0;
528
529 strid_t frotzopenprompt(int flag)
530 {
531         frefid_t fref;
532         strid_t stm;
533         glui32 gusage = flag2usage(flag);
534         glui32 gmode = flag2mode(flag);
535
536         fref = glk_fileref_create_by_prompt(gusage, gmode, 0);
537         if (fref == NULL)
538                 return NULL;
539
540         stm = glk_stream_open_file(fref, gmode, 0);
541
542         if (flag == FILE_SCRIPT)
543         {
544                 if (script_fref)
545                         glk_fileref_destroy(script_fref);
546                 script_fref = glk_fileref_create_from_fileref(gusage, fref, 0);
547                 script_exists = (script_fref != NULL);
548         }
549
550         glk_fileref_destroy(fref);
551
552         return stm;
553 }
554
555 strid_t frotzreopen(int flag)
556 {
557         frefid_t fref;
558
559         if (flag == FILE_SCRIPT && script_exists)
560                 fref = script_fref;
561         else
562                 return NULL;
563
564         strid_t stm;
565         glui32 gusage = flag2usage(flag);
566         glui32 gmode = flag2mode(flag);
567
568         stm = glk_stream_open_file(fref, gmode, 0);
569
570         return stm;
571 }
572
573 strid_t frotzopen(char *filename, int flag)
574 {
575         frefid_t fref;
576         strid_t stm;
577         glui32 gusage = flag2usage(flag);
578         glui32 gmode = flag2mode(flag);
579
580         fref = glk_fileref_create_by_name(gusage, filename, 0);
581         if (!fref)
582                 return NULL;
583
584         stm = glk_stream_open_file(fref, gmode, 0);
585
586         glk_fileref_destroy(fref);
587
588         return stm;
589 }
590