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.43"
31 int curr_status_ht = 0;
32 int mach_status_ht = 0;
34 winid_t gos_upper = NULL;
35 winid_t gos_lower = NULL;
36 winid_t gos_curwin = NULL;
38 int gos_linepending = 0;
39 char *gos_linebuf = NULL;
40 winid_t gos_linewin = NULL;
42 schanid_t gos_channel = NULL;
44 #define INFORMATION ""\
45 "An interpreter for Infocom and other Z-Machine games.\n"\
46 "Complies with standard 1.0 of Graham Nelson's specification.\n"\
47 "Plays Z-code versions 1-5 and 8.\n"\
49 "Syntax: frotz [options] story-file\n"\
50 " -a watch attribute setting\n"\
51 " -A watch attribute testing\n"\
52 " -i ignore fatal errors\n"\
53 " -I # interpreter number\n"\
54 " -o watch object movement\n"\
55 " -O watch object locating\n"\
56 " -P alter piracy opcode\n"\
57 " -Q use old-style save format\n"\
58 " -s # random number seed value\n"\
59 " -S # transscript width\n"\
60 " -t set Tandy bit\n"\
61 " -u # slots for multiple undo\n"\
62 " -x expand abbreviations g/x/z\n"
64 /* A unix-like getopt, but with the names changed to avoid any problems. */
65 static int zoptind = 1;
66 static int zoptopt = 0;
67 static char *zoptarg = NULL;
68 static int zgetopt (int argc, char *argv[], const char *options)
72 if (zoptind >= argc || argv[zoptind][0] != '-' || argv[zoptind][1] == 0)
74 zoptopt = argv[zoptind][pos++];
76 if (argv[zoptind][pos] == 0)
81 p = strchr (options, zoptopt);
82 if (zoptopt == ':' || p == NULL)
84 fputs ("illegal option -- ", stderr);
89 if (zoptind >= argc) {
90 fputs ("option requires an argument -- ", stderr);
93 zoptarg = argv[zoptind];
101 fputc (zoptopt, stderr);
102 fputc ('\n', stderr);
106 static int user_random_seed = -1;
107 static int user_tandy_bit = 0;
108 static char *graphics_filename = NULL;
110 void os_process_arguments(int argc, char *argv[])
115 garglk_set_program_name("Frotz " VERSION);
116 garglk_set_program_info(
117 "Glk Frotz " VERSION "\n"
118 "Original Frotz by Stefan Jokisch\n"
119 "Unix port by Jim Dunleavy and David Griffith\n"
120 "Glk port by Tor Andersson\n");
123 /* Parse the options */
125 c = zgetopt(argc, argv, "aAiI:oOPQs:S:tu:xZ:");
128 case 'a': f_setup.attribute_assignment = 1; break;
129 case 'A': f_setup.attribute_testing = 1; break;
130 case 'i': f_setup.ignore_errors = 1; break;
131 case 'I': f_setup.interpreter_number = atoi(zoptarg); break;
132 case 'o': f_setup.object_movement = 1; break;
133 case 'O': f_setup.object_locating = 1; break;
134 case 'P': f_setup.piracy = 1; break;
135 case 'Q': f_setup.save_quetzal = 0; break;
136 case 's': user_random_seed = atoi(zoptarg); break;
137 case 'S': f_setup.script_cols = atoi(zoptarg); break;
138 case 't': user_tandy_bit = 1; break;
139 case 'u': f_setup.undo_slots = atoi(zoptarg); break;
140 case 'x': f_setup.expand_abbreviations = 1; break;
141 case 'Z': f_setup.err_report_mode = atoi(zoptarg);
142 if ((f_setup.err_report_mode < ERR_REPORT_NEVER) ||
143 (f_setup.err_report_mode > ERR_REPORT_FATAL))
144 f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE;
149 if (((argc - zoptind) != 1) && ((argc - zoptind) != 2))
153 win = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
155 glk_put_string("FROTZ V" VERSION " -- Glk 0.6.1 interface.\n");
156 glk_put_string(INFORMATION);
158 " -Z # error checking mode (default = %d)\n"
159 " %d = don't report errors. "
160 "%d = report first error.\n"
161 " %d = report all errors. "
162 "%d = exit after any error.\n",
163 ERR_DEFAULT_REPORT_MODE, ERR_REPORT_NEVER,
164 ERR_REPORT_ONCE, ERR_REPORT_ALWAYS, ERR_REPORT_FATAL);
172 story_name = argv[zoptind++];
174 graphics_filename = argv[zoptind++];
177 s = strrchr(story_name, '\\');
178 if (!s) s = strrchr(story_name, '/');
179 garglk_set_story_name(s ? s + 1 : story_name);
184 void os_init_screen(void)
186 glui32 width, height;
192 glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1);
195 gos_lower = glk_window_open(0, 0, 0, wintype_TextGrid, 0);
197 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
198 glk_window_get_size(gos_lower, &width, &height);
199 glk_window_close(gos_lower, NULL);
201 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
202 gos_upper = glk_window_open(gos_lower,
203 winmethod_Above | winmethod_Fixed,
205 wintype_TextGrid, 0);
209 glk_set_window(gos_lower);
210 gos_curwin = gos_lower;
213 * Icky magic bit setting
216 if (h_version == V3 && user_tandy_bit)
217 h_config |= CONFIG_TANDY;
219 if (h_version == V3 && gos_upper)
220 h_config |= CONFIG_SPLITSCREEN;
222 if (h_version == V3 && !gos_upper)
223 h_config |= CONFIG_NOSTATUSLINE;
226 h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS |
227 CONFIG_FIXED | CONFIG_TIMEDINPUT;
230 h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG);
232 if ((h_version >= 5) && (h_flags & SOUND_FLAG))
233 h_flags |= SOUND_FLAG;
235 if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG))
236 h_flags |= OLD_SOUND_FLAG;
238 if ((h_version == 6) && (f_setup.sound != 0))
239 h_config |= CONFIG_SOUND;
241 if (h_version >= V5 && (h_flags & UNDO_FLAG))
242 if (f_setup.undo_slots == 0)
243 h_flags &= ~UNDO_FLAG;
245 h_screen_cols = width;
246 h_screen_rows = height;
248 h_screen_height = h_screen_rows;
249 h_screen_width = h_screen_cols;
254 /* Must be after screen dimensions are computed. */
255 if (h_version == V6) {
256 h_flags &= ~GRAPHICS_FLAG;
259 /* Use the ms-dos interpreter number for v6, because that's the
260 * kind of graphics files we understand. Otherwise, use DEC. */
261 h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20;
262 if (f_setup.interpreter_number > 0)
263 h_interpreter_number = f_setup.interpreter_number;
264 h_interpreter_version = 'F';
266 /* Set these per spec 8.3.2. */
267 h_default_foreground = WHITE_COLOUR;
268 h_default_background = BLACK_COLOUR;
269 if (h_flags & COLOUR_FLAG) h_flags &= ~COLOUR_FLAG;
273 int os_random_seed (void)
275 if (user_random_seed == -1)
276 /* Use the epoch as seed value */
277 return (time(0) & 0x7fff);
278 return user_random_seed;
281 void os_restart_game (int stage) {}
283 void os_fatal (char *s)
286 gos_lower = glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
288 glk_set_window(gos_lower);
289 glk_set_style(style_Normal);
290 glk_put_string("\n\nFatal error: ");
292 glk_put_string("\n");
296 void os_init_setup(void)
298 f_setup.attribute_assignment = 0;
299 f_setup.attribute_testing = 0;
300 f_setup.context_lines = 0;
301 f_setup.object_locating = 0;
302 f_setup.object_movement = 0;
303 f_setup.left_margin = 0;
304 f_setup.right_margin = 0;
305 f_setup.ignore_errors = 0;
306 f_setup.interpreter_number = 0;
308 f_setup.undo_slots = MAX_UNDO_SLOTS;
309 f_setup.expand_abbreviations = 0;
310 f_setup.script_cols = 80;
311 f_setup.save_quetzal = 1;
313 f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE;
316 void gos_cancel_pending_line(void)
319 glk_cancel_line_event(gos_linewin, &ev);
320 gos_linebuf[ev.val1] = '\0';
324 zchar os_read_key (int timeout, bool show_cursor)
327 winid_t win = gos_curwin ? gos_curwin : gos_lower;
330 gos_cancel_pending_line();
332 glk_request_char_event(win);
334 glk_request_timer_events(timeout * 100);
339 if (ev.type == evtype_Arrange) {
343 else if (ev.type == evtype_Timer)
345 glk_cancel_char_event(win);
346 glk_request_timer_events(0);
349 else if (ev.type == evtype_CharInput)
353 glk_request_timer_events(0);
355 if (gos_upper && mach_status_ht < curr_status_ht)
361 case keycode_Escape: return ZC_ESCAPE;
362 case keycode_PageUp: return ZC_ARROW_MIN;
363 case keycode_PageDown: return ZC_ARROW_MAX;
364 case keycode_Left: return ZC_ARROW_LEFT;
365 case keycode_Right: return ZC_ARROW_RIGHT;
366 case keycode_Up: return ZC_ARROW_UP;
367 case keycode_Down: return ZC_ARROW_DOWN;
368 case keycode_Return: return ZC_RETURN;
369 case keycode_Delete: return ZC_BACKSPACE;
370 case keycode_Tab: return ZC_INDENT;
376 zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued)
379 winid_t win = gos_curwin ? gos_curwin : gos_lower;
381 if (!continued && gos_linepending)
382 gos_cancel_pending_line();
384 if (!continued || !gos_linepending)
386 glk_request_line_event(win, buf, max, strlen(buf));
388 glk_request_timer_events(timeout * 100);
396 if (ev.type == evtype_Arrange) {
400 else if (ev.type == evtype_Timer)
407 else if (ev.type == evtype_LineInput)
411 glk_request_timer_events(0);
414 if (gos_upper && mach_status_ht < curr_status_ht)
421 zword os_read_mouse(void)
423 /* NOT IMPLEMENTED */
427 static glui32 flag2usage(int flag)
432 return fileusage_SavedGame | fileusage_BinaryMode;
434 return fileusage_SavedGame | fileusage_BinaryMode;
436 return fileusage_Transcript | fileusage_TextMode;
438 return fileusage_InputRecord | fileusage_TextMode;
440 return fileusage_InputRecord | fileusage_TextMode;
442 return fileusage_Data | fileusage_BinaryMode;
444 return fileusage_Data | fileusage_BinaryMode;
449 static glui32 flag2mode(int flag)
454 return filemode_Read;
456 return filemode_Write;
458 return filemode_ReadWrite; /* append really, but with erase option */
460 return filemode_Read;
462 return filemode_Write;
464 return filemode_Read;
466 return filemode_Write;
468 return filemode_ReadWrite;
471 strid_t frotzopenprompt(int flag)
475 glui32 gusage = flag2usage(flag);
476 glui32 gmode = flag2mode(flag);
478 fref = glk_fileref_create_by_prompt(gusage, gmode, 0);
482 stm = glk_stream_open_file(fref, gmode, 0);
484 glk_fileref_destroy(fref);
489 strid_t frotzopen(char *filename, int flag)
493 glui32 gusage = flag2usage(flag);
494 glui32 gmode = flag2mode(flag);
496 fref = glk_fileref_create_by_name(gusage, filename, 0);
500 stm = glk_stream_open_file(fref, gmode, 0);
502 glk_fileref_destroy(fref);