Implemented garglk_set_program_name(), garglk_set_program_info(), garglk_set_story_name()
[rodin/chimara.git] / interpreters / git / savefile.c
1 // $Id: savefile.c,v 1.6 2003/10/20 16:05:06 iain Exp $
2
3 #include "git.h"
4
5 static void writeWord (git_sint32 word)
6 {
7     char buffer [4];
8     write32 (buffer, word);
9     glk_put_buffer (buffer, 4);
10 }
11
12 static git_uint32 readWord (strid_t file)
13 {
14     char buffer [4];
15     glk_get_buffer_stream (file, buffer, 4);
16     return (git_uint32) read32 (buffer);
17 }
18
19 static int sort_heap_summary(const void *p1, const void *p2)
20 {
21     const glui32 *v1 = (const glui32 *)p1;
22     const glui32 *v2 = (const glui32 *)p2;
23
24     if (v1 < v2)
25         return -1;
26     if (v1 > v2)
27         return 1;
28     return 0;
29 }
30
31 git_sint32 restoreFromFile (git_sint32 * base, git_sint32 id,
32     git_uint32 protectPos, git_uint32 protectSize)
33 {
34     git_uint32 protectEnd = protectPos + protectSize;
35     git_uint32 i;
36     strid_t file;
37     glui32 fileSize, fileStart;
38
39     int gotIdent = 0;
40     int gotMemory = 0;
41     int gotStack = 0;
42     int gotHeap = 0;
43
44     // Find out what stream they want to use, and make sure it's valid.
45     file = git_find_stream_by_id (id);
46     if (file == 0)
47         return 1;
48
49     // Read IFF header.
50     if (readWord (file) != read32("FORM"))
51         return 1; // Not an IFF file.
52     
53     fileSize = readWord (file);
54     fileStart = glk_stream_get_position (file);
55     
56     if (readWord (file) != read32("IFZS"))
57         return 1; // Not a Quetzal file.
58     
59     // Discard the current heap.
60     heap_clear();
61     
62     // Read all the chunks.
63     
64     while (glk_stream_get_position(file) < fileStart + fileSize)
65     {
66         git_uint32 chunkType, chunkSize, chunkStart;
67         chunkType = readWord (file);
68         chunkSize = readWord (file);
69         chunkStart = glk_stream_get_position (file);
70
71         if (chunkType == read32("IFhd"))
72         {
73             if (gotIdent)
74                 return 1;
75
76             gotIdent = 1;
77
78             if (chunkSize != 128)
79                 return 1;
80
81             for (i = 0 ; i < 128 ; ++i)
82             {
83                 glui32 c = glk_get_char_stream (file);
84                 if (gRom [i] != c)
85                     return 1;
86             }
87         }
88         else if (chunkType == read32("Stks"))
89         {
90             if (gotStack)
91                 return 1;
92
93             gotStack = 1;
94
95             if (chunkSize & 3)
96                 return 1;
97
98             gStackPointer = base;
99             for ( ; chunkSize > 0 ; chunkSize -= 4)
100                 *gStackPointer++ = readWord(file);
101         }
102         else if (chunkType == read32("CMem"))
103         {
104             git_uint32 bytesRead = 0;
105             if (gotMemory)
106                 return 1;
107
108             gotMemory = 1;
109
110             if (resizeMemory (readWord(file), 1))
111                 fatalError ("Can't resize memory map");
112
113             bytesRead = 4;
114             i = gRamStart;
115             while (i < gExtStart && bytesRead < chunkSize)
116             {
117                 int mult = 0;
118                 char c = (char) glk_get_char_stream(file);
119                 ++bytesRead;
120                 
121                 if (c == 0)
122                 {
123                     mult = (unsigned char) glk_get_char_stream(file);
124                     ++bytesRead;
125                 }
126                 
127                 for (++mult ; mult > 0 ; --mult, ++i)
128                     if (i >= protectEnd || i < protectPos)
129                         gRam [i] = gRom [i] ^ c;
130             }
131
132             while (i < gEndMem && bytesRead < chunkSize)
133             {
134                 int mult = 0;
135                 char c = (char) glk_get_char_stream(file);
136                 ++bytesRead;
137                 
138                 if (c == 0)
139                 {
140                     mult = (unsigned char) glk_get_char_stream(file);
141                     ++bytesRead;
142                 }
143                 
144                 for (++mult ; mult > 0 ; --mult, ++i)
145                     if (i >= protectEnd || i < protectPos)
146                         gRam [i] = c;
147             }
148
149             while (i < gExtStart)
150                 if (i >= protectEnd || i < protectPos)
151                     gRam [i] = gRom [i], ++i;
152
153             while (i < gEndMem)
154                 if (i >= protectEnd || i < protectPos)
155                     gRam [i] = 0, ++i;
156
157             if (bytesRead != chunkSize)
158                 return 1; // Too much data!
159
160             if (chunkSize & 1)
161                 glk_get_char_stream (file);
162         }
163         else if (chunkType == read32("MAll"))
164         {
165             glui32 heapSize = 0;
166             glui32 * heap = 0;
167
168             if (gotHeap)
169                 return 1;
170
171             gotHeap = 1;
172
173             if (chunkSize & 3)
174                 return 1;
175
176             if (chunkSize > 0)
177             {
178                 heap = malloc (chunkSize);
179                 heapSize = chunkSize / 4;
180                 for (i = 0 ; i < heapSize ; ++i)
181                     heap[i] = readWord(file);
182
183                 /* The summary might have come from any interpreter, so it could
184                   be out of order. We'll sort it. */
185                 qsort(heap+2, (heapSize-2)/2, 8, &sort_heap_summary);
186
187                 if (heap_apply_summary (heapSize, heap))
188                     fatalError ("Couldn't apply heap summary");
189                 free (heap);
190             }
191         }
192         else
193         {
194             // Unknown chunk type -- just skip it.
195             glk_stream_set_position (file, (chunkSize + 1) & ~1, seekmode_Current);
196         }
197     }
198
199     // Make sure we have all the chunks we need.
200
201     if (!gotIdent)
202         fatalError ("No ident chunk in save file");
203
204     if (!gotStack)
205         fatalError ("No stack chunk in save file");
206
207     if (!gotMemory)
208         fatalError ("No memory chunk in save file");
209
210     // If we reach this point, we restored successfully.
211
212     return 0;
213 }
214
215 git_sint32 saveToFile (git_sint32 * base, git_sint32 * sp, git_sint32 id)
216 {
217     git_uint32 n, zeroCount;
218     glui32 fileSize, fileSizePos;
219     glui32 memSize, memSizePos;
220     glui32 heapSize;
221     glui32* heap;
222
223     strid_t file, oldFile;
224
225     // Find out what stream they want to use, and make sure it's valid.
226     file = git_find_stream_by_id (id);
227     if (file == 0)
228         return 1;
229
230     // Get the state of the heap.
231     if (heap_get_summary (&heapSize, &heap))
232         fatalError ("Couldn't get heap summary");
233
234     // Make the given stream the default.
235     oldFile = glk_stream_get_current ();
236     glk_stream_set_current (file);
237
238     // Write Quetzal header.
239     glk_put_string ("FORM");
240
241     fileSizePos = glk_stream_get_position (file);
242     writeWord (0);
243
244     glk_put_string ("IFZS");
245
246     // Header chunk.
247     glk_put_string ("IFhd");
248     writeWord (128);
249     glk_put_buffer ((char *) gRom, 128);
250
251     // Stack chunk.
252     glk_put_string ("Stks");
253     writeWord ((sp - base) * 4);
254     for (n = 0 ; n < (git_uint32) (sp - base) ; ++n)
255         writeWord (base [n]);
256
257     // Heap chunk.
258     if (heap != 0)
259     {
260         glk_put_string ("MAll");
261         writeWord (heapSize * 4);
262         for (n = 0 ; n < heapSize ; ++n)
263             writeWord (heap [n]);
264         free(heap);
265     }
266
267     // Memory chunk.
268     glk_put_string ("CMem");
269     memSizePos = glk_stream_get_position (file);
270     writeWord (0);
271
272     writeWord (gEndMem);
273     for (zeroCount = 0, n = gRamStart ; n < gEndMem ; ++n)
274     {
275         unsigned char romC = (n < gExtStart) ? gRom[n] : 0;
276         unsigned char c = ((git_uint32) romC) ^ ((git_uint32) gRam[n]);
277         if (c == 0)
278             ++zeroCount;
279         else
280         {
281             for ( ; zeroCount > 256 ; zeroCount -= 256)
282             {
283                 glk_put_char (0);
284                 glk_put_char (0xff);
285             }
286
287             if (zeroCount > 0)
288             {
289                 glk_put_char (0);
290                 glk_put_char ((char) (zeroCount - 1));
291                 zeroCount = 0;
292             }
293
294             glk_put_char (c);
295         }
296     }
297     // Note: we don't bother writing out any remaining zeroes,
298     // because the memory is padded out with zeroes on restore.
299
300     memSize = glk_stream_get_position (file) - memSizePos - 4;
301     if (memSize & 1)
302        glk_put_char (0);
303
304     // Back up and fill in the lengths.
305     fileSize = glk_stream_get_position (file) - fileSizePos - 4;
306
307     glk_stream_set_position (file, fileSizePos, seekmode_Start);
308     writeWord (fileSize);
309
310     glk_stream_set_position (file, memSizePos, seekmode_Start);
311     writeWord (memSize);
312
313     // Restore the previous default stream.
314     glk_stream_set_current (oldFile);
315
316     // And we're done.
317     return 0;
318 }