Add Bocfel interpreter
[projects/chimara/chimara.git] / interpreters / bocfel / stack.c
1 /*-
2  * Copyright 2010-2012 Chris Spiegel.
3  *
4  * This file is part of Bocfel.
5  *
6  * Bocfel is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version
8  * 2 or 3, as published by the Free Software Foundation.
9  *
10  * Bocfel is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Bocfel.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include <stdint.h>
22 #include <stddef.h>
23 #include <setjmp.h>
24
25 #include "stack.h"
26 #include "branch.h"
27 #include "iff.h"
28 #include "io.h"
29 #include "memory.h"
30 #include "process.h"
31 #include "screen.h"
32 #include "util.h"
33 #include "zterp.h"
34
35 struct call_frame
36 {
37   uint32_t pc;
38   uint16_t *sp;
39   uint8_t nlocals;
40   uint8_t nargs;
41   uint16_t where;
42   uint16_t locals[15];
43 };
44
45 static struct call_frame *frames;
46 static struct call_frame *fp;
47
48 #define BASE_OF_FRAMES  frames
49 static struct call_frame *TOP_OF_FRAMES;
50 #define CURRENT_FRAME   (fp - 1)
51 #define NFRAMES         ((long)(fp - frames))
52
53 static uint16_t *stack;
54 static uint16_t *sp;
55
56 #define BASE_OF_STACK   stack
57 static uint16_t *TOP_OF_STACK;
58
59 static void PUSH_STACK(uint16_t n) { ZASSERT(sp != TOP_OF_STACK, "stack overflow"); *sp++ = n; }
60 static uint16_t POP_STACK(void) { ZASSERT(sp > CURRENT_FRAME->sp, "stack underflow"); return *--sp; }
61
62 static struct save_state
63 {
64   uint32_t pc;
65
66   uint32_t memsize;
67   uint8_t *memory;
68
69   uint32_t stack_size;
70   uint16_t *stack;
71
72   long nframes;
73   struct call_frame *frames;
74
75   struct save_state *prev, *next;
76 } *saves_head, *saves_tail;
77
78 static long nsaves;
79
80 static void add_frame(uint32_t pc_, uint16_t *sp_, uint8_t nlocals, uint8_t nargs, uint16_t where)
81 {
82   ZASSERT(fp != TOP_OF_FRAMES, "call stack too deep: %ld", NFRAMES + 1);
83
84   fp->pc = pc_;
85   fp->sp = sp_;
86   fp->nlocals = nlocals;
87   fp->nargs = nargs;
88   fp->where = where;
89
90   fp++;
91 }
92
93 void init_stack(void)
94 {
95   /* Allocate space for the evaluation and call stacks.
96    * Clamp the size between 1 and the largest value that will not
97    * produce an overflow of size_t when multiplied by the size of the
98    * type.
99    * Also, the call stack can be no larger than 0xffff so that the
100    * result of a @catch will fit into a 16-bit integer.
101    */
102   if(stack == NULL)
103   {
104 #define MIN(a, b)       ((a) < (b) ? (a) : (b))
105 #define CLAMP(n, a, b)  ((n) < (a) ? (a) : (n) > (b) ? (b): (n))
106     options.eval_stack_size = CLAMP(options.eval_stack_size, 1, SIZE_MAX / sizeof *stack);
107     stack = malloc(options.eval_stack_size * sizeof *stack);
108     if(stack == NULL) die("unable to allocate %lu bytes for the evaluation stack", options.eval_stack_size * (unsigned long)sizeof *stack);
109     TOP_OF_STACK = &stack[options.eval_stack_size];
110
111     options.call_stack_size = CLAMP(options.call_stack_size, 1, MIN(0xffff, (SIZE_MAX / sizeof *frames) - sizeof *frames));
112     /* One extra to help with saving (thus the subtraction of sizeof *frames above). */
113     frames = malloc((options.call_stack_size + 1) * sizeof *frames);
114     if(frames == NULL) die("unable to allocate %lu bytes for the call stack", (options.call_stack_size + 1) * (unsigned long)sizeof *frames);
115     TOP_OF_FRAMES = &frames[options.call_stack_size];
116 #undef MIN
117 #undef CLAMP
118   }
119
120   sp = BASE_OF_STACK;
121   fp = BASE_OF_FRAMES;
122
123   /* Quetzal requires a dummy frame in non-V6 games, so do that here. */
124   if(zversion != 6) add_frame(0, sp, 0, 0, 0);
125
126   /* Free all previous save states (from @save_undo). */
127   while(saves_head != NULL)
128   {
129     struct save_state *tmp = saves_head;
130     saves_head = saves_head->next;
131     free(tmp->stack);
132     free(tmp->frames);
133     free(tmp->memory);
134     free(tmp);
135   }
136   saves_tail = NULL;
137   nsaves = 0;
138 }
139
140 uint16_t variable(uint16_t var)
141 {
142   ZASSERT(var < 0x100, "unable to decode variable %u", (unsigned)var);
143
144   /* Stack */
145   if(var == 0)
146   {
147     return POP_STACK();
148   }
149
150   /* Locals */
151   else if(var <= 0x0f)
152   {
153     ZASSERT(var <= CURRENT_FRAME->nlocals, "attempting to read from nonexistent local variable %d: routine has %d", (int)var, CURRENT_FRAME->nlocals);
154     return CURRENT_FRAME->locals[var - 1];
155   }
156
157   /* Globals */
158   else if(var <= 0xff)
159   {
160     var -= 0x10;
161     return WORD(header.globals + (var * 2));
162   }
163
164   /* This is an “impossible” situation (ie, the game did something wrong).
165    * It will be caught above if safety checks are turned on, but if they
166    * are not, do what we can: lie.
167    */
168   return -1;
169 }
170
171 void store_variable(uint16_t var, uint16_t n)
172 {
173   ZASSERT(var < 0x100, "unable to decode variable %u", (unsigned)var);
174
175   /* Stack. */
176   if(var == 0)
177   {
178     PUSH_STACK(n);
179   }
180
181   /* Local variables. */
182   else if(var <= 0x0f)
183   {
184     ZASSERT(var <= CURRENT_FRAME->nlocals, "attempting to store to nonexistent local variable %d: routine has %d", (int)var, CURRENT_FRAME->nlocals);
185     CURRENT_FRAME->locals[var - 1] = n;
186   }
187
188   /* Global variables. */
189   else if(var <= 0xff)
190   {
191     var -= 0x10;
192     STORE_WORD(header.globals + (var * 2), n);
193   }
194 }
195
196 uint16_t *stack_top_element(void)
197 {
198   ZASSERT(sp > CURRENT_FRAME->sp, "stack underflow");
199
200   return sp - 1;
201 }
202
203 void zpush(void)
204 {
205   PUSH_STACK(zargs[0]);
206 }
207
208 void zpull(void)
209 {
210   uint16_t v;
211
212   if(zversion != 6)
213   {
214     v = POP_STACK();
215
216     /* The z-spec 1.1 requires indirect variable references to the stack not to push/pop */
217     if(zargs[0] == 0) *stack_top_element() = v;
218     else              store_variable(zargs[0], v);
219   }
220   else
221   {
222     if(znargs == 0)
223     {
224       v = POP_STACK();
225     }
226     else
227     {
228       uint16_t slots = user_word(zargs[0]) + 1;
229
230       v = user_word(zargs[0] + (2 * slots));
231
232       user_store_word(zargs[0], slots);
233     }
234
235     store(v);
236   }
237 }
238
239 void zload(void)
240 {
241   /* The z-spec 1.1 requires indirect variable references to the stack not to push/pop */
242   if(zargs[0] == 0) store(*stack_top_element());
243   else              store(variable(zargs[0]));
244 }
245
246 void zstore(void)
247 {
248   /* The z-spec 1.1 requires indirect variable references to the stack not to push/pop */
249   if(zargs[0] == 0) *stack_top_element() = zargs[1];
250   else              store_variable(zargs[0], zargs[1]);
251 }
252
253 void call(int do_store)
254 {
255   uint32_t jmp_to;
256   uint8_t nlocals;
257   uint16_t where;
258
259   if(zargs[0] == 0)
260   {
261     /* call(2) should never happen if zargs[0] is 0. */
262     if(do_store) store(0);
263     return;
264   }
265
266   jmp_to = unpack(zargs[0], 0);
267   ZASSERT(jmp_to < memory_size - 1, "call to invalid address 0x%lx", (unsigned long)jmp_to);
268
269   nlocals = BYTE(jmp_to++);
270   ZASSERT(nlocals <= 15, "too many (%d) locals at 0x%lx", nlocals, (unsigned long)jmp_to - 1);
271
272   if(zversion <= 4) ZASSERT(jmp_to + (nlocals * 2) < memory_size, "call to invalid address 0x%lx", (unsigned long)jmp_to);
273
274   switch(do_store)
275   {
276     case 1:  where = BYTE(pc++); break; /* Where to store return value */
277     case 0:  where = 0xff + 1;   break; /* Or a tag meaning no return value */
278     default: where = 0xff + 2;   break; /* Or a tag meaning push the return value */
279   }
280
281   add_frame(pc, sp, nlocals, znargs - 1, where);
282
283   for(int i = 0; i < nlocals; i++)
284   {
285     if(i < znargs - 1)
286     {
287       CURRENT_FRAME->locals[i] = zargs[i + 1];
288     }
289     else
290     {
291       if(zversion <= 4) CURRENT_FRAME->locals[i] = WORD(jmp_to + (2 * i));
292       else              CURRENT_FRAME->locals[i] = 0;
293     }
294   }
295
296   /* Take care of locals! */
297   if(zversion <= 4) jmp_to += nlocals * 2;
298
299   pc = jmp_to;
300 }
301
302 #ifdef ZTERP_GLK
303 uint16_t direct_call(uint16_t routine)
304 {
305   uint16_t saved_args[znargs];
306   uint16_t saved_nargs;
307
308   memcpy(saved_args, zargs, sizeof saved_args);
309   saved_nargs = znargs;
310
311   znargs = 1;
312   zargs[0] = routine;
313   call(2);
314
315   process_instructions();
316
317   memcpy(zargs, saved_args, sizeof saved_args);
318   znargs = saved_nargs;
319
320   return POP_STACK();
321 }
322 #endif
323
324 void zcall_store(void)
325 {
326   call(1);
327 }
328 void zcall_nostore(void)
329 {
330   call(0);
331 }
332
333 void do_return(uint16_t retval)
334 {
335   uint16_t where;
336
337   ZASSERT(NFRAMES > 1, "return attempted outside of a function");
338
339   pc = CURRENT_FRAME->pc;
340   sp = CURRENT_FRAME->sp;
341   where = CURRENT_FRAME->where;
342   fp--;
343
344   if(where <= 0xff)
345   {
346     store_variable(where, retval);
347   }
348   else if(where == 0xff + 2)
349   {
350     PUSH_STACK(retval);
351     break_from(interrupt_level());
352   }
353 }
354
355 void zret_popped(void)
356 {
357   do_return(POP_STACK());
358 }
359
360 void zpop(void)
361 {
362   POP_STACK();
363 }
364
365 void zcatch(void)
366 {
367   ZASSERT(zversion == 6 || NFRAMES > 1, "@catch called outside of a function");
368
369   /* Must account for the dummy frame in non-V6 stories. */
370   store(zversion == 6 ? NFRAMES : NFRAMES - 1);
371 }
372
373 void zthrow(void)
374 {
375   /* As with @catch, account for the dummy frame. */
376   if(zversion != 6) zargs[1]++;
377
378   ZASSERT(zversion == 6 || NFRAMES > 1, "@throw called outside of a function");
379   ZASSERT(zargs[1] <= NFRAMES, "unwinding too far");
380
381   fp = BASE_OF_FRAMES + zargs[1];
382
383   do_return(zargs[0]);
384 }
385
386 void zret(void)
387 {
388   do_return(zargs[0]);
389 }
390
391 void zrtrue(void)
392 {
393   do_return(1);
394 }
395
396 void zrfalse(void)
397 {
398   do_return(0);
399 }
400
401 void zcheck_arg_count(void)
402 {
403   ZASSERT(zversion == 6 || NFRAMES > 1, "@check_arg_count called outside of a function");
404
405   branch_if(zargs[0] <= CURRENT_FRAME->nargs);
406 }
407
408 void zpop_stack(void)
409 {
410   if(znargs == 1)
411   {
412     for(uint16_t i = 0; i < zargs[0]; i++) POP_STACK();
413   }
414   else
415   {
416     user_store_word(zargs[1], user_word(zargs[1]) + zargs[0]);
417   }
418 }
419
420 void zpush_stack(void)
421 {
422   uint16_t slots = user_word(zargs[1]);
423
424   if(slots == 0)
425   {
426     branch_if(0);
427     return;
428   }
429
430   user_store_word(zargs[1] + (2 * slots), zargs[0]);
431   user_store_word(zargs[1], slots - 1);
432
433   branch_if(1);
434 }
435
436 /* Compress dynamic memory according to Quetzal.  Memory is allocated
437  * for the passed-in pointer, and must be freed by the caller.  The
438  * return value is the size of compressed memory, or 0 on failure.
439  */
440 static uint32_t compress_memory(uint8_t **compressed)
441 {
442   uint32_t ret = 0;
443   long i = 0;
444   uint8_t *tmp;
445
446   /* The output buffer needs to be 1.5× the size of dynamic memory for
447    * the worst-case scenario: every other byte in memory differs from
448    * the story file.  This will cause every other byte to take up two
449    * bytes in the output, thus creating 3 bytes of output for every 2 of
450    * input.  This should round up for the extreme case of alternating
451    * zero/non-zero bytes with zeroes at the beginning and end, but due
452    * to the fact that trailing zeroes are not stored, it does not need
453    * to.
454    */
455   tmp = malloc((3 * header.static_start) / 2);
456   if(tmp == NULL) return 0;
457
458   while(1)
459   {
460     long run = i;
461
462     /* Count zeroes.  Stop counting when:
463      * • The end of dynamic memory is reached
464      * • A non-zero value is found
465      */
466     while(i < header.static_start && (BYTE(i) ^ dynamic_memory[i]) == 0)
467     {
468       i++;
469     }
470
471     run = i - run;
472
473     /* A run of zeroes at the end need not be written. */
474     if(i == header.static_start) break;
475
476     /* If there has been a run of zeroes, write them out
477      * 256 at a time.
478      */
479     while(run > 0)
480     {
481       tmp[ret++] = 0;
482       tmp[ret++] = (run > 256 ? 255 : run - 1);
483       run -= 256;
484     }
485
486     /* The current byte differs from the story, so write it. */
487     tmp[ret++] = BYTE(i) ^ dynamic_memory[i];
488
489     i++;
490   }
491
492   *compressed = realloc(tmp, ret);
493   if(*compressed == NULL) *compressed = tmp;
494
495   return ret;
496 }
497
498 /* Reverse of the above function. */
499 static int uncompress_memory(const uint8_t *compressed, uint32_t size)
500 {
501   uint32_t memory_index = 0;
502
503   memcpy(memory, dynamic_memory, header.static_start);
504
505   for(uint32_t i = 0; i < size; i++)
506   {
507     if(compressed[i] != 0)
508     {
509       if(memory_index == header.static_start) return -1;
510       STORE_BYTE(memory_index, BYTE(memory_index) ^ compressed[i]);
511       memory_index++;
512     }
513     else
514     {
515       if(++i == size) return -1;
516
517       if(memory_index + (compressed[i] + 1) > header.static_start) return -1;
518       memory_index += (compressed[i] + 1);
519     }
520   }
521
522   return 0;
523 }
524
525 /* Push the current game state onto the game-state stack. */
526 int push_save(void)
527 {
528   struct save_state *new;
529
530   if(options.max_saves == 0) return -1;
531
532   new = malloc(sizeof *new);
533   if(new == NULL) goto err;
534   new->stack = NULL;
535   new->frames = NULL;
536
537   new->pc = pc;
538
539   new->stack_size = sp - BASE_OF_STACK;
540   new->stack = malloc(new->stack_size * sizeof *new->stack);
541   if(new->stack == NULL) goto err;
542   memcpy(new->stack, BASE_OF_STACK, new->stack_size * sizeof *new->stack);
543
544   new->nframes = NFRAMES;
545   new->frames = malloc(new->nframes * sizeof *new->frames);
546   if(new->frames == NULL) goto err;
547   memcpy(new->frames, BASE_OF_FRAMES, new->nframes * sizeof *new->frames);
548
549   if(options.disable_undo_compression)
550   {
551     new->memory = malloc(header.static_start);
552     if(new->memory == NULL) goto err;
553     memcpy(new->memory, memory, header.static_start);
554   }
555   else
556   {
557     new->memsize = compress_memory(&new->memory);
558     if(new->memsize == 0) goto err;
559   }
560
561   /* If the maximum number has been reached, drop the last element.
562    * A negative value for max_saves means there is no maximum.
563    */
564   if(options.max_saves > 0 && nsaves == options.max_saves)
565   {
566     struct save_state *tmp = saves_tail;
567     saves_tail = saves_tail->prev;
568     if(saves_tail == NULL) saves_head = NULL;
569     else                   saves_tail->next = NULL;
570     free(tmp->stack);
571     free(tmp->frames);
572     free(tmp->memory);
573     free(tmp);
574     nsaves--;
575   }
576
577   new->next = saves_head;
578   new->prev = NULL;
579   if(new->next != NULL) new->next->prev = new;
580   saves_head = new;
581   if(saves_tail == NULL) saves_tail = new;
582
583   nsaves++;
584
585   return 1;
586
587 err:
588   if(new != NULL)
589   {
590     free(new->stack);
591     free(new->frames);
592     free(new);
593   }
594
595   return 0;
596 }
597
598 /* Pop the last-stored game state and jump to it. */
599 int pop_save(void)
600 {
601   struct save_state *p;
602
603   if(nsaves == 0) return 0;
604
605   p = saves_head;
606
607   pc = p->pc;
608
609   if(options.disable_undo_compression)
610   {
611     memcpy(memory, p->memory, header.static_start);
612   }
613   else
614   {
615     /* If this fails it’s a bug: unlike Quetzal files, the contents of
616      * p->memory are known to be good, because the compression was done
617      * by us with no chance for corruption (apart, again, from bugs).
618      */
619     if(uncompress_memory(p->memory, p->memsize) == -1) die("error uncompressing memory: unable to continue");
620   }
621
622   sp = BASE_OF_STACK + p->stack_size;
623   memcpy(BASE_OF_STACK, p->stack, sizeof *sp * p->stack_size);
624
625   fp = BASE_OF_FRAMES + p->nframes;
626   memcpy(BASE_OF_FRAMES, p->frames, sizeof *p->frames * p->nframes);
627
628   /* Never pop off the last state.  A story has every right to call
629    * @restore_undo as many times as it called @save_undo.  However, if
630    * there aren’t enough save slots, popping off the last state would
631    * cause @restore_undo to return failure when it should not.
632    */
633   if(nsaves > 1)
634   {
635     saves_head = saves_head->next;
636     saves_head->prev = NULL;
637
638     free(p->stack);
639     free(p->frames);
640     free(p->memory);
641     free(p);
642
643     nsaves--;
644   }
645
646   return 2;
647 }
648
649 void zsave_undo(void)
650 {
651   if(interrupt_level() != 0) die("@save_undo called inside of an interrupt");
652
653   store(push_save());
654 }
655
656 void zrestore_undo(void)
657 {
658   uint16_t flags2;
659
660   /* §6.1.2: Flags 2 should be preserved. */
661   flags2 = WORD(0x10);
662   store(pop_save());
663   STORE_WORD(0x10, flags2);
664 }
665
666 /* Quetzal save/restore functions. */
667 static jmp_buf exception;
668 #define WRITE8(v)  do { uint8_t  v_ = (v); if(zterp_io_write(savefile, &v_, sizeof v_) != sizeof v_) longjmp(exception, 1); local_written += 1; } while(0)
669 #define WRITE16(v) do { uint16_t w_ = (v); WRITE8(w_ >>  8); WRITE8(w_ & 0xff); } while(0)
670 #define WRITE32(v) do { uint32_t x_ = (v); WRITE8(x_ >> 24); WRITE8((x_ >> 16) & 0xff); WRITE8((x_ >> 8) & 0xff); WRITE8(x_ & 0xff); } while(0)
671 #define WRITEID(v) WRITE32(STRID(v))
672
673 static size_t quetzal_write_stack(zterp_io *savefile)
674 {
675   size_t local_written = 0;
676
677   /* Add one more “fake” call frame with just enough information to
678    * calculate the evaluation stack used by the current routine.
679    */
680   fp->sp = sp;
681   for(struct call_frame *p = BASE_OF_FRAMES; p != fp; p++)
682   {
683     uint8_t temp;
684
685     WRITE8((p->pc >> 16) & 0xff);
686     WRITE8((p->pc >>  8) & 0xff);
687     WRITE8((p->pc >>  0) & 0xff);
688
689     temp = p->nlocals;
690     if(p->where > 0xff) temp |= 0x10;
691     WRITE8(temp);
692
693     if(p->where > 0xff) WRITE8(0);
694     else                WRITE8(p->where);
695
696     WRITE8((1U << p->nargs) - 1);
697
698     /* number of words of evaluation stack used */
699     WRITE16((p + 1)->sp - p->sp);
700
701     /* local variables */
702     for(int i = 0; i < p->nlocals; i++) WRITE16(p->locals[i]);
703
704     /* evaluation stack */
705     for(ptrdiff_t i = 0; i < (p + 1)->sp - p->sp; i++) WRITE16(p->sp[i]);
706   }
707
708   return local_written;
709 }
710
711 int save_quetzal(zterp_io *savefile, int is_meta)
712 {
713   if(setjmp(exception) != 0) return 0;
714
715   size_t local_written = 0;
716   size_t game_len;
717   uint32_t memsize;
718   uint8_t *compressed;
719   uint8_t *mem = memory;
720   long stks_pos;
721   size_t stack_size;
722
723   WRITEID("FORM");
724   WRITEID("    "); /* to be filled in */
725   WRITEID(is_meta ? "BFMS" : "IFZS");
726
727   WRITEID("IFhd");
728   WRITE32(13);
729   WRITE16(header.release);
730   zterp_io_write(savefile, header.serial, 6);
731   local_written += 6;
732   WRITE16(header.checksum);
733   WRITE8(pc >> 16);
734   WRITE8(pc >> 8);
735   WRITE8(pc & 0xff);
736   WRITE8(0); /* padding */
737
738   /* Store the filename in an IntD chunk. */
739   game_len = 12 + strlen(game_file);
740   WRITEID("IntD");
741   WRITE32(game_len);
742   WRITEID("UNIX");
743   WRITE8(0x02);
744   WRITE8(0);
745   WRITE16(0);
746   WRITEID("    ");
747   zterp_io_write(savefile, game_file, game_len - 12);
748   local_written += (game_len - 12);
749   if(game_len & 1) WRITE8(0);
750
751   memsize = compress_memory(&compressed);
752
753   /* It is possible for the compressed memory size to be larger than
754    * uncompressed; in this case, just store the uncompressed memory.
755    */
756   if(memsize > 0 && memsize < header.static_start)
757   {
758     mem = compressed;
759     WRITEID("CMem");
760   }
761   else
762   {
763     memsize = header.static_start;
764     WRITEID("UMem");
765   }
766   WRITE32(memsize);
767   zterp_io_write(savefile, mem, memsize);
768   local_written += memsize;
769   if(memsize & 1) WRITE8(0); /* padding */
770   free(compressed);
771
772   WRITEID("Stks");
773   stks_pos = zterp_io_tell(savefile);
774   WRITEID("    "); /* to be filled in */
775   stack_size = quetzal_write_stack(savefile);
776   local_written += stack_size;
777   if(stack_size & 1) WRITE8(0); /* padding */
778
779   zterp_io_seek(savefile, 4, SEEK_SET);
780   WRITE32(local_written - 8); /* entire file size minus 8 (FORM + size) */
781
782   zterp_io_seek(savefile, stks_pos, SEEK_SET);
783   WRITE32(stack_size); /* size of the stacks chunk */
784
785   return 1;
786 }
787
788 /* Restoring can put the system in an inconsistent state by restoring
789  * only part of memory: the save file may be corrupt and cause failure
790  * part way through updating memory, for example.  This set of functions
791  * takes a snapshot of the current state of dynamic memory and the
792  * stacks so they can be restored on failure.
793  */
794 static uint8_t *memory_backup;
795 static uint16_t *stack_backup;
796 static int stack_backup_size;
797 static struct call_frame *frames_backup;
798 static int frames_backup_size;
799
800 static void memory_snapshot_free(void)
801 {
802   free(memory_backup);
803   free(stack_backup);
804   free(frames_backup);
805
806   memory_backup = NULL;
807   stack_backup = NULL;
808   frames_backup = NULL;
809 }
810
811 static void memory_snapshot(void)
812 {
813   memory_snapshot_free();
814
815   memory_backup = malloc(header.static_start);
816   if(memory_backup == NULL) goto err;
817
818   memcpy(memory_backup, memory, header.static_start);
819
820   stack_backup_size = sp - stack;
821   if(stack_backup_size != 0)
822   {
823     stack_backup = malloc(stack_backup_size * sizeof *stack);
824     if(stack_backup == NULL) goto err;
825     memcpy(stack_backup, stack, stack_backup_size * sizeof *stack);
826   }
827
828   frames_backup_size = fp - frames;
829   if(frames_backup_size != 0)
830   {
831     frames_backup = malloc(frames_backup_size * sizeof *frames);
832     if(frames_backup == NULL) goto err;
833     memcpy(frames_backup, frames, frames_backup_size * sizeof *frames);
834   }
835
836   return;
837
838 err:
839   memory_snapshot_free();
840
841   return;
842 }
843
844 static int memory_restore(void)
845 {
846   /* stack_backup and frames_backup will be NULL if the stacks were
847    * empty, so use memory_backup to determine if a snapshot has been
848    * taken.
849    */
850   if(memory_backup == NULL) return 0;
851
852   memcpy(memory, memory_backup, header.static_start);
853   if(stack_backup != NULL) memcpy(stack, stack_backup, stack_backup_size * sizeof *stack);
854   sp = stack + stack_backup_size;
855   if(frames_backup != NULL) memcpy(frames, frames_backup, frames_backup_size * sizeof *frames);
856   fp = frames + frames_backup_size;
857
858   memory_snapshot_free();
859
860   return 1;
861 }
862
863 #define goto_err(...)   do { show_message("save file error: " __VA_ARGS__); goto err; } while(0)
864 #define goto_death(...) do { show_message("save file error: " __VA_ARGS__); goto death; } while(0)
865
866 int restore_quetzal(zterp_io *savefile, int is_meta)
867 {
868   zterp_iff *iff;
869   uint32_t size;
870   uint32_t n = 0;
871   uint8_t ifhd[13];
872
873   iff = zterp_iff_parse(savefile, is_meta ? "BFMS" : "IFZS");
874
875   if(iff == NULL ||
876      !zterp_iff_find(iff, "IFhd", &size) ||
877      size != 13 ||
878      zterp_io_read(savefile, ifhd, sizeof ifhd) != sizeof ifhd)
879   {
880     goto_err("corrupted save file or not a save file at all");
881   }
882
883   if(((ifhd[0] << 8) | ifhd[1]) != header.release ||
884      memcmp(&ifhd[2], header.serial, sizeof header.serial) != 0 ||
885      ((ifhd[8] << 8) | ifhd[9]) != header.checksum)
886   {
887     goto_err("wrong game or version");
888   }
889
890   memory_snapshot();
891
892   if(zterp_iff_find(iff, "CMem", &size))
893   {
894     uint8_t buf[size]; /* Too big for the stack? */
895
896     if(zterp_io_read(savefile, buf, size) != size) goto_err("unexpected eof reading compressed memory");
897
898     if(uncompress_memory(buf, size) == -1) goto_death("memory cannot be uncompressed");
899   }
900   else if(zterp_iff_find(iff, "UMem", &size))
901   {
902     if(size != header.static_start) goto_err("memory size mismatch");
903     if(zterp_io_read(savefile, memory, header.static_start) != header.static_start) goto_death("unexpected eof reading memory");
904   }
905   else
906   {
907     goto_err("no memory chunk found");
908   }
909
910   if(!zterp_iff_find(iff, "Stks", &size)) goto_death("no stacks chunk found");
911
912   sp = BASE_OF_STACK;
913   fp = BASE_OF_FRAMES;
914
915   while(n < size)
916   {
917     uint8_t frame[8];
918     uint8_t nlocals;
919     uint16_t nstack;
920     uint8_t nargs = 0;
921
922     if(zterp_io_read(savefile, frame, sizeof frame) != sizeof frame) goto_death("unexpected eof reading stack frame");
923     n += sizeof frame;
924
925     nlocals = frame[3] & 0xf;
926     nstack = (frame[6] << 8) | frame[7];
927     frame[5]++;
928     while(frame[5] >>= 1) nargs++;
929
930     add_frame((frame[0] << 16) | (frame[1] << 8) | frame[2], sp, nlocals, nargs, (frame[3] & 0x10) ? 0xff + 1 : frame[4]);
931
932     for(int i = 0; i < nlocals; i++)
933     {
934       uint16_t l;
935
936       if(!zterp_io_read16(savefile, &l)) goto_death("unexpected eof reading local variable");
937       CURRENT_FRAME->locals[i] = l;
938
939       n += sizeof l;
940     }
941
942     for(uint16_t i = 0; i < nstack; i++)
943     {
944       uint16_t s;
945
946       if(!zterp_io_read16(savefile, &s)) goto_death("unexpected eof reading stack entry");
947       PUSH_STACK(s);
948
949       n += sizeof s;
950     }
951   }
952
953   if(n != size) goto_death("stack size mismatch");
954
955   zterp_iff_free(iff);
956   memory_snapshot_free();
957
958   pc = (ifhd[10] << 16) | (ifhd[11] << 8) | ifhd[12];
959
960   return 1;
961
962 death:
963   /* At this point, something vital (memory and/or the stacks) has been
964    * scribbed upon; if there was a successful backup, restore it.
965    * Otherwise the only course of action is to exit.
966    */
967   if(!memory_restore()) die("the system is likely in an inconsistent state");
968
969 err:
970   /* A snapshot may have been taken, but neither memory nor the stacks
971    * have been overwritten, so just free the snapshot.
972    */
973   memory_snapshot_free();
974   zterp_iff_free(iff);
975   return 0;
976 }
977
978 #undef goto_err
979 #undef goto_death
980
981 /* Perform all aspects of a save, apart from storing/branching.
982  * Returns true if the save was success, false if not.
983  * “is_meta” is true if this save file is from a meta-save.
984  */
985 int do_save(int is_meta)
986 {
987   zterp_io *savefile;
988   int success;
989
990   savefile = zterp_io_open(NULL, ZTERP_IO_WRONLY | ZTERP_IO_SAVE);
991   if(savefile == NULL)
992   {
993     warning("unable to open save file");
994     return 0;
995   }
996
997   success = save_quetzal(savefile, is_meta);
998
999   zterp_io_close(savefile);
1000
1001   return success;
1002 }
1003
1004 /* The suggested filename is ignored, because Glk and, at least as of
1005  * right now, zterp_io_open(), do not provide a method to do this.
1006  * The “prompt” argument added by standard 1.1 is thus also ignored.
1007  */
1008 void zsave(void)
1009 {
1010   if(interrupt_level() != 0) die("@save called inside of an interrupt");
1011
1012   int success = do_save(0);
1013
1014   if(zversion <= 3) branch_if(success);
1015   else              store(success);
1016 }
1017
1018 /* Perform all aspects of a restore, apart from storing/branching.
1019  * Returns true if the restore was success, false if not.
1020  * “is_meta” is true if this save file is expected to be from a
1021  * meta-save.
1022  */
1023 int do_restore(int is_meta)
1024 {
1025   zterp_io *savefile;
1026   uint16_t flags2;
1027   int success;
1028
1029   savefile = zterp_io_open(NULL, ZTERP_IO_RDONLY | ZTERP_IO_SAVE);
1030   if(savefile == NULL)
1031   {
1032     warning("unable to open save file");
1033     return 0;
1034   }
1035
1036   flags2 = WORD(0x10);
1037
1038   success = restore_quetzal(savefile, is_meta);
1039
1040   zterp_io_close(savefile);
1041
1042   if(success)
1043   {
1044     /* On a successful restore, we are outside of any interrupt (since
1045      * @save cannot be called inside an interrupt), so reset the level
1046      * back to zero.  In addition, there may be pending read events that
1047      * need to be canceled, so do that, too.
1048      */
1049     reset_level();
1050     cancel_all_events();
1051
1052     /* §8.6.1.3 */
1053     if(zversion == 3) close_upper_window();
1054
1055     /* The save might be from a different interpreter with different
1056      * capabilities, so update the header to indicate what the current
1057      * capabilities are...
1058      */
1059     write_header();
1060
1061     /* ...except that flags2 should be preserved (§6.1.2). */
1062     STORE_WORD(0x10, flags2);
1063
1064     /* Redraw the status line in games that use one. */
1065     if(zversion <= 3) zshow_status();
1066   }
1067
1068   return success;
1069 }
1070
1071 void zrestore(void)
1072 {
1073   int success = do_restore(0);
1074
1075   if(zversion <= 3) branch_if(success);
1076   else              store(success ? 2 : 0);
1077 }