1 /* serial.c: Glulxe code for saving and restoring the VM state.
2 Designed by Andrew Plotkin <erkyrath@eblong.com>
3 http://eblong.com/zarf/glulx/index.html
10 /* This structure allows us to write either to a Glk stream or to
11 a dynamically-allocated memory chunk. */
12 typedef struct dest_struct {
15 /* If it's a Glk stream: */
18 /* If it's a block of memory: */
24 #define IFFID(c1, c2, c3, c4) \
25 ( (((glui32)c1) << 24) \
26 | (((glui32)c2) << 16) \
27 | (((glui32)c3) << 8) \
30 /* This can be adjusted before startup by platform-specific startup
31 code -- that is, preference code. */
32 int max_undo_level = 8;
34 static int undo_chain_size = 0;
35 static int undo_chain_num = 0;
36 unsigned char **undo_chain = NULL;
38 static glui32 write_memstate(dest_t *dest);
39 static glui32 write_heapstate(dest_t *dest, int portable);
40 static glui32 write_stackstate(dest_t *dest, int portable);
41 static glui32 read_memstate(dest_t *dest, glui32 chunklen);
42 static glui32 read_heapstate(dest_t *dest, glui32 chunklen, int portable,
43 glui32 *sumlen, glui32 **summary);
44 static glui32 read_stackstate(dest_t *dest, glui32 chunklen, int portable);
45 static glui32 write_heapstate_sub(glui32 sumlen, glui32 *sumarray,
46 dest_t *dest, int portable);
47 static int sort_heap_summary(void *p1, void *p2);
48 static int write_long(dest_t *dest, glui32 val);
49 static int read_long(dest_t *dest, glui32 *val);
50 static int write_byte(dest_t *dest, unsigned char val);
51 static int read_byte(dest_t *dest, unsigned char *val);
52 static int reposition_write(dest_t *dest, glui32 pos);
55 Set up the undo chain and anything else that needs to be set up.
60 undo_chain_size = max_undo_level;
61 undo_chain = (unsigned char **)glulx_malloc(sizeof(unsigned char *) * undo_chain_size);
68 /* perform_saveundo():
69 Add a state pointer to the undo chain. This returns 0 on success,
72 glui32 perform_saveundo()
76 glui32 memstart, memlen, heapstart, heaplen, stackstart, stacklen;
78 /* The format for undo-saves is simpler than for saves on disk. We
79 just have a memory chunk, a heap chunk, and a stack chunk, in
80 that order. We skip the IFF chunk headers (although the size
81 fields are still there.) We also don't bother with IFF's 16-bit
84 if (undo_chain_size == 0)
95 res = write_long(&dest, 0); /* space for chunk length */
99 res = write_memstate(&dest);
100 memlen = dest.pos - memstart;
103 res = write_long(&dest, 0); /* space for chunk length */
106 heapstart = dest.pos;
107 res = write_heapstate(&dest, FALSE);
108 heaplen = dest.pos - heapstart;
111 res = write_long(&dest, 0); /* space for chunk length */
114 stackstart = dest.pos;
115 res = write_stackstate(&dest, FALSE);
116 stacklen = dest.pos - stackstart;
120 /* Trim it down to the perfect size. */
121 dest.ptr = glulx_realloc(dest.ptr, dest.pos);
126 res = reposition_write(&dest, memstart-4);
129 res = write_long(&dest, memlen);
132 res = reposition_write(&dest, heapstart-4);
135 res = write_long(&dest, heaplen);
138 res = reposition_write(&dest, stackstart-4);
141 res = write_long(&dest, stacklen);
146 if (undo_chain_num >= undo_chain_size) {
147 glulx_free(undo_chain[undo_chain_num-1]);
148 undo_chain[undo_chain_num-1] = NULL;
150 if (undo_chain_size > 1)
151 memmove(undo_chain+1, undo_chain,
152 (undo_chain_size-1) * sizeof(unsigned char *));
153 undo_chain[0] = dest.ptr;
154 if (undo_chain_num < undo_chain_size)
159 /* It didn't work. */
161 glulx_free(dest.ptr);
169 /* perform_restoreundo():
170 Pull a state pointer from the undo chain. This returns 0 on success,
171 1 on failure. Note that if it succeeds, the frameptr, localsbase,
172 and valstackbase registers are invalid; they must be rebuilt from
175 glui32 perform_restoreundo()
179 glui32 heapsumlen = 0;
180 glui32 *heapsumarr = NULL;
182 if (undo_chain_size == 0 || undo_chain_num == 0)
188 dest.ptr = undo_chain[0];
193 res = read_long(&dest, &val);
196 res = read_memstate(&dest, val);
199 res = read_long(&dest, &val);
202 res = read_heapstate(&dest, val, FALSE, &heapsumlen, &heapsumarr);
205 res = read_long(&dest, &val);
208 res = read_stackstate(&dest, val, FALSE);
210 /* ### really, many of the failure modes of those calls ought to
211 cause fatal errors. The stack or main memory may be damaged now. */
215 res = heap_apply_summary(heapsumlen, heapsumarr);
220 if (undo_chain_size > 1)
221 memmove(undo_chain, undo_chain+1,
222 (undo_chain_size-1) * sizeof(unsigned char *));
224 glulx_free(dest.ptr);
228 /* It didn't work. */
236 Write the state to the output stream. This returns 0 on success,
239 glui32 perform_save(strid_t str)
244 glui32 memstart, memlen, stackstart, stacklen, heapstart, heaplen;
245 glui32 filestart, filelen;
247 stream_get_iosys(&val, &lx);
249 /* Not using the Glk I/O system, so bail. This function only
250 knows how to write to a Glk stream. */
251 fatal_error("Streams are only available in Glk I/O system.");
265 /* Quetzal header. */
267 res = write_long(&dest, IFFID('F', 'O', 'R', 'M'));
270 res = write_long(&dest, 0); /* space for file length */
271 filestart = dest.pos;
275 res = write_long(&dest, IFFID('I', 'F', 'Z', 'S')); /* ### ? */
278 /* Header chunk. This is the first 128 bytes of memory. */
280 res = write_long(&dest, IFFID('I', 'F', 'h', 'd'));
283 res = write_long(&dest, 128);
285 for (ix=0; res==0 && ix<128; ix++) {
286 res = write_byte(&dest, Mem1(ix));
288 /* Always even, so no padding necessary. */
292 res = write_long(&dest, IFFID('C', 'M', 'e', 'm'));
295 res = write_long(&dest, 0); /* space for chunk length */
299 res = write_memstate(&dest);
300 memlen = dest.pos - memstart;
302 if (res == 0 && (memlen & 1) != 0) {
303 res = write_byte(&dest, 0);
308 res = write_long(&dest, IFFID('M', 'A', 'l', 'l'));
311 res = write_long(&dest, 0); /* space for chunk length */
314 heapstart = dest.pos;
315 res = write_heapstate(&dest, TRUE);
316 heaplen = dest.pos - heapstart;
318 /* Always even, so no padding necessary. */
322 res = write_long(&dest, IFFID('S', 't', 'k', 's'));
325 res = write_long(&dest, 0); /* space for chunk length */
328 stackstart = dest.pos;
329 res = write_stackstate(&dest, TRUE);
330 stacklen = dest.pos - stackstart;
332 if (res == 0 && (stacklen & 1) != 0) {
333 res = write_byte(&dest, 0);
336 filelen = dest.pos - filestart;
338 /* Okay, fill in all the lengths. */
340 res = reposition_write(&dest, memstart-4);
343 res = write_long(&dest, memlen);
346 res = reposition_write(&dest, heapstart-4);
349 res = write_long(&dest, heaplen);
352 res = reposition_write(&dest, stackstart-4);
355 res = write_long(&dest, stacklen);
358 res = reposition_write(&dest, filestart-4);
361 res = write_long(&dest, filelen);
369 /* perform_restore():
370 Pull a state pointer from a stream. This returns 0 on success,
371 1 on failure. Note that if it succeeds, the frameptr, localsbase,
372 and valstackbase registers are invalid; they must be rebuilt from
375 glui32 perform_restore(strid_t str)
380 glui32 filestart, filelen;
381 glui32 heapsumlen = 0;
382 glui32 *heapsumarr = NULL;
384 stream_get_iosys(&val, &lx);
386 /* Not using the Glk I/O system, so bail. This function only
387 knows how to read from a Glk stream. */
388 fatal_error("Streams are only available in Glk I/O system.");
402 /* ### the format errors checked below should send error messages to
403 the current stream. */
406 res = read_long(&dest, &val);
408 if (res == 0 && val != IFFID('F', 'O', 'R', 'M')) {
413 res = read_long(&dest, &filelen);
415 filestart = dest.pos;
418 res = read_long(&dest, &val);
420 if (res == 0 && val != IFFID('I', 'F', 'Z', 'S')) { /* ### ? */
425 while (res == 0 && dest.pos < filestart+filelen) {
426 /* Read a chunk and deal with it. */
427 glui32 chunktype, chunkstart, chunklen;
431 res = read_long(&dest, &chunktype);
434 res = read_long(&dest, &chunklen);
436 chunkstart = dest.pos;
438 if (chunktype == IFFID('I', 'F', 'h', 'd')) {
439 for (ix=0; res==0 && ix<128; ix++) {
440 res = read_byte(&dest, &dummy);
441 if (res == 0 && Mem1(ix) != dummy) {
442 /* ### non-matching header */
447 else if (chunktype == IFFID('C', 'M', 'e', 'm')) {
448 res = read_memstate(&dest, chunklen);
450 else if (chunktype == IFFID('M', 'A', 'l', 'l')) {
451 res = read_heapstate(&dest, chunklen, TRUE, &heapsumlen, &heapsumarr);
453 else if (chunktype == IFFID('S', 't', 'k', 's')) {
454 res = read_stackstate(&dest, chunklen, TRUE);
457 /* Unknown chunk type. Skip it. */
458 for (lx=0; res==0 && lx<chunklen; lx++) {
459 res = read_byte(&dest, &dummy);
463 if (chunkstart+chunklen != dest.pos) {
464 /* ### funny chunk length */
468 if ((chunklen & 1) != 0) {
470 res = read_byte(&dest, &dummy);
477 /* The summary might have come from any interpreter, so it could
478 be out of order. We'll sort it. */
479 glulx_sort(heapsumarr+2, (heapsumlen-2)/2, 2*sizeof(glui32),
481 res = heap_apply_summary(heapsumlen, heapsumarr);
491 static int reposition_write(dest_t *dest, glui32 pos)
497 glk_stream_set_position(dest->str, pos, seekmode_Start);
504 static int write_buffer(dest_t *dest, unsigned char *ptr, glui32 len)
507 if (dest->pos+len > dest->size) {
508 dest->size = dest->pos+len+1024;
510 dest->ptr = glulx_malloc(dest->size);
513 dest->ptr = glulx_realloc(dest->ptr, dest->size);
518 memcpy(dest->ptr+dest->pos, ptr, len);
521 glk_put_buffer_stream(dest->str, (char *)ptr, len);
529 static int read_buffer(dest_t *dest, unsigned char *ptr, glui32 len)
534 memcpy(ptr, dest->ptr+dest->pos, len);
537 newlen = glk_get_buffer_stream(dest->str, (char *)ptr, len);
547 static int write_long(dest_t *dest, glui32 val)
549 unsigned char buf[4];
551 return write_buffer(dest, buf, 4);
554 static int write_short(dest_t *dest, glui16 val)
556 unsigned char buf[2];
558 return write_buffer(dest, buf, 2);
561 static int write_byte(dest_t *dest, unsigned char val)
563 return write_buffer(dest, &val, 1);
566 static int read_long(dest_t *dest, glui32 *val)
568 unsigned char buf[4];
569 int res = read_buffer(dest, buf, 4);
576 static int read_short(dest_t *dest, glui16 *val)
578 unsigned char buf[2];
579 int res = read_buffer(dest, buf, 2);
586 static int read_byte(dest_t *dest, unsigned char *val)
588 return read_buffer(dest, val, 1);
591 static glui32 write_memstate(dest_t *dest)
598 res = write_long(dest, endmem);
603 glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
605 for (pos=ramstart; pos<endmem; pos++) {
607 if (pos < endgamefile) {
608 val = glk_get_char_stream(gamefile);
610 fatal_error("The game file ended unexpectedly while saving.");
612 ch ^= (unsigned char)val;
618 /* Write any run we've got. */
624 res = write_byte(dest, 0);
627 res = write_byte(dest, (val-1));
632 /* Write the byte we got. */
633 res = write_byte(dest, ch);
638 /* It's possible we've got a run left over, but we don't write it. */
643 static glui32 read_memstate(dest_t *dest, glui32 chunklen)
645 glui32 chunkend = dest->pos + chunklen;
650 unsigned char ch, ch2;
654 res = read_long(dest, &newlen);
658 res = change_memsize(newlen, FALSE);
663 glk_stream_set_position(gamefile, gamefile_start+ramstart, seekmode_Start);
665 for (pos=ramstart; pos<endmem; pos++) {
666 if (pos < endgamefile) {
667 val = glk_get_char_stream(gamefile);
669 fatal_error("The game file ended unexpectedly while restoring.");
671 ch = (unsigned char)val;
677 if (dest->pos >= chunkend) {
678 /* we're into the final, unstored run. */
684 res = read_byte(dest, &ch2);
688 res = read_byte(dest, &ch2);
691 runlen = (glui32)ch2;
698 if (pos >= protectstart && pos < protectend)
707 static glui32 write_heapstate(dest_t *dest, int portable)
713 res = heap_get_summary(&sumlen, &sumarray);
718 return 0; /* no heap */
720 res = write_heapstate_sub(sumlen, sumarray, dest, portable);
722 glulx_free(sumarray);
726 static glui32 write_heapstate_sub(glui32 sumlen, glui32 *sumarray,
727 dest_t *dest, int portable)
731 /* If we're storing for the purpose of undo, we don't need to do any
732 byte-swapping, because the result will only be used by this session. */
734 res = write_buffer(dest, (void *)sumarray, sumlen*sizeof(glui32));
740 for (lx=0; lx<sumlen; lx++) {
741 res = write_long(dest, sumarray[lx]);
749 static int sort_heap_summary(void *p1, void *p2)
751 glui32 *v1 = (glui32 *)p1;
752 glui32 *v2 = (glui32 *)p2;
761 static glui32 read_heapstate(dest_t *dest, glui32 chunklen, int portable,
762 glui32 *sumlen, glui32 **summary)
764 glui32 res, count, lx;
771 return 0; /* no heap */
774 count = chunklen / sizeof(glui32);
776 arr = glulx_malloc(chunklen);
780 res = read_buffer(dest, (void *)arr, chunklen);
790 count = chunklen / 4;
792 arr = glulx_malloc(count * sizeof(glui32));
796 for (lx=0; lx<count; lx++) {
797 res = read_long(dest, arr+lx);
808 static glui32 write_stackstate(dest_t *dest, int portable)
814 /* If we're storing for the purpose of undo, we don't need to do any
815 byte-swapping, because the result will only be used by this session. */
817 res = write_buffer(dest, stack, stackptr);
823 /* Write a portable stack image. To do this, we have to write stack
824 frames in order, bottom to top. Remember that the last word of
825 every stack frame is a pointer to the beginning of that stack frame.
826 (This includes the last frame, because the save opcode pushes on
827 a call stub before it calls perform_save().) */
829 lastframe = (glui32)(-1);
831 glui32 frameend, frm, frm2, frm3;
832 unsigned char loctype, loccount;
833 glui32 numlocals, frlen, locpos;
835 /* Find the next stack frame (after the one in lastframe). Sadly,
836 this requires searching the stack from the top down. We have to
837 do this for *every* frame, which takes N^2 time overall. But
838 save routines usually aren't nested very deep.
839 If it becomes a practical problem, we can build a stack-frame
840 array, which requires dynamic allocation. */
841 for (frm = stackptr, frameend = stackptr;
842 frm != 0 && (frm2 = Stk4(frm-4)) != lastframe;
843 frameend = frm, frm = frm2) { };
845 /* Write out the frame. */
850 res = write_long(dest, frlen);
855 res = write_long(dest, locpos);
863 loctype = Stk1(frm2);
865 loccount = Stk1(frm2);
868 res = write_byte(dest, loctype);
871 res = write_byte(dest, loccount);
875 if (loctype == 0 && loccount == 0)
881 if ((numlocals & 1) == 0) {
882 res = write_byte(dest, 0);
885 res = write_byte(dest, 0);
891 if (frm2 != frm+locpos)
892 fatal_error("Inconsistent stack frame during save.");
894 /* Write out the locals. */
895 for (lx=0; lx<numlocals; lx++) {
896 loctype = Stk1(frm3);
898 loccount = Stk1(frm3);
901 if (loctype == 0 && loccount == 0)
904 /* Put in up to 0, 1, or 3 bytes of padding, depending on loctype. */
905 while (frm2 & (loctype-1)) {
906 res = write_byte(dest, 0);
912 /* Put in this set of locals. */
917 res = write_byte(dest, Stk1(frm2));
927 res = write_short(dest, Stk2(frm2));
937 res = write_long(dest, Stk4(frm2));
948 if (frm2 != frm+frlen)
949 fatal_error("Inconsistent stack frame during save.");
951 while (frm2 < frameend) {
952 res = write_long(dest, Stk4(frm2));
958 /* Go on to the next frame. */
959 if (frameend == stackptr)
960 break; /* All done. */
967 static glui32 read_stackstate(dest_t *dest, glui32 chunklen, int portable)
970 glui32 frameend, frm, frm2, frm3, locpos, frlen, numlocals;
972 if (chunklen > stacksize)
981 res = read_buffer(dest, stack, stackptr);
987 /* This isn't going to be pleasant; we're going to read the data in
988 as a block, and then convert it in-place. */
989 res = read_buffer(dest, stack, stackptr);
994 while (frameend != 0) {
995 /* Read the beginning-of-frame pointer. Remember, right now, the
996 whole frame is stored big-endian. So we have to read with the
997 Read*() macros, and then write with the StkW*() macros. */
998 frm = Read4(stack+(frameend-4));
1002 frlen = Read4(stack+frm2);
1005 locpos = Read4(stack+frm2);
1006 StkW4(frm2, locpos);
1009 /* The locals-format list is in bytes, so we don't have to convert it. */
1016 unsigned char loctype, loccount;
1017 loctype = Read1(stack+frm3);
1019 loccount = Read1(stack+frm3);
1022 if (loctype == 0 && loccount == 0)
1025 /* Skip up to 0, 1, or 3 bytes of padding, depending on loctype. */
1026 while (frm2 & (loctype-1)) {
1031 /* Convert this set of locals. */
1036 /* Don't need to convert bytes. */
1044 glui16 loc = Read2(stack+frm2);
1053 glui32 loc = Read4(stack+frm2);
1065 if ((numlocals & 1) == 0) {
1072 if (frm3 != frm+locpos) {
1081 if (frm2 != frm+frlen) {
1085 /* Now, the values pushed on the stack after the call frame itself.
1086 This includes the stub. */
1087 while (frm2 < frameend) {
1088 glui32 loc = Read4(stack+frm2);
1099 glui32 perform_verify()
1101 glui32 len, checksum, newlen;
1102 unsigned char buf[4];
1103 glui32 val, newsum, ix;
1107 if (len < 256 || (len & 0xFF) != 0)
1110 glk_stream_set_position(gamefile, gamefile_start, seekmode_Start);
1113 /* Read the header */
1114 for (ix=0; ix<9; ix++) {
1115 newlen = glk_get_buffer_stream(gamefile, (char *)buf, 4);
1129 /* Read everything else */
1130 for (; ix < len/4; ix++) {
1131 newlen = glk_get_buffer_stream(gamefile, (char *)buf, 4);
1138 if (newsum != checksum)