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);