1 /******************************************************************************
3 * Copyright (C) 2006-2009 by Tor Andersson. *
5 * This file is part of Gargoyle. *
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. *
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. *
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 *
21 *****************************************************************************/
23 /* glkstuff.c -- non-screen related glk stuff */
27 #define VERSION "2.50"
29 int curr_status_ht = 0;
30 int mach_status_ht = 0;
32 winid_t gos_upper = NULL;
33 winid_t gos_lower = NULL;
34 winid_t gos_curwin = NULL;
36 int gos_linepending = 0;
37 zchar *gos_linebuf = NULL;
38 winid_t gos_linewin = NULL;
40 schanid_t gos_channel = NULL;
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"\
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"
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)
69 if (zoptind >= argc || argv[zoptind][0] != '-' || argv[zoptind][1] == 0)
71 zoptopt = argv[zoptind][pos++];
73 if (argv[zoptind][pos] == 0)
78 p = strchr (options, zoptopt);
79 if (zoptopt == ':' || p == NULL)
81 fputs ("illegal option -- ", stderr);
86 if (zoptind >= argc) {
87 fputs ("option requires an argument -- ", stderr);
90 zoptarg = argv[zoptind];
98 fputc (zoptopt, stderr);
103 static int user_random_seed = -1;
104 static int user_tandy_bit = 0;
105 static char *graphics_filename = NULL;
107 void os_process_arguments(int argc, char *argv[])
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");
120 /* Parse the options */
122 c = zgetopt(argc, argv, "aAioOPQs:S:tu:xZ:");
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;
145 if (((argc - zoptind) != 1) && ((argc - zoptind) != 2))
149 win = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
151 glk_put_string("FROTZ V" VERSION " -- Glk 0.7.0 interface.\n");
152 glk_put_string(INFORMATION);
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);
168 story_name = argv[zoptind++];
170 graphics_filename = argv[zoptind++];
173 s = strrchr(story_name, '\\');
174 if (!s) s = strrchr(story_name, '/');
175 garglk_set_story_name(s ? s + 1 : story_name);
180 void os_init_screen(void)
182 glui32 width, height;
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);
194 glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Proportional, 0);
195 glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Weight, 1);
196 glk_stylehint_set(wintype_AllTypes, style_Subheader, stylehint_Oblique, 0);
199 glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Proportional, 0);
200 glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Weight, 0);
201 glk_stylehint_set(wintype_AllTypes, style_Alert, stylehint_Oblique, 1);
204 glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Proportional, 0);
205 glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Weight, 1);
206 glk_stylehint_set(wintype_AllTypes, style_BlockQuote, stylehint_Oblique, 1);
209 glk_stylehint_set(wintype_TextBuffer, style_Normal, stylehint_Proportional, 1);
210 glk_stylehint_set(wintype_TextGrid, style_Normal, stylehint_Proportional, 0);
211 glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Weight, 0);
212 glk_stylehint_set(wintype_AllTypes, style_Normal, stylehint_Oblique, 0);
215 glk_stylehint_set(wintype_TextBuffer, style_Header, stylehint_Proportional, 1);
216 glk_stylehint_set(wintype_TextGrid, style_Header, stylehint_Proportional, 0);
217 glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Weight, 1);
218 glk_stylehint_set(wintype_AllTypes, style_Header, stylehint_Oblique, 0);
221 glk_stylehint_set(wintype_TextBuffer, style_Emphasized, stylehint_Proportional, 1);
222 glk_stylehint_set(wintype_TextGrid, style_Emphasized, stylehint_Proportional, 0);
223 glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Weight, 0);
224 glk_stylehint_set(wintype_AllTypes, style_Emphasized, stylehint_Oblique, 1);
227 glk_stylehint_set(wintype_TextBuffer, style_Note, stylehint_Proportional, 1);
228 glk_stylehint_set(wintype_TextGrid, style_Note, stylehint_Proportional, 0);
229 glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Weight, 1);
230 glk_stylehint_set(wintype_AllTypes, style_Note, stylehint_Oblique, 1);
232 gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
234 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
235 glk_window_get_size(gos_lower, &width, &height);
236 glk_window_close(gos_lower, NULL);
238 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
239 gos_upper = glk_window_open(gos_lower,
240 winmethod_Above | winmethod_Fixed,
242 wintype_TextGrid, 0);
246 glk_set_window(gos_lower);
247 gos_curwin = gos_lower;
250 * Icky magic bit setting
253 if (h_version == V3 && user_tandy_bit)
254 h_config |= CONFIG_TANDY;
256 if (h_version == V3 && gos_upper)
257 h_config |= CONFIG_SPLITSCREEN;
259 if (h_version == V3 && !gos_upper)
260 h_config |= CONFIG_NOSTATUSLINE;
263 h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
264 CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
267 h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
269 if ((h_version >= 5) && (h_flags & SOUND_FLAG))
270 h_flags |= SOUND_FLAG;
272 if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
273 h_flags |= OLD_SOUND_FLAG;
275 if ((h_version == 6) && (option_sound != 0))
276 h_config |= CONFIG_SOUND;
278 if (h_version >= V5 && (h_flags & UNDO_FLAG))
279 if (option_undo_slots == 0)
280 h_flags &= ~UNDO_FLAG;
282 h_screen_cols = width;
283 h_screen_rows = height;
285 h_screen_height = h_screen_rows;
286 h_screen_width = h_screen_cols;
291 /* Must be after screen dimensions are computed. */
292 if (h_version == V6) {
293 h_flags &= ~GRAPHICS_FLAG;
296 /* Use the ms-dos interpreter number for v6, because that's the
297 * kind of graphics files we understand. Otherwise, use DEC. */
298 h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
299 h_interpreter_version = 'F';
301 /* Set these per spec 8.3.2. */
302 h_default_foreground = WHITE_COLOUR;
303 h_default_background = BLACK_COLOUR;
304 if (h_flags & COLOUR_FLAG) h_flags &= ~COLOUR_FLAG;
308 int os_random_seed (void)
310 if (user_random_seed == -1)
311 /* Use the epoch as seed value */
312 return (time(0) & 0x7fff);
313 return user_random_seed;
316 void os_restart_game (int stage) {}
318 void os_fatal (const char *s)
324 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
326 glk_set_window(gos_lower);
327 glk_set_style(style_Normal);
328 glk_put_string("\n\nFatal error: ");
330 glk_put_string("\n");
334 void os_init_setup(void)
336 option_attribute_assignment = 0;
337 option_attribute_testing = 0;
338 option_context_lines = 0;
339 option_object_locating = 0;
340 option_object_movement = 0;
341 option_left_margin = 0;
342 option_right_margin = 0;
343 option_ignore_errors = 0;
345 option_undo_slots = MAX_UNDO_SLOTS;
346 option_expand_abbreviations = 0;
347 option_script_cols = 80;
348 option_save_quetzal = 1;
350 option_err_report_mode = ERR_DEFAULT_REPORT_MODE;
353 void gos_cancel_pending_line(void)
356 glk_cancel_line_event(gos_linewin, &ev);
357 gos_linebuf[ev.val1] = '\0';
361 zchar os_read_key (int timeout, bool show_cursor)
364 winid_t win = gos_curwin ? gos_curwin : gos_lower;
367 gos_cancel_pending_line();
369 glk_request_char_event_uni(win);
371 glk_request_timer_events(timeout * 100);
376 if (ev.type == evtype_Arrange) {
380 else if (ev.type == evtype_Timer)
382 glk_cancel_char_event(win);
383 glk_request_timer_events(0);
386 else if (ev.type == evtype_CharInput)
390 glk_request_timer_events(0);
392 if (gos_upper && mach_status_ht < curr_status_ht)
398 case keycode_Escape: return ZC_ESCAPE;
399 case keycode_PageUp: return ZC_ARROW_MIN;
400 case keycode_PageDown: return ZC_ARROW_MAX;
401 case keycode_Left: return ZC_ARROW_LEFT;
402 case keycode_Right: return ZC_ARROW_RIGHT;
403 case keycode_Up: return ZC_ARROW_UP;
404 case keycode_Down: return ZC_ARROW_DOWN;
405 case keycode_Return: return ZC_RETURN;
406 case keycode_Delete: return ZC_BACKSPACE;
407 case keycode_Tab: return ZC_INDENT;
413 zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued)
416 winid_t win = gos_curwin ? gos_curwin : gos_lower;
418 if (!continued && gos_linepending)
419 gos_cancel_pending_line();
421 if (!continued || !gos_linepending)
423 glk_request_line_event_uni(win, buf, max, os_string_length(buf));
425 glk_request_timer_events(timeout * 100);
433 if (ev.type == evtype_Arrange) {
437 else if (ev.type == evtype_Timer)
444 else if (ev.type == evtype_LineInput)
448 glk_request_timer_events(0);
451 if (gos_upper && mach_status_ht < curr_status_ht)
458 zword os_read_mouse(void)
460 /* NOT IMPLEMENTED */
464 void os_scrollback_char (zchar z)
466 /* NOT IMPLEMENTED */
469 void os_scrollback_erase (int amount)
471 /* NOT IMPLEMENTED */
474 static glui32 flag2usage(int flag)
479 return fileusage_SavedGame | fileusage_BinaryMode;
481 return fileusage_SavedGame | fileusage_BinaryMode;
483 return fileusage_Transcript | fileusage_TextMode;
485 return fileusage_InputRecord | fileusage_TextMode;
487 return fileusage_InputRecord | fileusage_TextMode;
489 return fileusage_Data | fileusage_BinaryMode;
491 return fileusage_Data | fileusage_BinaryMode;
496 static glui32 flag2mode(int flag)
501 return filemode_Read;
503 return filemode_Write;
505 return filemode_ReadWrite; /* append really, but with erase option */
507 return filemode_Read;
509 return filemode_Write;
511 return filemode_Read;
513 return filemode_Write;
515 return filemode_ReadWrite;
518 frefid_t script_fref = NULL;
519 int script_exists = 0;
521 strid_t frotzopenprompt(int flag)
525 glui32 gusage = flag2usage(flag);
526 glui32 gmode = flag2mode(flag);
528 fref = glk_fileref_create_by_prompt(gusage, gmode, 0);
532 stm = glk_stream_open_file(fref, gmode, 0);
534 if (flag == FILE_SCRIPT)
537 glk_fileref_destroy(script_fref);
538 script_fref = glk_fileref_create_from_fileref(gusage, fref, 0);
539 script_exists = (script_fref != NULL);
542 glk_fileref_destroy(fref);
547 strid_t frotzreopen(int flag)
551 if (flag == FILE_SCRIPT && script_exists)
557 glui32 gusage = flag2usage(flag);
558 glui32 gmode = flag2mode(flag);
560 stm = glk_stream_open_file(fref, gmode, 0);
565 strid_t frotzopen(char *filename, int flag)
569 glui32 gusage = flag2usage(flag);
570 glui32 gmode = flag2mode(flag);
572 fref = glk_fileref_create_by_name(gusage, filename, 0);
576 stm = glk_stream_open_file(fref, gmode, 0);
578 glk_fileref_destroy(fref);