forked from erkyrath/glulxe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
unixstrt.c
307 lines (269 loc) · 9.46 KB
/
unixstrt.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/* unixstrt.c: Unix-specific code for Glulxe.
Designed by Andrew Plotkin <erkyrath@eblong.com>
http://eblong.com/zarf/glulx/index.html
*/
#include <stdlib.h>
#include <string.h>
#include "glk.h"
#include "gi_blorb.h"
#include "glulxe.h"
#include "unixstrt.h"
#include "glkstart.h" /* This comes with the Glk library. */
#if VM_DEBUGGER
/* This header file may come with the Glk library. If it doesn't, comment
out VM_DEBUGGER in glulxe.h -- you won't be able to use debugging. */
#include "gi_debug.h"
#endif /* VM_DEBUGGER */
static void glkunix_game_select(glui32 selector, glui32 arg0, glui32 arg1, glui32 arg2);
static void glkunix_game_start(void);
static void glkunix_game_autorestore(void);
/* The only command-line arguments are the filename and the number of
undo states. And the profiling switch, if that's compiled in. The
only *three* command-line arguments are...
You may wonder why there's no argument for a save file to autorestore
at startup. That would be nice; unfortunately it can't work. A Glulx
game expects to set up its Glk environment (@setiosys, open windows,
etc) before handling a "restore" command. It can't pick up from a
restored state without that environment in place.
*/
glkunix_argumentlist_t glkunix_arguments[] = {
{ "--undo", glkunix_arg_ValueFollows, "Number of undo states to store." },
#if GLKUNIX_AUTOSAVE_FEATURES
{ "--autosave", glkunix_arg_NoValue, "Autosave every turn." },
{ "--autorestore", glkunix_arg_NoValue, "Autorestore at launch." },
{ "--autodir", glkunix_arg_ValueFollows, "Directory for autosave/restore files (default: .)." },
{ "--autoname", glkunix_arg_ValueFollows, "Base filename for autosave/restore (default: autosave)." },
{ "--autoskiparrange", glkunix_arg_NoValue, "Don't autosave on arrange events." },
#endif /* GLKUNIX_AUTOSAVE_FEATURES */
#if VM_PROFILING
{ "--profile", glkunix_arg_ValueFollows, "Generate profiling information to a file." },
{ "--profcalls", glkunix_arg_NoValue, "Include what-called-what details in profiling. (Slow!)" },
#endif /* VM_PROFILING */
#if VM_DEBUGGER
{ "--gameinfo", glkunix_arg_ValueFollows, "Read debug information from a file." },
{ "--cpu", glkunix_arg_NoValue, "Display CPU usage of each command (debug)." },
{ "--starttrap", glkunix_arg_NoValue, "Enter debug mode at startup time (debug)." },
{ "--quittrap", glkunix_arg_NoValue, "Enter debug mode at quit time (debug)." },
{ "--crashtrap", glkunix_arg_NoValue, "Enter debug mode on any fatal error (debug)." },
#endif /* VM_DEBUGGER */
{ "", glkunix_arg_ValueFollows, "filename: The game file to load." },
{ NULL, glkunix_arg_End, NULL }
};
int glkunix_startup_code(glkunix_startup_t *data)
{
/* It turns out to be more convenient if we return TRUE from here, even
when an error occurs, and display an error in glk_main(). */
int ix;
char *filename = NULL;
char *gameinfofilename = NULL;
int gameinfoloaded = FALSE;
int pref_autosave = FALSE;
int pref_autorestore = FALSE;
unsigned char buf[12];
int res;
/* Parse out the arguments. They've already been checked for validity,
and the library-specific ones stripped out.
As usual for Unix, the zeroth argument is the executable name. */
for (ix=1; ix<data->argc; ix++) {
if (!strcmp(data->argv[ix], "--undo")) {
ix++;
if (ix<data->argc) {
int val = atoi(data->argv[ix]);
if (val <= 0) {
init_err = "--undo must be a number.";
return TRUE;
}
max_undo_level = val;
}
continue;
}
#if GLKUNIX_AUTOSAVE_FEATURES
if (!strcmp(data->argv[ix], "--autosave")) {
pref_autosave = TRUE;
continue;
}
if (!strcmp(data->argv[ix], "--autorestore")) {
pref_autorestore = TRUE;
continue;
}
if (!strcmp(data->argv[ix], "--autodir")) {
ix++;
if (ix<data->argc) {
pref_autosavedir = data->argv[ix];
}
continue;
}
if (!strcmp(data->argv[ix], "--autoname")) {
ix++;
if (ix<data->argc) {
pref_autosavename = data->argv[ix];
}
continue;
}
if (!strcmp(data->argv[ix], "--autoskiparrange")) {
pref_autosave_skiparrange = TRUE;
continue;
}
#endif /* GLKUNIX_AUTOSAVE_FEATURES */
#if VM_PROFILING
if (!strcmp(data->argv[ix], "--profile")) {
ix++;
if (ix<data->argc) {
strid_t profstr = glkunix_stream_open_pathname_gen(data->argv[ix], TRUE, FALSE, 1);
if (!profstr) {
init_err = "Unable to open profile output file.";
init_err2 = data->argv[ix];
return TRUE;
}
setup_profile(profstr, NULL);
}
continue;
}
if (!strcmp(data->argv[ix], "--profcalls")) {
profile_set_call_counts(TRUE);
continue;
}
#endif /* VM_PROFILING */
#if VM_DEBUGGER
if (!strcmp(data->argv[ix], "--gameinfo")) {
ix++;
if (ix<data->argc) {
gameinfofilename = data->argv[ix];
}
continue;
}
if (!strcmp(data->argv[ix], "--cpu")) {
debugger_track_cpu(TRUE);
continue;
}
if (!strcmp(data->argv[ix], "--starttrap")) {
debugger_set_start_trap(TRUE);
continue;
}
if (!strcmp(data->argv[ix], "--quittrap")) {
debugger_set_quit_trap(TRUE);
continue;
}
if (!strcmp(data->argv[ix], "--crashtrap")) {
debugger_set_crash_trap(TRUE);
continue;
}
#endif /* VM_DEBUGGER */
if (filename) {
init_err = "You must supply exactly one game file.";
return TRUE;
}
filename = data->argv[ix];
}
if (!filename) {
init_err = "You must supply the name of a game file.";
return TRUE;
}
gamefile = glkunix_stream_open_pathname(filename, FALSE, 1);
if (!gamefile) {
init_err = "The game file could not be opened.";
init_err2 = filename;
return TRUE;
}
#if GLKUNIX_AUTOSAVE_FEATURES
if (pref_autosave || pref_autorestore) {
set_library_start_hook(glkunix_game_start);
if (pref_autorestore)
set_library_autorestore_hook(glkunix_game_autorestore);
if (pref_autosave)
set_library_select_hook(glkunix_game_select);
}
#endif /* GLKUNIX_AUTOSAVE_FEATURES */
#if VM_DEBUGGER
if (gameinfofilename) {
strid_t debugstr = glkunix_stream_open_pathname_gen(gameinfofilename, FALSE, FALSE, 1);
if (!debugstr) {
nonfatal_warning("Unable to open gameinfo file for debug data.");
}
else {
int bres = debugger_load_info_stream(debugstr);
glk_stream_close(debugstr, NULL);
if (!bres)
nonfatal_warning("Unable to parse game info.");
else
gameinfoloaded = TRUE;
}
}
/* Report debugging available, whether a game info file is loaded or not. */
gidebug_debugging_available(debugger_cmd_handler, debugger_cycle_handler);
#endif /* VM_DEBUGGER */
/* Now we have to check to see if it's a Blorb file. */
glk_stream_set_position(gamefile, 0, seekmode_Start);
res = glk_get_buffer_stream(gamefile, (char *)buf, 12);
if (!res) {
init_err = "The data in this stand-alone game is too short to read.";
return TRUE;
}
if (buf[0] == 'G' && buf[1] == 'l' && buf[2] == 'u' && buf[3] == 'l') {
/* Load game directly from file. */
locate_gamefile(FALSE);
return TRUE;
}
else if (buf[0] == 'F' && buf[1] == 'O' && buf[2] == 'R' && buf[3] == 'M'
&& buf[8] == 'I' && buf[9] == 'F' && buf[10] == 'R' && buf[11] == 'S') {
/* Load game from a chunk in the Blorb file. */
locate_gamefile(TRUE);
#if VM_DEBUGGER
/* Load the debug info from the Blorb, if it wasn't loaded from a file. */
if (!gameinfoloaded) {
glui32 giblorb_ID_Dbug = giblorb_make_id('D', 'b', 'u', 'g');
giblorb_err_t err;
giblorb_result_t blorbres;
err = giblorb_load_chunk_by_type(giblorb_get_resource_map(),
giblorb_method_FilePos,
&blorbres, giblorb_ID_Dbug, 0);
if (!err) {
int bres = debugger_load_info_chunk(gamefile, blorbres.data.startpos, blorbres.length);
if (!bres)
nonfatal_warning("Unable to parse game info.");
else
gameinfoloaded = TRUE;
}
}
#endif /* VM_DEBUGGER */
return TRUE;
}
else {
init_err = "This is neither a Glulx game file nor a Blorb file "
"which contains one.";
return TRUE;
}
}
/* The following only make sense when compiled with a Glk library which offers autosave/autorestore hooks. */
#ifdef GLKUNIX_AUTOSAVE_FEATURES
static void glkunix_game_start()
{
unsigned char buf[64];
glk_stream_set_position(gamefile, gamefile_start, seekmode_Start);
glui32 res = glk_get_buffer_stream(gamefile, (char *)buf, 64);
if (res == 0) {
fatal_error("Unable to read game file.");
return;
}
glkunix_set_autosave_signature(buf, res);
}
/* This is the library_select_hook, which will be called every time glk_select() is invoked.
*/
static void glkunix_game_select(glui32 selector, glui32 arg0, glui32 arg1, glui32 arg2)
{
glui32 lasteventtype = glkunix_get_last_event_type();
/* Do not autosave if we've just started up or autorestored, or if the last event was a rearrange event. (We get rearranges in clusters, and they don't change anything interesting anyhow.) */
if (lasteventtype == 0xFFFFFFFF
|| lasteventtype == 0xFFFFFFFE)
return;
if (pref_autosave_skiparrange && lasteventtype == evtype_Arrange)
return;
glkunix_do_autosave(selector, arg0, arg1, arg2);
}
static void glkunix_game_autorestore()
{
int res = glkunix_do_autorestore();
if (!res)
fatal_error("Autorestore failed.");
}
#endif /* GLKUNIX_AUTOSAVE_FEATURES */