Bug fixes
[rodin/chimara.git] / interpreters / glulxe / main.c
1 /* main.c: Glulxe top-level code.
2     Designed by Andrew Plotkin <erkyrath@eblong.com>
3     http://eblong.com/zarf/glulx/index.html
4 */
5
6 #include "glk.h"
7 #include "glulxe.h"
8
9 strid_t gamefile = NULL; /* The stream containing the Glulx file. */
10 glui32 gamefile_start = 0; /* The position within the stream. (This will not 
11     be zero if the Glulx file is a chunk inside a Blorb archive.) */
12 glui32 gamefile_len = 0; /* The length within the stream. */
13 char *init_err = NULL;
14 char *init_err2 = NULL;
15
16 static winid_t get_error_win(void);
17 static void stream_hexnum(glsi32 val);
18
19 /* glk_main():
20    The top-level routine. This does everything, and consequently is
21    very simple. 
22 */
23 void glk_main()
24 {
25   if (init_err) {
26     fatal_error_2(init_err, init_err2);
27     return;
28   }
29
30   if (!is_gamefile_valid()) {
31     /* The fatal error has already been displayed. */
32     return;
33   }
34
35   glulx_setrandom(0);
36   if (!init_dispatch()) {
37     return;
38   }
39   if (!init_profile()) {
40     return;
41   }
42
43   setup_vm();
44   execute_loop();
45   finalize_vm();
46
47   profile_quit();
48   glk_exit();
49 }
50
51 /* get_error_win():
52    Return a window in which to display errors. The first time this is called,
53    it creates a new window; after that it returns the window it first
54    created.
55 */
56 static winid_t get_error_win()
57 {
58   static winid_t errorwin = NULL;
59
60   if (!errorwin) {
61     winid_t rootwin = glk_window_get_root();
62     if (!rootwin) {
63       errorwin = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
64     }
65     else {
66       errorwin = glk_window_open(rootwin, winmethod_Below | winmethod_Fixed, 
67         3, wintype_TextBuffer, 0);
68     }
69     if (!errorwin)
70       errorwin = rootwin;
71   }
72
73   return errorwin;
74 }
75
76 /* fatal_error_handler():
77    Display an error in the error window, and then exit.
78 */
79 void fatal_error_handler(char *str, char *arg, int useval, glsi32 val)
80 {
81   winid_t win = get_error_win();
82   if (win) {
83     glk_set_window(win);
84     glk_put_string("Glulxe fatal error: ");
85     glk_put_string(str);
86     if (arg || useval) {
87       glk_put_string(" (");
88       if (arg)
89         glk_put_string(arg);
90       if (arg && useval)
91         glk_put_string(" ");
92       if (useval)
93         stream_hexnum(val);
94       glk_put_string(")");
95     }
96     glk_put_string("\n");
97   }
98   glk_exit();
99 }
100
101 /* nonfatal_warning_handler():
102    Display a warning in the error window, and then continue.
103 */
104 void nonfatal_warning_handler(char *str, char *arg, int useval, glsi32 val)
105 {
106   winid_t win = get_error_win();
107   if (win) {
108     strid_t oldstr = glk_stream_get_current();
109     glk_set_window(win);
110     glk_put_string("Glulxe warning: ");
111     glk_put_string(str);
112     if (arg || useval) {
113       glk_put_string(" (");
114       if (arg)
115         glk_put_string(arg);
116       if (arg && useval)
117         glk_put_string(" ");
118       if (useval)
119         stream_hexnum(val);
120       glk_put_string(")");
121     }
122     glk_put_string("\n");
123     glk_stream_set_current(oldstr);
124   }
125 }
126
127 /* stream_hexnum():
128    Write a signed integer to the current Glk output stream.
129 */
130 static void stream_hexnum(glsi32 val)
131 {
132   char buf[16];
133   glui32 ival;
134   int ix;
135
136   if (val == 0) {
137     glk_put_char('0');
138     return;
139   }
140
141   if (val < 0) {
142     glk_put_char('-');
143     ival = -val;
144   }
145   else {
146     ival = val;
147   }
148
149   ix = 0;
150   while (ival != 0) {
151     buf[ix] = (ival % 16) + '0';
152     if (buf[ix] > '9')
153       buf[ix] += ('A' - ('9' + 1));
154     ix++;
155     ival /= 16;
156   }
157
158   while (ix) {
159     ix--;
160     glk_put_char(buf[ix]);
161   }
162 }
163