Implemented glk_select_poll() (Fix #18)
[projects/chimara/chimara.git] / interpreters / frotz / glkmisc.c
1 /* glkstuff.c -- non-screen related glk stuff */
2
3 #include "glkfrotz.h"
4
5 #define VERSION "2.43"
6
7 f_setup_t f_setup;
8
9 int curr_status_ht = 0;
10 int mach_status_ht = 0;
11
12 winid_t gos_upper = NULL;
13 winid_t gos_lower = NULL;
14 winid_t gos_curwin = NULL;
15
16 int gos_linepending = 0;
17 char *gos_linebuf = NULL;
18 winid_t gos_linewin = NULL;
19
20 schanid_t gos_channel = NULL;
21
22 #define INFORMATION ""\
23 "An interpreter for Infocom and other Z-Machine games.\n"\
24 "Complies with standard 1.0 of Graham Nelson's specification.\n"\
25 "Plays Z-code versions 1-5 and 8.\n"\
26 "\n"\
27 "Syntax: frotz [options] story-file\n"\
28 "    -a   watch attribute setting\n"\
29 "    -A   watch attribute testing\n"\
30 "    -i   ignore fatal errors\n"\
31 "    -I # interpreter number\n"\
32 "    -o   watch object movement\n"\
33 "    -O   watch object locating\n"\
34 "    -P   alter piracy opcode\n"\
35 "    -Q   use old-style save format\n"\
36 "    -s # random number seed value\n"\
37 "    -S # transscript width\n"\
38 "    -t   set Tandy bit\n"\
39 "    -u # slots for multiple undo\n"\
40 "    -x   expand abbreviations g/x/z\n"
41
42 /* A unix-like getopt, but with the names changed to avoid any problems.  */
43 static int zoptind = 1;
44 static int zoptopt = 0;
45 static char *zoptarg = NULL;
46 static int zgetopt (int argc, char *argv[], const char *options)
47 {
48         static int pos = 1;
49         const char *p;
50         if (zoptind >= argc || argv[zoptind][0] != '-' || argv[zoptind][1] == 0)
51                 return EOF;
52         zoptopt = argv[zoptind][pos++];
53         zoptarg = NULL;
54         if (argv[zoptind][pos] == 0)
55         {
56                 pos = 1;
57                 zoptind++;
58         }
59         p = strchr (options, zoptopt);
60         if (zoptopt == ':' || p == NULL)
61         {
62                 fputs ("illegal option -- ", stderr);
63                 goto error;
64         }
65         else if (p[1] == ':')
66         {
67                 if (zoptind >= argc) {
68                         fputs ("option requires an argument -- ", stderr);
69                         goto error;
70                 } else {
71                         zoptarg = argv[zoptind];
72                         if (pos != 1)
73                                 zoptarg += pos;
74                         pos = 1; zoptind++;
75                 }
76         }
77         return zoptopt;
78 error:
79         fputc (zoptopt, stderr);
80         fputc ('\n', stderr);
81         return '?';
82 }
83
84 static int user_random_seed = -1;
85 static int user_tandy_bit = 0;
86 static char *graphics_filename = NULL;
87
88 void os_process_arguments(int argc, char *argv[]) 
89 {
90         int c;
91
92 #ifdef GARGLK
93         garglk_set_program_name("Frotz " VERSION);
94         garglk_set_program_info(
95                         "Glk Frotz " VERSION "\n"
96                         "Original Frotz by Stefan Jokisch\n"
97                         "Unix port by Jim Dunleavy and David Griffith\n"
98                         "Glk port by Tor Andersson\n");
99 #endif
100
101         /* Parse the options */
102         do {
103                 c = zgetopt(argc, argv, "aAiI:oOPQs:S:tu:xZ:");
104                 switch (c)
105                 {
106                         case 'a': f_setup.attribute_assignment = 1; break;
107                         case 'A': f_setup.attribute_testing = 1; break;
108                         case 'i': f_setup.ignore_errors = 1; break;
109                         case 'I': f_setup.interpreter_number = atoi(zoptarg); break;
110                         case 'o': f_setup.object_movement = 1; break;
111                         case 'O': f_setup.object_locating = 1; break;
112                         case 'P': f_setup.piracy = 1; break;
113                         case 'Q': f_setup.save_quetzal = 0; break;
114                         case 's': user_random_seed = atoi(zoptarg); break;
115                         case 'S': f_setup.script_cols = atoi(zoptarg); break;
116                         case 't': user_tandy_bit = 1; break;
117                         case 'u': f_setup.undo_slots = atoi(zoptarg); break;
118                         case 'x': f_setup.expand_abbreviations = 1; break;
119                         case 'Z': f_setup.err_report_mode = atoi(zoptarg);
120                                           if ((f_setup.err_report_mode < ERR_REPORT_NEVER) ||
121                                                           (f_setup.err_report_mode > ERR_REPORT_FATAL))
122                                                   f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE;
123                                           break;
124                 }
125         } while (c != EOF);
126
127         if (((argc - zoptind) != 1) && ((argc - zoptind) != 2))
128         {
129                 winid_t win;
130                 char buf[256];
131                 win = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
132                 glk_set_window(win);
133                 glk_put_string("FROTZ V" VERSION " -- Glk 0.6.1 interface.\n");
134                 glk_put_string(INFORMATION);
135                 sprintf(buf,
136                                 "    -Z # error checking mode (default = %d)\n"
137                                 "         %d = don't report errors.  "
138                                 "%d = report first error.\n"
139                                 "         %d = report all errors.  "
140                                 "%d = exit after any error.\n",
141                                 ERR_DEFAULT_REPORT_MODE, ERR_REPORT_NEVER,
142                                 ERR_REPORT_ONCE, ERR_REPORT_ALWAYS, ERR_REPORT_FATAL);
143                 glk_put_string(buf);
144                 glk_exit();
145         }
146         else
147         {
148                 char *s;
149
150                 story_name = argv[zoptind++];
151                 if (zoptind < argc)
152                         graphics_filename = argv[zoptind++];
153
154                 #ifdef GARGLK
155                 s = strrchr(story_name, '\\');
156                 if (!s) s = strrchr(story_name, '/');
157                 garglk_set_story_name(s ? s + 1 : story_name);
158                 #endif
159         }
160 }
161
162 void os_init_screen(void)
163 {
164         glui32 width, height;
165
166         /*
167          * Init glk stuff
168          */
169
170         glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1);
171
172
173         gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
174         if (!gos_lower)
175                 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
176         glk_window_get_size(gos_lower, &width, &height);
177         glk_window_close(gos_lower, NULL);
178
179         gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
180         gos_upper = glk_window_open(gos_lower,
181                         winmethod_Above | winmethod_Fixed,
182                         0,
183                         wintype_TextGrid, 0);
184
185         gos_channel = NULL;
186
187         glk_set_window(gos_lower);
188         gos_curwin = gos_lower;
189
190         /*
191          * Icky magic bit setting
192          */
193
194         if (h_version == V3 && user_tandy_bit)
195                 h_config |= CONFIG_TANDY;
196
197         if (h_version == V3 && gos_upper)
198                 h_config |= CONFIG_SPLITSCREEN;
199
200         if (h_version == V3 && !gos_upper)
201                 h_config |= CONFIG_NOSTATUSLINE;
202
203         if (h_version >= V4)
204                 h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
205                         CONFIG_FIXED | CONFIG_TIMEDINPUT;
206
207         if (h_version >= V5)
208                 h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
209
210         if ((h_version >= 5) && (h_flags & SOUND_FLAG))
211                 h_flags |= SOUND_FLAG;
212
213         if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
214                 h_flags |= OLD_SOUND_FLAG;
215
216         if ((h_version == 6) && (f_setup.sound != 0)) 
217                 h_config |= CONFIG_SOUND;
218
219         if (h_version >= V5 && (h_flags & UNDO_FLAG))
220                 if (f_setup.undo_slots == 0)
221                         h_flags &= ~UNDO_FLAG;
222
223         h_screen_cols = width;
224         h_screen_rows = height;
225
226         h_screen_height = h_screen_rows;
227         h_screen_width = h_screen_cols;
228
229         h_font_width = 1;
230         h_font_height = 1;
231
232         /* Must be after screen dimensions are computed.  */
233         if (h_version == V6) {
234                 h_flags &= ~GRAPHICS_FLAG;
235         }
236
237         /* Use the ms-dos interpreter number for v6, because that's the
238          * kind of graphics files we understand.  Otherwise, use DEC.  */
239         h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
240         if (f_setup.interpreter_number > 0)
241                 h_interpreter_number = f_setup.interpreter_number;
242         h_interpreter_version = 'F';
243         {
244                 /* Set these per spec 8.3.2. */
245                 h_default_foreground = WHITE_COLOUR;
246                 h_default_background = BLACK_COLOUR;
247                 if (h_flags & COLOUR_FLAG) h_flags &= ~COLOUR_FLAG;
248         }
249 }
250
251 int os_random_seed (void)
252 {
253     if (user_random_seed == -1)
254         /* Use the epoch as seed value */
255         return (time(0) & 0x7fff);
256     return user_random_seed;
257 }
258
259 void os_restart_game (int stage) {}
260
261 void os_fatal (char *s)
262 {
263         if (!gos_lower)
264                 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
265
266         glk_set_window(gos_lower);
267         glk_set_style(style_Normal);
268         glk_put_string("\n\nFatal error: ");
269         glk_put_string(s);
270         glk_put_string("\n");
271         glk_exit();
272 }
273
274 void os_init_setup(void)
275 {
276         f_setup.attribute_assignment = 0;
277         f_setup.attribute_testing = 0;
278         f_setup.context_lines = 0;
279         f_setup.object_locating = 0;
280         f_setup.object_movement = 0;
281         f_setup.left_margin = 0;
282         f_setup.right_margin = 0;
283         f_setup.ignore_errors = 0;
284         f_setup.interpreter_number = 0;
285         f_setup.piracy = 0;
286         f_setup.undo_slots = MAX_UNDO_SLOTS;
287         f_setup.expand_abbreviations = 0;
288         f_setup.script_cols = 80;
289         f_setup.save_quetzal = 1;
290         f_setup.sound = 1;
291         f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE;
292 }
293
294 void gos_cancel_pending_line(void)
295 {
296         event_t ev;
297         glk_cancel_line_event(gos_linewin, &ev);
298         gos_linebuf[ev.val1] = '\0';
299         gos_linepending = 0;
300 }
301
302 zchar os_read_key (int timeout, bool show_cursor)
303 {
304         event_t ev;
305         winid_t win = gos_curwin ? gos_curwin : gos_lower;
306
307         if (gos_linepending)
308                 gos_cancel_pending_line();
309
310         glk_request_char_event(win);
311         if (timeout != 0)
312                 glk_request_timer_events(timeout * 100);
313
314         while (1)
315         {
316                 glk_select(&ev);
317                 if (ev.type == evtype_Arrange) {
318                         gos_update_height();
319                         gos_update_width();
320                 }
321                 else if (ev.type == evtype_Timer)
322                 {
323                         glk_cancel_char_event(win);
324                         glk_request_timer_events(0);
325                         return ZC_TIME_OUT;
326                 }
327                 else if (ev.type == evtype_CharInput)
328                         break;
329         }
330
331         glk_request_timer_events(0);
332
333         if (gos_upper && mach_status_ht < curr_status_ht)
334                 reset_status_ht();
335         curr_status_ht = 0;
336
337         switch (ev.val1)
338         {
339                 case keycode_Escape: return ZC_ESCAPE;
340                 case keycode_PageUp: return ZC_ARROW_MIN;
341                 case keycode_PageDown: return ZC_ARROW_MAX;
342                 case keycode_Left: return ZC_ARROW_LEFT;
343                 case keycode_Right: return ZC_ARROW_RIGHT;
344                 case keycode_Up: return ZC_ARROW_UP;
345                 case keycode_Down: return ZC_ARROW_DOWN;
346                 case keycode_Return: return ZC_RETURN;
347                 case keycode_Delete: return ZC_BACKSPACE;
348                 case keycode_Tab: return ZC_INDENT;
349                 default:
350                         return ev.val1;
351         }
352 }
353
354 zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued)
355 {
356         event_t ev;
357         winid_t win = gos_curwin ? gos_curwin : gos_lower;
358
359         if (!continued && gos_linepending)
360                 gos_cancel_pending_line(); 
361
362         if (!continued || !gos_linepending)
363         {
364                 glk_request_line_event(win, buf, max - 1, strlen(buf));
365                 if (timeout != 0)
366                         glk_request_timer_events(timeout * 100);
367         }
368
369         gos_linepending = 0;
370
371         while (1)
372         {
373                 glk_select(&ev);
374                 if (ev.type == evtype_Arrange) {
375                         gos_update_height();
376                         gos_update_width();
377                 }
378                 else if (ev.type == evtype_Timer)
379                 {
380                         gos_linewin = win;
381                         gos_linepending = 1;
382                         gos_linebuf = buf;
383                         return ZC_TIME_OUT;
384                 }
385                 else if (ev.type == evtype_LineInput)
386                         break;
387         }
388
389         glk_request_timer_events(0);
390         buf[ev.val1] = '\0';
391
392         if (gos_upper && mach_status_ht < curr_status_ht)
393                 reset_status_ht();
394         curr_status_ht = 0;
395
396         return ZC_RETURN;
397 }
398
399 zword os_read_mouse(void)
400 {
401         /* NOT IMPLEMENTED */
402         return 0;
403 }
404
405 static glui32 flag2usage(int flag)
406 {
407         switch (flag)
408         {       
409                 case FILE_RESTORE:
410                         return fileusage_SavedGame | fileusage_BinaryMode;
411                 case FILE_SAVE:
412                         return fileusage_SavedGame | fileusage_BinaryMode;
413                 case FILE_SCRIPT:
414                         return fileusage_Transcript | fileusage_TextMode;
415                 case FILE_PLAYBACK:
416                         return fileusage_InputRecord | fileusage_TextMode;
417                 case FILE_RECORD:
418                         return fileusage_InputRecord | fileusage_TextMode;
419                 case FILE_LOAD_AUX:
420                         return fileusage_Data | fileusage_BinaryMode;
421                 case FILE_SAVE_AUX:
422                         return fileusage_Data | fileusage_BinaryMode;
423         }
424         return 0;
425 }
426
427 static glui32 flag2mode(int flag)
428 {
429         switch (flag)
430         {       
431         case FILE_RESTORE:
432                 return filemode_Read;
433         case FILE_SAVE:
434                 return filemode_Write;
435         case FILE_SCRIPT:
436                 return filemode_ReadWrite;      /* append really, but with erase option */
437         case FILE_PLAYBACK:
438                 return filemode_Read;
439         case FILE_RECORD:
440                 return filemode_Write;
441         case FILE_LOAD_AUX:
442                 return filemode_Read;
443         case FILE_SAVE_AUX:
444                 return filemode_Write;
445         }
446         return filemode_ReadWrite;
447 }
448
449 strid_t frotzopenprompt(int flag)
450 {
451         frefid_t fref;
452         strid_t stm;
453         glui32 gusage = flag2usage(flag);
454         glui32 gmode = flag2mode(flag);
455
456         fref = glk_fileref_create_by_prompt(gusage, gmode, 0);
457         if (fref == NULL)
458                 return NULL;
459
460         stm = glk_stream_open_file(fref, gmode, 0);
461
462         glk_fileref_destroy(fref);
463
464         return stm;
465 }
466
467 strid_t frotzopen(char *filename, int flag)
468 {
469         frefid_t fref;
470         strid_t stm;
471         glui32 gusage = flag2usage(flag);
472         glui32 gmode = flag2mode(flag);
473
474         fref = glk_fileref_create_by_name(gusage, filename, 0);
475         if (!fref)
476                 return NULL;
477
478         stm = glk_stream_open_file(fref, gmode, 0);
479
480         glk_fileref_destroy(fref);
481
482         return stm;
483 }
484