forked from erkyrath/glulxe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
210 lines (183 loc) · 4.64 KB
/
main.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
/* main.c: Glulxe top-level code.
Designed by Andrew Plotkin <[email protected]>
http://eblong.com/zarf/glulx/index.html
*/
#include "glk.h"
#include "glulxe.h"
int vm_exited_cleanly = TRUE;
strid_t gamefile = NULL; /* The stream containing the Glulx file. */
glui32 gamefile_start = 0; /* The position within the stream. (This will not
be zero if the Glulx file is a chunk inside a Blorb archive.) */
glui32 gamefile_len = 0; /* The length within the stream. */
char *init_err = NULL;
char *init_err2 = NULL;
/* The library_start_hook is called at the beginning of glk_main. This
is not normally necessary -- the library can do all its setup work
before calling glk_main -- but iosglk has some weird cases which
require it. */
static void (*library_start_hook)(void) = NULL;
/* The library_autorestore_hook is called right after the VM's initial
setup. This is an appropriate time to autorestore an initial game
state, if the library has that capability. (Currently, only iosglk
and remglk do.) */
static void (*library_autorestore_hook)(void) = NULL;
static winid_t get_error_win(void);
static void stream_hexnum(glsi32 val);
/* glk_main():
The top-level routine. This does everything, and consequently is
very simple.
*/
void glk_main()
{
vm_exited_cleanly = FALSE;
if (library_start_hook)
library_start_hook();
if (init_err) {
fatal_error_2(init_err, init_err2);
return;
}
if (!is_gamefile_valid()) {
/* The fatal error has already been displayed. */
return;
}
glulx_setrandom(0);
#ifdef FLOAT_SUPPORT
if (!init_float()) {
return;
}
#endif /* FLOAT_SUPPORT */
if (!init_dispatch()) {
return;
}
if (!init_profile()) {
return;
}
setup_vm();
if (library_autorestore_hook)
library_autorestore_hook();
execute_loop();
finalize_vm();
gamefile = NULL;
gamefile_start = 0;
gamefile_len = 0;
init_err = NULL;
vm_exited_cleanly = TRUE;
profile_quit();
glk_exit();
}
void set_library_start_hook(void (*func)(void))
{
library_start_hook = func;
}
void set_library_autorestore_hook(void (*func)(void))
{
library_autorestore_hook = func;
}
/* get_error_win():
Return a window in which to display errors. The first time this is called,
it creates a new window; after that it returns the window it first
created.
*/
static winid_t get_error_win()
{
static winid_t errorwin = NULL;
if (!errorwin) {
winid_t rootwin = glk_window_get_root();
if (!rootwin) {
errorwin = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
}
else {
errorwin = glk_window_open(rootwin, winmethod_Below | winmethod_Fixed,
3, wintype_TextBuffer, 0);
}
if (!errorwin)
errorwin = rootwin;
}
return errorwin;
}
/* fatal_error_handler():
Display an error in the error window, and then exit.
*/
void fatal_error_handler(char *str, char *arg, int useval, glsi32 val)
{
winid_t win;
/* If the debugger is compiled in, send the error message to the debug
console. This may also block for debug commands, depending on
preferences. */
debugger_handle_crash(str);
win = get_error_win();
if (win) {
glk_set_window(win);
glk_put_string("Glulxe fatal error: ");
glk_put_string(str);
if (arg || useval) {
glk_put_string(" (");
if (arg)
glk_put_string(arg);
if (arg && useval)
glk_put_string(" ");
if (useval)
stream_hexnum(val);
glk_put_string(")");
}
glk_put_string("\n");
}
glk_exit();
}
/* nonfatal_warning_handler():
Display a warning in the error window, and then continue.
*/
void nonfatal_warning_handler(char *str, char *arg, int useval, glsi32 val)
{
winid_t win = get_error_win();
if (win) {
strid_t oldstr = glk_stream_get_current();
glk_set_window(win);
glk_put_string("Glulxe warning: ");
glk_put_string(str);
if (arg || useval) {
glk_put_string(" (");
if (arg)
glk_put_string(arg);
if (arg && useval)
glk_put_string(" ");
if (useval)
stream_hexnum(val);
glk_put_string(")");
}
glk_put_string("\n");
glk_stream_set_current(oldstr);
}
}
/* stream_hexnum():
Write a signed integer to the current Glk output stream.
*/
static void stream_hexnum(glsi32 val)
{
char buf[16];
glui32 ival;
int ix;
if (val == 0) {
glk_put_char('0');
return;
}
if (val < 0) {
glk_put_char('-');
ival = -val;
}
else {
ival = val;
}
ix = 0;
while (ival != 0) {
buf[ix] = (ival % 16) + '0';
if (buf[ix] > '9')
buf[ix] += ('A' - ('9' + 1));
ix++;
ival /= 16;
}
while (ix) {
ix--;
glk_put_char(buf[ix]);
}
}