Improved Glulxercise test program
[projects/chimara/chimara.git] / interpreters / git / git.c
1 // $Id: git.c,v 1.21 2004/12/22 12:40:07 iain Exp $
2
3 #include "git.h"
4 #include <gi_blorb.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7
8 // The four-char code 'FORM' as a big-endian value.
9 // This is the magic number at the start of Blorb files.
10 #define FORM 0x464f524d
11
12 static void gitMain (const git_uint8 * game, git_uint32 gameSize, git_uint32 cacheSize, git_uint32 undoSize)
13 {
14     git_uint32 version;
15     enum IOMode ioMode = IO_NULL;
16     
17     init_accel ();
18
19     // Initialise the Glk dispatch layer.
20     git_init_dispatch();
21
22     // Set various globals.    
23     gPeephole = 1;
24     gDebug = 0;
25     
26     // Load the gamefile into memory
27     // and initialise undo records.
28     initMemory (game, gameSize);
29     initUndo (undoSize);
30     
31     // Check that we're compatible with the
32     // glulx spec version that the game uses.
33     version = memRead32 (4);
34     if (version == 0x010000 && version <= 0x0100FF)
35     {
36         // We support version 1.0.0 even though it's
37         // officially obsolete. The only significant
38         // difference is the lack of I/O modes. In 1.0,
39         // all output goes directly to the Glk library.
40         ioMode = IO_GLK;
41     }
42     else if (version == 0x020000 && version <= 0x0200FF)
43     {
44         // We support version 2.0, which most people currently use.
45     }
46     else if (version >= 0x030000 && version <= 0x0300FF)
47     {
48         // We support version 3.0, which adds Unicode functionality.
49     }
50     else if (version >= 0x030100 && version <= 0x0301FF)
51     {
52         // We support version 3.1, which adds some memory-management opcodes.
53     }
54     else
55     {
56         fatalError ("Can't run this game, because it uses a newer version "
57             "of the gamefile format than Git understands. You should check "
58             "whether a newer version of Git is available.");
59     }
60     
61     // Call the top-level function.
62     startProgram (cacheSize, ioMode);
63     
64     // Shut everything down cleanly.
65     shutdownUndo();
66     shutdownMemory();
67 }
68
69 static giblorb_result_t handleBlorb (strid_t stream)
70 {
71     giblorb_err_t err;
72     giblorb_result_t blorbres;
73     giblorb_map_t *map;
74
75     err = giblorb_set_resource_map (stream);
76     switch (err)
77     {
78         case giblorb_err_None:
79             break;
80             
81         case giblorb_err_CompileTime:
82             fatalError ("Can't read the Blorb file because something is compiled wrong in the Blorb library.");
83         case giblorb_err_Alloc:
84             fatalError ("Can't read the Blorb file because there isn't enough memory available.");
85         case giblorb_err_Read:
86             fatalError ("Can't read data from the Blorb file.");
87         case giblorb_err_Format:
88             fatalError ("Can't read the Blorb file because it seems to be corrupted.");
89         default:
90             fatalError ("Can't read the Blorb file because an unknown error occurred.");
91     }
92     
93     map = giblorb_get_resource_map();
94     if (map == NULL)
95         fatalError ("Can't find the Blorb file's resource map.");
96         
97     err = giblorb_load_resource(map, giblorb_method_FilePos, &blorbres, giblorb_ID_Exec, 0);
98     if (err)
99         fatalError ("This Blorb file does not contain an executable Glulx chunk.");
100
101     if (blorbres.chunktype != giblorb_make_id('G', 'L', 'U', 'L'))
102         fatalError ("This Blorb file contains an executable chunk, but it is not a Glulx file.");
103
104     return blorbres;
105 }
106
107 void gitWithStream (strid_t str, git_uint32 cacheSize, git_uint32 undoSize)
108 {
109     char * game;
110     git_uint32 gamePos;
111     git_uint32 gameSize;
112     
113     git_uint32 remaining;
114     char * ptr;
115     
116     char buffer [4];
117     
118     glk_stream_set_position (str, 0, seekmode_Start);
119     if (4 != glk_get_buffer_stream (str, buffer, 4))
120         fatalError ("can't read from game file stream");
121     
122     if (read32 (buffer) == FORM)
123     {
124         giblorb_result_t result = handleBlorb (str);
125         gamePos = result.data.startpos;
126         gameSize = result.length;
127     }
128     else
129     {
130         gamePos = 0;
131         glk_stream_set_position (str, 0, seekmode_End);
132         gameSize = glk_stream_get_position (str);        
133     }
134     
135     game = malloc (gameSize);
136     if (game == NULL)
137         fatalError ("failed to allocate memory to store game file");
138     
139     glk_stream_set_position (str, gamePos, seekmode_Start);
140     
141     remaining = gameSize;
142     ptr = game;    
143     while (remaining > 0)
144     {
145         git_uint32 n = glk_get_buffer_stream (str, ptr, remaining);
146         if (n == 0)
147             fatalError ("failed to read entire game file");
148         remaining -= n;
149         ptr += n;
150     }
151     
152     gitMain ((git_uint8 *) game, gameSize, cacheSize, undoSize);
153     free (game);
154 }
155
156 void git (const git_uint8 * game, git_uint32 gameSize, git_uint32 cacheSize, git_uint32 undoSize)
157 {
158     // If this is a blorb file, register it
159     // with glk and find the gamefile chunk.
160
161     if (read32 (game) == FORM)
162     {
163         strid_t stream;
164         giblorb_result_t result;
165         
166         stream = glk_stream_open_memory ((char *) game, gameSize, filemode_Read, 0);
167         if (stream == NULL)
168             fatalError ("Can't open the Blorb file as a Glk memory stream.");
169             
170         result = handleBlorb (stream);
171         game += result.data.startpos;
172         gameSize = result.length;
173     }
174     
175     gitMain (game, gameSize, cacheSize, undoSize);
176 }