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);
192 glk_stylehint_set(wintype_TextGrid, style_Preformatted, stylehint_ReverseColor, 1);
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);
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);
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);
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);
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);
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);
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);
240 gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
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);
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,
250 wintype_TextGrid, 0);
254 glk_set_window(gos_lower);
255 gos_curwin = gos_lower;
258 * Icky magic bit setting
261 if (h_version == V3 && user_tandy_bit)
262 h_config |= CONFIG_TANDY;
264 if (h_version == V3 && gos_upper)
265 h_config |= CONFIG_SPLITSCREEN;
267 if (h_version == V3 && !gos_upper)
268 h_config |= CONFIG_NOSTATUSLINE;
271 h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
272 CONFIG_FIXED | CONFIG_TIMEDINPUT | CONFIG_COLOUR;
275 h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
277 if ((h_version >= 5) && (h_flags & SOUND_FLAG))
278 h_flags |= SOUND_FLAG;
280 if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
281 h_flags |= OLD_SOUND_FLAG;
283 if ((h_version == 6) && (option_sound != 0))
284 h_config |= CONFIG_SOUND;
286 if (h_version >= V5 && (h_flags & UNDO_FLAG))
287 if (option_undo_slots == 0)
288 h_flags &= ~UNDO_FLAG;
290 h_screen_cols = width;
291 h_screen_rows = height;
293 h_screen_height = h_screen_rows;
294 h_screen_width = h_screen_cols;
299 /* Must be after screen dimensions are computed. */
300 if (h_version == V6) {
301 h_flags &= ~GRAPHICS_FLAG;
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';
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;
316 int os_random_seed (void)
318 if (user_random_seed == -1)
319 /* Use the epoch as seed value */
320 return (time(0) & 0x7fff);
321 return user_random_seed;
324 void os_restart_game (int stage) {}
326 void os_fatal (const char *s)
332 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
334 glk_set_window(gos_lower);
335 glk_set_style(style_Normal);
336 glk_put_string("\n\nFatal error: ");
338 glk_put_string("\n");
342 void os_init_setup(void)
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;
353 option_undo_slots = MAX_UNDO_SLOTS;
354 option_expand_abbreviations = 0;
355 option_script_cols = 80;
356 option_save_quetzal = 1;
358 option_err_report_mode = ERR_DEFAULT_REPORT_MODE;
361 void gos_cancel_pending_line(void)
364 glk_cancel_line_event(gos_linewin, &ev);
365 gos_linebuf[ev.val1] = '\0';
369 zchar os_read_key (int timeout, bool show_cursor)
372 winid_t win = gos_curwin ? gos_curwin : gos_lower;
375 gos_cancel_pending_line();
377 glk_request_char_event_uni(win);
379 glk_request_timer_events(timeout * 100);
384 if (ev.type == evtype_Arrange) {
388 else if (ev.type == evtype_Timer)
390 glk_cancel_char_event(win);
391 glk_request_timer_events(0);
394 else if (ev.type == evtype_CharInput)
398 glk_request_timer_events(0);
400 if (gos_upper && mach_status_ht < curr_status_ht)
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;
421 zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued)
424 winid_t win = gos_curwin ? gos_curwin : gos_lower;
426 if (!continued && gos_linepending)
427 gos_cancel_pending_line();
429 if (!continued || !gos_linepending)
431 glk_request_line_event_uni(win, buf, max, os_string_length(buf));
433 glk_request_timer_events(timeout * 100);
441 if (ev.type == evtype_Arrange) {
445 else if (ev.type == evtype_Timer)
452 else if (ev.type == evtype_LineInput)
456 glk_request_timer_events(0);
459 if (gos_upper && mach_status_ht < curr_status_ht)
466 zword os_read_mouse(void)
468 /* NOT IMPLEMENTED */
472 void os_scrollback_char (zchar z)
474 /* NOT IMPLEMENTED */
477 void os_scrollback_erase (int amount)
479 /* NOT IMPLEMENTED */
482 static glui32 flag2usage(int flag)
487 return fileusage_SavedGame | fileusage_BinaryMode;
489 return fileusage_SavedGame | fileusage_BinaryMode;
491 return fileusage_Transcript | fileusage_TextMode;
493 return fileusage_InputRecord | fileusage_TextMode;
495 return fileusage_InputRecord | fileusage_TextMode;
497 return fileusage_Data | fileusage_BinaryMode;
499 return fileusage_Data | fileusage_BinaryMode;
504 static glui32 flag2mode(int flag)
509 return filemode_Read;
511 return filemode_Write;
513 return filemode_ReadWrite; /* append really, but with erase option */
515 return filemode_Read;
517 return filemode_Write;
519 return filemode_Read;
521 return filemode_Write;
523 return filemode_ReadWrite;
526 frefid_t script_fref = NULL;
527 int script_exists = 0;
529 strid_t frotzopenprompt(int flag)
533 glui32 gusage = flag2usage(flag);
534 glui32 gmode = flag2mode(flag);
536 fref = glk_fileref_create_by_prompt(gusage, gmode, 0);
540 stm = glk_stream_open_file(fref, gmode, 0);
542 if (flag == FILE_SCRIPT)
545 glk_fileref_destroy(script_fref);
546 script_fref = glk_fileref_create_from_fileref(gusage, fref, 0);
547 script_exists = (script_fref != NULL);
550 glk_fileref_destroy(fref);
555 strid_t frotzreopen(int flag)
559 if (flag == FILE_SCRIPT && script_exists)
565 glui32 gusage = flag2usage(flag);
566 glui32 gmode = flag2mode(flag);
568 stm = glk_stream_open_file(fref, gmode, 0);
573 strid_t frotzopen(char *filename, int flag)
577 glui32 gusage = flag2usage(flag);
578 glui32 gmode = flag2mode(flag);
580 fref = glk_fileref_create_by_name(gusage, filename, 0);
584 stm = glk_stream_open_file(fref, gmode, 0);
586 glk_fileref_destroy(fref);