git_sint32 saveToFile (git_sint32 * base, git_sint32 * sp, git_sint32 id) { // Find out what stream they want to use, and make sure it's valid. strid_t file = git_find_stream_by_id (id); if (file == 0) return 1; return saveToFileStrCore (base, sp, file, 0, NULL, NULL); }
git_sint32 restoreFromFile (git_sint32 * base, git_sint32 id, git_uint32 protectPos, git_uint32 protectSize) { // Find out what stream they want to use, and make sure it's valid. strid_t file = git_find_stream_by_id (id); if (file == 0) return 1; return restoreFromFileStr(base, file, protectPos, protectSize); }
git_sint32 restoreFromFile (git_sint32 * base, git_sint32 id, git_uint32 protectPos, git_uint32 protectSize) { git_uint32 protectEnd = protectPos + protectSize; git_uint32 i; strid_t file; glui32 fileSize, fileStart; int gotIdent = 0; int gotMemory = 0; int gotStack = 0; int gotHeap = 0; // Find out what stream they want to use, and make sure it's valid. file = git_find_stream_by_id (id); if (file == 0) return 1; // Read IFF header. if (readWord (file) != read32("FORM")) return 1; // Not an IFF file. fileSize = readWord (file); fileStart = glk_stream_get_position (file); if (readWord (file) != read32("IFZS")) return 1; // Not a Quetzal file. // Discard the current heap. heap_clear(); // Read all the chunks. while (glk_stream_get_position(file) < fileStart + fileSize) { git_uint32 chunkType, chunkSize, chunkStart; chunkType = readWord (file); chunkSize = readWord (file); chunkStart = glk_stream_get_position (file); if (chunkType == read32("IFhd")) { if (gotIdent) return 1; gotIdent = 1; if (chunkSize != 128) return 1; for (i = 0 ; i < 128 ; ++i) { glui32 c = glk_get_char_stream (file); if (gRom [i] != c) return 1; } } else if (chunkType == read32("Stks")) { if (gotStack) return 1; gotStack = 1; if (chunkSize & 3) return 1; gStackPointer = base; for ( ; chunkSize > 0 ; chunkSize -= 4) *gStackPointer++ = readWord(file); } else if (chunkType == read32("CMem")) { git_uint32 bytesRead = 0; if (gotMemory) return 1; gotMemory = 1; if (resizeMemory (readWord(file), 1)) fatalError ("Can't resize memory map"); bytesRead = 4; i = gRamStart; while (i < gExtStart && bytesRead < chunkSize) { int mult = 0; char c = (char) glk_get_char_stream(file); ++bytesRead; if (c == 0) { mult = (unsigned char) glk_get_char_stream(file); ++bytesRead; } for (++mult ; mult > 0 ; --mult, ++i) if (i >= protectEnd || i < protectPos) gRam [i] = gRom [i] ^ c; } while (i < gEndMem && bytesRead < chunkSize) { int mult = 0; char c = (char) glk_get_char_stream(file); ++bytesRead; if (c == 0) { mult = (unsigned char) glk_get_char_stream(file); ++bytesRead; } for (++mult ; mult > 0 ; --mult, ++i) if (i >= protectEnd || i < protectPos) gRam [i] = c; } while (i < gExtStart) if (i >= protectEnd || i < protectPos) gRam [i] = gRom [i], ++i; while (i < gEndMem) if (i >= protectEnd || i < protectPos) gRam [i] = 0, ++i; if (bytesRead != chunkSize) return 1; // Too much data! if (chunkSize & 1) glk_get_char_stream (file); } else if (chunkType == read32("MAll")) { glui32 heapSize = 0; glui32 * heap = 0; if (gotHeap) return 1; gotHeap = 1; if (chunkSize & 3) return 1; if (chunkSize > 0) { heap = malloc (chunkSize); heapSize = chunkSize / 4; for (i = 0 ; i < heapSize ; ++i) heap[i] = readWord(file); /* The summary might have come from any interpreter, so it could be out of order. We'll sort it. */ qsort(heap+2, (heapSize-2)/2, 8, &sort_heap_summary); if (heap_apply_summary (heapSize, heap)) fatalError ("Couldn't apply heap summary"); free (heap); } } else { // Unknown chunk type -- just skip it. glk_stream_set_position (file, (chunkSize + 1) & ~1, seekmode_Current); } } // Make sure we have all the chunks we need. if (!gotIdent) fatalError ("No ident chunk in save file"); if (!gotStack) fatalError ("No stack chunk in save file"); if (!gotMemory) fatalError ("No memory chunk in save file"); // If we reach this point, we restored successfully. return 0; }
git_sint32 saveToFile (git_sint32 * base, git_sint32 * sp, git_sint32 id) { git_uint32 n, zeroCount; glui32 fileSize, fileSizePos; glui32 memSize, memSizePos; glui32 heapSize; glui32* heap; strid_t file, oldFile; // Find out what stream they want to use, and make sure it's valid. file = git_find_stream_by_id (id); if (file == 0) return 1; // Get the state of the heap. if (heap_get_summary (&heapSize, &heap)) fatalError ("Couldn't get heap summary"); // Make the given stream the default. oldFile = glk_stream_get_current (); glk_stream_set_current (file); // Write Quetzal header. glk_put_string ("FORM"); fileSizePos = glk_stream_get_position (file); writeWord (0); glk_put_string ("IFZS"); // Header chunk. glk_put_string ("IFhd"); writeWord (128); glk_put_buffer ((char *) gRom, 128); // Stack chunk. glk_put_string ("Stks"); writeWord ((sp - base) * 4); for (n = 0 ; n < (git_uint32) (sp - base) ; ++n) writeWord (base [n]); // Heap chunk. if (heap != 0) { glk_put_string ("MAll"); writeWord (heapSize * 4); for (n = 0 ; n < heapSize ; ++n) writeWord (heap [n]); free(heap); } // Memory chunk. glk_put_string ("CMem"); memSizePos = glk_stream_get_position (file); writeWord (0); writeWord (gEndMem); for (zeroCount = 0, n = gRamStart ; n < gEndMem ; ++n) { unsigned char romC = (n < gExtStart) ? gRom[n] : 0; unsigned char c = ((git_uint32) romC) ^ ((git_uint32) gRam[n]); if (c == 0) ++zeroCount; else { for ( ; zeroCount > 256 ; zeroCount -= 256) { glk_put_char (0); glk_put_char (0xff); } if (zeroCount > 0) { glk_put_char (0); glk_put_char ((char) (zeroCount - 1)); zeroCount = 0; } glk_put_char (c); } } // Note: we don't bother writing out any remaining zeroes, // because the memory is padded out with zeroes on restore. memSize = glk_stream_get_position (file) - memSizePos - 4; if (memSize & 1) glk_put_char (0); // Back up and fill in the lengths. fileSize = glk_stream_get_position (file) - fileSizePos - 4; glk_stream_set_position (file, fileSizePos, seekmode_Start); writeWord (fileSize); glk_stream_set_position (file, memSizePos, seekmode_Start); writeWord (memSize); // Restore the previous default stream. glk_stream_set_current (oldFile); // And we're done. return 0; }
/* git_perform_glk(): Turn a list of Glulx arguments into a list of Glk arguments, dispatch the function call, and return the result. */ glui32 git_perform_glk(glui32 funcnum, glui32 numargs, glui32 *arglist) { glui32 retval = 0; switch (funcnum) { /* To speed life up, we implement commonly-used Glk functions directly -- instead of bothering with the whole prototype mess. */ case 0x0047: /* stream_set_current */ if (numargs != 1) goto WrongArgNum; glk_stream_set_current(git_find_stream_by_id(arglist[0])); break; case 0x0048: /* stream_get_current */ if (numargs != 0) goto WrongArgNum; retval = git_find_id_for_stream(glk_stream_get_current()); break; case 0x0080: /* put_char */ if (numargs != 1) goto WrongArgNum; glk_put_char(arglist[0] & 0xFF); break; case 0x0081: /* put_char_stream */ if (numargs != 2) goto WrongArgNum; glk_put_char_stream(git_find_stream_by_id(arglist[0]), arglist[1] & 0xFF); break; case 0x00C0: /* select */ /* call a library hook on every glk_select() */ if (library_select_hook) library_select_hook(arglist[0]); /* but then fall through to full dispatcher, because there's no real need for speed here */ goto FullDispatcher; case 0x00A0: /* char_to_lower */ if (numargs != 1) goto WrongArgNum; retval = glk_char_to_lower(arglist[0] & 0xFF); break; case 0x00A1: /* char_to_upper */ if (numargs != 1) goto WrongArgNum; retval = glk_char_to_upper(arglist[0] & 0xFF); break; case 0x0128: /* put_char_uni */ if (numargs != 1) goto WrongArgNum; glk_put_char_uni(arglist[0]); break; case 0x012B: /* put_char_stream_uni */ if (numargs != 2) goto WrongArgNum; glk_put_char_stream_uni(git_find_stream_by_id(arglist[0]), arglist[1]); break; WrongArgNum: fatalError("Wrong number of arguments to Glk function."); break; FullDispatcher: default: { /* Go through the full dispatcher prototype foo. */ char *proto, *cx; dispatch_splot_t splot; int argnum, argnum2; /* Grab the string. */ proto = gidispatch_prototype(funcnum); if (!proto) fatalError("Unknown Glk function."); splot.varglist = arglist; splot.numvargs = numargs; splot.retval = &retval; /* The work goes in four phases. First, we figure out how many arguments we want, and allocate space for the Glk argument list. Then we go through the Glulxe arguments and load them into the Glk list. Then we call. Then we go through the arguments again, unloading the data back into Glulx memory. */ /* Phase 0. */ prepare_glk_args(proto, &splot); /* Phase 1. */ argnum = 0; cx = proto; parse_glk_args(&splot, &cx, 0, &argnum, 0, 0); /* Phase 2. */ gidispatch_call(funcnum, argnum, splot.garglist); /* Phase 3. */ argnum2 = 0; cx = proto; unparse_glk_args(&splot, &cx, 0, &argnum2, 0, 0); if (argnum != argnum2) fatalError("Argument counts did not match."); break; } } return retval; }