Saving, restoring, restarting and verifying the program from within itself.
§1. Environment. The language "interpreter" here supposes that the eventual program is running in a VM which is being interpreted, and that may not be the case, but it's traditional.
[ VM_ReportOnInterpreter ix; @gestalt GLULX_GESTALT_TerpVersion 0 ix; print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / "; @gestalt GLULX_GESTALT_GlulxVersion 0 ix; print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, "^"; ];
§2. Verification. This verifies that the current story file is intact.
[ VM_Verify res; @verify res; return res; ];
§3. Save, restore, restart. Restart does what it says: restarts the program as if it had just loaded for the first time.
VM_Save() attempts to save the current state of the program to a file, and returns 0 if this fails, 1 if this succeeds, or 2 if in fact a restore has just succeeded. (A successful restoration should resume execution where the save succeeded, but we want to distinguish those cases.)
VM_Restore() pretends to return true or false according to whether or not it succeeds, but in fact it can only return false to indicate failure, because a successful restoration means that execution has transferred to VM_Save instead.
[ VM_Restart; @restart; ]; [ VM_Restore res fref; fref = glk_fileref_create_by_prompt($01, $02, 0); if (fref == 0) jump RFailed; gg_savestr = glk_stream_open_file(fref, $02, GG_SAVESTR_ROCK); glk_fileref_destroy(fref); if (gg_savestr == 0) jump RFailed; @restore gg_savestr res; glk_stream_close(gg_savestr, 0); gg_savestr = 0; .RFailed; rfalse; ]; [ VM_Save fref len res; fref = glk_fileref_create_by_prompt(fileusage_SavedGame, filemode_Write, 0); if (fref) { gg_savestr = glk_stream_open_file(fref, filemode_Write, GG_SAVESTR_ROCK); if (gg_savestr) { @save gg_savestr res; if (res == -1) { The player actually just typed "restore". We first have to recover all the Glk objects; the values in our global variables are all wrong. GGRecoverObjects(); glk_stream_close(gg_savestr, GLK_NULL); gg_savestr = 0; return 2; } glk_stream_close(gg_savestr, GLK_NULL); gg_savestr = 0; if (res == 0) { Check that the savefile was actually written - this is mostly to account for browser limits in Parchment if (glk_fileref_does_file_exist(fref)) { gg_savestr = glk_stream_open_file(fref, filemode_Read, GG_SAVESTR_ROCK); if (gg_savestr) { glk_stream_set_position(gg_savestr, 0, seekmode_End); len = glk_stream_get_position(gg_savestr); glk_stream_close(gg_savestr, GLK_NULL); gg_savestr = 0; if (len) { We've confirmed the file exists and has content, which is about all we can do glk_fileref_destroy(fref); return 1; } Cleanup the empty file glk_fileref_delete_file(fref); } } } } glk_fileref_destroy(fref); } return 0; ];
§4. Undo. These are really emulations of the Z-machine's conventions on UNDO.
[ VM_Undo result_code; @restoreundo result_code; return (~~result_code); ]; [ VM_Save_Undo result_code; @saveundo result_code; if (result_code == -1) { GGRecoverObjects(); return 2; } return (~~result_code); ];