~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to terps/frotz/quetzal.c

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* quetzal.c  - Saving and restoring of Quetzal files.
 
2
 *      Written by Martin Frost <mdf@doc.ic.ac.uk>
 
3
 *
 
4
 * This file is part of Frotz.
 
5
 *
 
6
 * Frotz is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * Frotz is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
19
 */
 
20
 
 
21
#include "frotz.h"
 
22
#include "glk.h"
 
23
#include "glkio.h"
 
24
 
 
25
#define get_c fgetc
 
26
#define put_c fputc
 
27
 
 
28
typedef unsigned long zlong;
 
29
 
 
30
/*
 
31
 * This is used only by save_quetzal. It probably should be allocated
 
32
 * dynamically rather than statically.
 
33
 */
 
34
 
 
35
static zword frames[STACK_SIZE/4+1];
 
36
 
 
37
/*
 
38
 * ID types.
 
39
 */
 
40
 
 
41
#define makeid(a,b,c,d) ((zlong) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d)))
 
42
 
 
43
#define ID_FORM makeid ('F','O','R','M')
 
44
#define ID_IFZS makeid ('I','F','Z','S')
 
45
#define ID_IFhd makeid ('I','F','h','d')
 
46
#define ID_UMem makeid ('U','M','e','m')
 
47
#define ID_CMem makeid ('C','M','e','m')
 
48
#define ID_Stks makeid ('S','t','k','s')
 
49
#define ID_ANNO makeid ('A','N','N','O')
 
50
 
 
51
/*
 
52
 * Various parsing states within restoration.
 
53
 */
 
54
 
 
55
#define GOT_HEADER      0x01
 
56
#define GOT_STACK       0x02
 
57
#define GOT_MEMORY      0x04
 
58
#define GOT_NONE        0x00
 
59
#define GOT_ALL         0x07
 
60
#define GOT_ERROR       0x80
 
61
 
 
62
/*
 
63
 * Macros used to write the files.
 
64
 */
 
65
 
 
66
#define write_byte(fp,b) (put_c (b, fp) != EOF)
 
67
#define write_bytx(fp,b) write_byte (fp, (b) & 0xFF)
 
68
#define write_word(fp,w) \
 
69
    (write_bytx (fp, (w) >>  8) && write_bytx (fp, (w)))
 
70
#define write_long(fp,l) \
 
71
    (write_bytx (fp, (l) >> 24) && write_bytx (fp, (l) >> 16) && \
 
72
     write_bytx (fp, (l) >>  8) && write_bytx (fp, (l)))
 
73
#define write_chnk(fp,id,len) \
 
74
    (write_long (fp, (id))      && write_long (fp, (len)))
 
75
#define write_run(fp,run) \
 
76
    (write_byte (fp, 0)         && write_byte (fp, (run)))
 
77
 
 
78
/* Read one word from file; return TRUE if OK. */
 
79
static bool read_word (FILE *f, zword *result)
 
80
{
 
81
    int a, b;
 
82
 
 
83
    if ((a = get_c (f)) == EOF) return FALSE;
 
84
    if ((b = get_c (f)) == EOF) return FALSE;
 
85
 
 
86
    *result = ((zword) a << 8) | (zword) b;
 
87
    return TRUE;
 
88
}
 
89
 
 
90
/* Read one long from file; return TRUE if OK. */
 
91
static bool read_long (FILE *f, zlong *result)
 
92
{
 
93
    int a, b, c, d;
 
94
 
 
95
    if ((a = get_c (f)) == EOF) return FALSE;
 
96
    if ((b = get_c (f)) == EOF) return FALSE;
 
97
    if ((c = get_c (f)) == EOF) return FALSE;
 
98
    if ((d = get_c (f)) == EOF) return FALSE;
 
99
 
 
100
    *result = ((zlong) a << 24) | ((zlong) b << 16) |
 
101
              ((zlong) c <<  8) |  (zlong) d;
 
102
    return TRUE;
 
103
}
 
104
 
 
105
/*
 
106
 * Restore a saved game using Quetzal format. Return 2 if OK, 0 if an error
 
107
 * occurred before any damage was done, -1 on a fatal error.
 
108
 */
 
109
 
 
110
zword restore_quetzal (FILE *svf, FILE *stf, int blorb_ofs)
 
111
{
 
112
    zlong ifzslen, currlen, tmpl;
 
113
    zlong pc;
 
114
    zword i, tmpw;
 
115
    zword fatal = 0;    /* Set to -1 when errors must be fatal. */
 
116
    zbyte skip, progress = GOT_NONE;
 
117
    int x, y;
 
118
 
 
119
    /* Check it's really an `IFZS' file. */
 
120
    if (!read_long (svf, &tmpl)
 
121
        || !read_long (svf, &ifzslen)
 
122
        || !read_long (svf, &currlen))                          return 0;
 
123
    if (tmpl != ID_FORM || currlen != ID_IFZS)
 
124
    {
 
125
        print_string ("This is not a saved game file!\n");
 
126
        return 0;
 
127
    }
 
128
    if ((ifzslen & 1) || ifzslen<4) /* Sanity checks. */        return 0;
 
129
    ifzslen -= 4;
 
130
 
 
131
    /* Read each chunk and process it. */
 
132
    while (ifzslen > 0)
 
133
    {
 
134
        /* Read chunk header. */
 
135
        if (ifzslen < 8) /* Couldn't contain a chunk. */        return 0;
 
136
        if (!read_long (svf, &tmpl)
 
137
            || !read_long (svf, &currlen))                      return 0;
 
138
        ifzslen -= 8;   /* Reduce remaining by size of header. */
 
139
 
 
140
        /* Handle chunk body. */
 
141
        if (ifzslen < currlen) /* Chunk goes past EOF?! */      return 0;
 
142
        skip = currlen & 1;
 
143
        ifzslen -= currlen + (zlong) skip;
 
144
 
 
145
        switch (tmpl)
 
146
        {
 
147
            /* `IFhd' header chunk; must be first in file. */
 
148
            case ID_IFhd:
 
149
                if (progress & GOT_HEADER)
 
150
                {
 
151
                    print_string ("Save file has two IFZS chunks!\n");
 
152
                    return fatal;
 
153
                }
 
154
                progress |= GOT_HEADER;
 
155
                if (currlen < 13
 
156
                    || !read_word (svf, &tmpw))                 return fatal;
 
157
                if (tmpw != h_release)
 
158
                    progress = GOT_ERROR;
 
159
 
 
160
                for (i=H_SERIAL; i<H_SERIAL+6; ++i)
 
161
                {
 
162
                    if ((x = get_c (svf)) == EOF)               return fatal;
 
163
                    if (x != zmp[i])
 
164
                        progress = GOT_ERROR;
 
165
                }
 
166
 
 
167
                if (!read_word (svf, &tmpw))                    return fatal;
 
168
                if (tmpw != h_checksum)
 
169
                    progress = GOT_ERROR;
 
170
 
 
171
                if (progress & GOT_ERROR)
 
172
                {
 
173
                    print_string ("File was not saved from this story!\n");
 
174
                    return fatal;
 
175
                }
 
176
                if ((x = get_c (svf)) == EOF)                   return fatal;
 
177
                pc = (zlong) x << 16;
 
178
                if ((x = get_c (svf)) == EOF)                   return fatal;
 
179
                pc |= (zlong) x << 8;
 
180
                if ((x = get_c (svf)) == EOF)                   return fatal;
 
181
                pc |= (zlong) x;
 
182
                fatal = -1;     /* Setting PC means errors must be fatal. */
 
183
                SET_PC (pc);
 
184
 
 
185
                for (i=13; i<currlen; ++i)
 
186
                    (void) get_c (svf); /* Skip rest of chunk. */
 
187
                break;
 
188
            /* `Stks' stacks chunk; restoring this is quite complex. ;) */
 
189
            case ID_Stks:
 
190
                if (progress & GOT_STACK)
 
191
                {
 
192
                    print_string ("File contains two stack chunks!\n");
 
193
                    break;
 
194
                }
 
195
                progress |= GOT_STACK;
 
196
 
 
197
                fatal = -1;     /* Setting SP means errors must be fatal. */
 
198
                sp = stack + STACK_SIZE;
 
199
 
 
200
                /*
 
201
                 * All versions other than V6 may use evaluation stack outside
 
202
                 * any function context. As a result a faked function context
 
203
                 * will be present in the file here. We skip this context, but
 
204
                 * load the associated stack onto the stack proper...
 
205
                 */
 
206
                if (h_version != V6)
 
207
                {
 
208
                    if (currlen < 8)                            return fatal;
 
209
                    for (i=0; i<6; ++i)
 
210
                        if (get_c (svf) != 0)                   return fatal;
 
211
                    if (!read_word (svf, &tmpw))                return fatal;
 
212
                    if (tmpw > STACK_SIZE)
 
213
                    {
 
214
                        print_string ("Save-file has too much stack (and I can't cope).\n");
 
215
                        return fatal;
 
216
                    }
 
217
                    currlen -= 8;
 
218
                    if (currlen < tmpw*2)                       return fatal;
 
219
                    for (i=0; i<tmpw; ++i)
 
220
                        if (!read_word (svf, --sp))             return fatal;
 
221
                    currlen -= tmpw*2;
 
222
                }
 
223
 
 
224
                /* We now proceed to load the main block of stack frames. */
 
225
                for (fp = stack+STACK_SIZE, frame_count = 0;
 
226
                     currlen > 0;
 
227
                     currlen -= 8, ++frame_count)
 
228
                {
 
229
                    if (currlen < 8)                            return fatal;
 
230
                    if (sp - stack < 4) /* No space for frame. */
 
231
                    {
 
232
                        print_string ("Save-file has too much stack (and I can't cope).\n");
 
233
                        return fatal;
 
234
                    }
 
235
 
 
236
                    /* Read PC, procedure flag and formal param count. */
 
237
                    if (!read_long (svf, &tmpl))                return fatal;
 
238
                    y = (int) (tmpl & 0x0F);    /* Number of formals. */
 
239
                    tmpw = y << 8;
 
240
 
 
241
                    /* Read result variable. */
 
242
                    if ((x = get_c (svf)) == EOF)               return fatal;
 
243
 
 
244
                    /* Check the procedure flag... */
 
245
                    if (tmpl & 0x10)
 
246
                    {
 
247
                        tmpw |= 0x1000; /* It's a procedure. */
 
248
                        tmpl >>= 8;     /* Shift to get PC value. */
 
249
                    }
 
250
                    else
 
251
                    {
 
252
                        /* Functions have type 0, so no need to or anything. */
 
253
                        tmpl >>= 8;     /* Shift to get PC value. */
 
254
                        --tmpl;         /* Point at result byte. */
 
255
                        /* Sanity check on result variable... */
 
256
                        if (zmp[tmpl] != (zbyte) x)
 
257
                        {
 
258
                            print_string ("Save-file has wrong variable number on stack (possibly wrong game version?)\n");
 
259
                            return fatal;
 
260
                        }
 
261
                    }
 
262
                    *--sp = (zword) (tmpl >> 9);        /* High part of PC */
 
263
                    *--sp = (zword) (tmpl & 0x1FF);     /* Low part of PC */
 
264
                    *--sp = (zword) (fp - stack - 1);   /* FP */
 
265
 
 
266
                    /* Read and process argument mask. */
 
267
                    if ((x = get_c (svf)) == EOF)               return fatal;
 
268
                    ++x;        /* Should now be a power of 2 */
 
269
                    for (i=0; i<8; ++i)
 
270
                        if (x & (1<<i))
 
271
                            break;
 
272
                    if (x ^ (1<<i))     /* Not a power of 2 */
 
273
                    {
 
274
                        print_string ("Save-file uses incomplete argument lists (which I can't handle)\n");
 
275
                        return fatal;
 
276
                    }
 
277
                    *--sp = tmpw | i;
 
278
                    fp = sp;    /* FP for next frame. */
 
279
 
 
280
                    /* Read amount of eval stack used. */
 
281
                    if (!read_word (svf, &tmpw))                return fatal;
 
282
 
 
283
                    tmpw += y;  /* Amount of stack + number of locals. */
 
284
                    if (sp - stack <= tmpw)
 
285
                    {
 
286
                        print_string ("Save-file has too much stack (and I can't cope).\n");
 
287
                        return fatal;
 
288
                    }
 
289
                    if (currlen < tmpw*2)                       return fatal;
 
290
                    for (i=0; i<tmpw; ++i)
 
291
                        if (!read_word (svf, --sp))             return fatal;
 
292
                    currlen -= tmpw*2;
 
293
                }
 
294
                /* End of `Stks' processing... */
 
295
                break;
 
296
            /* Any more special chunk types must go in HERE or ABOVE. */
 
297
            /* `CMem' compressed memory chunk; uncompress it. */
 
298
            case ID_CMem:
 
299
                if (!(progress & GOT_MEMORY))   /* Don't complain if two. */
 
300
                {
 
301
                    (void) fseek (stf, blorb_ofs, SEEK_SET);
 
302
                    i=0;        /* Bytes written to data area. */
 
303
                    for (; currlen > 0; --currlen)
 
304
                    {
 
305
                        if ((x = get_c (svf)) == EOF)           return fatal;
 
306
                        if (x == 0)     /* Start run. */
 
307
                        {
 
308
                            /* Check for bogus run. */
 
309
                            if (currlen < 2)
 
310
                            {
 
311
                                print_string ("File contains bogus `CMem' chunk.\n");
 
312
                                for (; currlen > 0; --currlen)
 
313
                                    (void) get_c (svf); /* Skip rest. */
 
314
                                currlen = 1;
 
315
                                i = 0xFFFF;
 
316
                                break; /* Keep going; may be a `UMem' too. */
 
317
                            }
 
318
                            /* Copy story file to memory during the run. */
 
319
                            --currlen;
 
320
                            if ((x = get_c (svf)) == EOF)       return fatal;
 
321
                            for (; x >= 0 && i<h_dynamic_size; --x, ++i)
 
322
                                if ((y = get_c (stf)) == EOF)   return fatal;
 
323
                                else
 
324
                                    zmp[i] = (zbyte) y;
 
325
                        }
 
326
                        else    /* Not a run. */
 
327
                        {
 
328
                            if ((y = get_c (stf)) == EOF)       return fatal;
 
329
                            zmp[i] = (zbyte) (x ^ y);
 
330
                            ++i;
 
331
                        }
 
332
                        /* Make sure we don't load too much. */
 
333
                        if (i > h_dynamic_size)
 
334
                        {
 
335
                            print_string ("warning: `CMem' chunk too long!\n");
 
336
                            for (; currlen > 1; --currlen)
 
337
                                (void) get_c (svf);     /* Skip rest. */
 
338
                            break;      /* Keep going; there may be a `UMem' too. */
 
339
                        }
 
340
                    }
 
341
                    /* If chunk is short, assume a run. */
 
342
                    for (; i<h_dynamic_size; ++i)
 
343
                        if ((y = get_c (stf)) == EOF)           return fatal;
 
344
                        else
 
345
                            zmp[i] = (zbyte) y;
 
346
                    if (currlen == 0)
 
347
                        progress |= GOT_MEMORY; /* Only if succeeded. */
 
348
                    break;
 
349
            }
 
350
                /* Fall right thru (to default) if already GOT_MEMORY */
 
351
            /* `UMem' uncompressed memory chunk; load it. */
 
352
            case ID_UMem:
 
353
                if (!(progress & GOT_MEMORY))   /* Don't complain if two. */
 
354
                {
 
355
                    /* Must be exactly the right size. */
 
356
                    if (currlen == h_dynamic_size)
 
357
                    {
 
358
                        if (fread (zmp, currlen, 1, svf) == 1)
 
359
                        {
 
360
                            progress |= GOT_MEMORY;     /* Only on success. */
 
361
                            break;
 
362
                        }
 
363
                    }
 
364
                    else
 
365
                        print_string ("`UMem' chunk wrong size!\n");
 
366
                    /* Fall into default action (skip chunk) on errors. */
 
367
                }
 
368
                /* Fall thru (to default) if already GOT_MEMORY */
 
369
            /* Unrecognised chunk type; skip it. */
 
370
            default:
 
371
                (void) fseek (svf, currlen, SEEK_CUR);  /* Skip chunk. */
 
372
                break;
 
373
        }
 
374
        if (skip)
 
375
            (void) get_c (svf); /* Skip pad byte. */
 
376
    }
 
377
 
 
378
    /*
 
379
     * We've reached the end of the file. For the restoration to have been a
 
380
     * success, we must have had one of each of the required chunks.
 
381
     */
 
382
    if (!(progress & GOT_HEADER))
 
383
        print_string ("error: no valid header (`IFhd') chunk in file.\n");
 
384
    if (!(progress & GOT_STACK))
 
385
        print_string ("error: no valid stack (`Stks') chunk in file.\n");
 
386
    if (!(progress & GOT_MEMORY))
 
387
        print_string ("error: no valid memory (`CMem' or `UMem') chunk in file.\n");
 
388
 
 
389
    return (progress == GOT_ALL ? 2 : fatal);
 
390
}
 
391
 
 
392
/*
 
393
 * Save a game using Quetzal format. Return 1 if OK, 0 if failed.
 
394
 */
 
395
 
 
396
zword save_quetzal (FILE *svf, FILE *stf, int blorb_ofs)
 
397
{
 
398
    zlong ifzslen = 0, cmemlen = 0, stkslen = 0;
 
399
    zlong pc;
 
400
    zword i, j, n;
 
401
    zword nvars, nargs, nstk, *p;
 
402
    zbyte var;
 
403
    long cmempos, stkspos;
 
404
    int c;
 
405
 
 
406
    /* Write `IFZS' header. */
 
407
    if (!write_chnk (svf, ID_FORM, 0))                  return 0;
 
408
    if (!write_long (svf, ID_IFZS))                     return 0;
 
409
 
 
410
    /* Write `IFhd' chunk. */
 
411
    GET_PC (pc);
 
412
    if (!write_chnk (svf, ID_IFhd, 13))                 return 0;
 
413
    if (!write_word (svf, h_release))                   return 0;
 
414
    for (i=H_SERIAL; i<H_SERIAL+6; ++i)
 
415
        if (!write_byte (svf, zmp[i]))                  return 0;
 
416
    if (!write_word (svf, h_checksum))                  return 0;
 
417
    if (!write_long (svf, pc << 8)) /* Includes pad. */ return 0;
 
418
 
 
419
    /* Write `CMem' chunk. */
 
420
    if ((cmempos = ftell (svf)) < 0)                    return 0;
 
421
    if (!write_chnk (svf, ID_CMem, 0))                  return 0;
 
422
    (void) fseek (stf, blorb_ofs, SEEK_SET);
 
423
    /* j holds current run length. */
 
424
    for (i=0, j=0, cmemlen=0; i < h_dynamic_size; ++i)
 
425
    {
 
426
        if ((c = get_c (stf)) == EOF)                   return 0;
 
427
        c ^= (int) zmp[i];
 
428
        if (c == 0)
 
429
            ++j;        /* It's a run of equal bytes. */
 
430
        else
 
431
        {
 
432
            /* Write out any run there may be. */
 
433
            if (j > 0)
 
434
            {
 
435
                for (; j > 0x100; j -= 0x100)
 
436
                {
 
437
                    if (!write_run (svf, 0xFF))         return 0;
 
438
                    cmemlen += 2;
 
439
                }
 
440
                if (!write_run (svf, j-1))              return 0;
 
441
                cmemlen += 2;
 
442
                j = 0;
 
443
            }
 
444
            /* Any runs are now written. Write this (nonzero) byte. */
 
445
            if (!write_byte (svf, (zbyte) c))           return 0;
 
446
            ++cmemlen;
 
447
        }
 
448
    }
 
449
    /*
 
450
     * Reached end of dynamic memory. We ignore any unwritten run there may be
 
451
     * at this point.
 
452
     */
 
453
    if (cmemlen & 1)    /* Chunk length must be even. */
 
454
        if (!write_byte (svf, 0))                       return 0;
 
455
 
 
456
    /* Write `Stks' chunk. You are not expected to understand this. ;) */
 
457
    if ((stkspos = ftell (svf)) < 0)                    return 0;
 
458
    if (!write_chnk (svf, ID_Stks, 0))                  return 0;
 
459
 
 
460
    /*
 
461
     * We construct a list of frame indices, most recent first, in `frames'.
 
462
     * These indices are the offsets into the `stack' array of the word before
 
463
     * the first word pushed in each frame.
 
464
     */
 
465
    frames[0] = sp - stack;     /* The frame we'd get by doing a call now. */
 
466
    for (i = fp - stack + 4, n=0; i < STACK_SIZE+4; i = stack[i-3] + 5)
 
467
        frames[++n] = i;
 
468
 
 
469
    /*
 
470
     * All versions other than V6 can use evaluation stack outside a function
 
471
     * context. We write a faked stack frame (most fields zero) to cater for
 
472
     * this.
 
473
     */
 
474
    if (h_version != V6)
 
475
    {
 
476
        for (i=0; i<6; ++i)
 
477
            if (!write_byte (svf, 0))                   return 0;
 
478
        nstk = STACK_SIZE - frames[n];
 
479
        if (!write_word (svf, nstk))                    return 0;
 
480
        for (j=STACK_SIZE-1; j >= frames[n]; --j)
 
481
            if (!write_word (svf, stack[j]))            return 0;
 
482
        stkslen = 8 + 2*nstk;
 
483
    }
 
484
 
 
485
    /* Write out the rest of the stack frames. */
 
486
    for (i=n; i>0; --i)
 
487
    {
 
488
        p = stack + frames[i] - 4;      /* Points to call frame. */
 
489
        nvars = (p[0] & 0x0F00) >> 8;
 
490
        nargs =  p[0] & 0x00FF;
 
491
        nstk  =  frames[i] - frames[i-1] - nvars - 4;
 
492
        pc    =  ((zlong) p[3] << 9) | p[2];
 
493
 
 
494
        switch (p[0] & 0xF000)  /* Check type of call. */
 
495
        {
 
496
            case 0x0000:        /* Function. */
 
497
                var = zmp[pc];
 
498
                pc = ((pc + 1) << 8) | nvars;
 
499
                break;
 
500
            case 0x1000:        /* Procedure. */
 
501
                var = 0;
 
502
                pc = (pc << 8) | 0x10 | nvars;  /* Set procedure flag. */
 
503
                break;
 
504
            /* case 0x2000: */
 
505
            default:
 
506
                runtime_error (ERR_SAVE_IN_INTER);
 
507
                return 0;
 
508
        }
 
509
        if (nargs != 0)
 
510
            nargs = (1 << nargs) - 1;   /* Make args into bitmap. */
 
511
 
 
512
        /* Write the main part of the frame... */
 
513
        if (!write_long (svf, pc)
 
514
            || !write_byte (svf, var)
 
515
            || !write_byte (svf, nargs)
 
516
            || !write_word (svf, nstk))                 return 0;
 
517
 
 
518
        /* Write the variables and eval stack. */
 
519
        for (j=0, --p; j<nvars+nstk; ++j, --p)
 
520
            if (!write_word (svf, *p))                  return 0;
 
521
 
 
522
        /* Calculate length written thus far. */
 
523
        stkslen += 8 + 2 * (nvars + nstk);
 
524
    }
 
525
 
 
526
    /* Fill in variable chunk lengths. */
 
527
    ifzslen = 3*8 + 4 + 14 + cmemlen + stkslen;
 
528
    if (cmemlen & 1)
 
529
        ++ifzslen;
 
530
    (void) fseek (svf,         4, SEEK_SET);
 
531
    if (!write_long (svf, ifzslen))                     return 0;
 
532
    (void) fseek (svf, cmempos+4, SEEK_SET);
 
533
    if (!write_long (svf, cmemlen))                     return 0;
 
534
    (void) fseek (svf, stkspos+4, SEEK_SET);
 
535
    if (!write_long (svf, stkslen))                     return 0;
 
536
 
 
537
    /* After all that, still nothing went wrong! */
 
538
    return 1;
 
539
}