aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/agt/savegame.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2019-11-24 18:15:56 -0800
committerPaul Gilbert2019-11-27 21:10:29 -0800
commit28c3584148c49c1c6f118c5cbf13d0bf53d56726 (patch)
tree03102c6aeccebf3aec189deaf19cfd641673245e /engines/glk/agt/savegame.cpp
parentf9921a7a177dd506640211dd05751e046100a57b (diff)
downloadscummvm-rg350-28c3584148c49c1c6f118c5cbf13d0bf53d56726.tar.gz
scummvm-rg350-28c3584148c49c1c6f118c5cbf13d0bf53d56726.tar.bz2
scummvm-rg350-28c3584148c49c1c6f118c5cbf13d0bf53d56726.zip
GLK: AGT: Added subengine files
Diffstat (limited to 'engines/glk/agt/savegame.cpp')
-rw-r--r--engines/glk/agt/savegame.cpp429
1 files changed, 429 insertions, 0 deletions
diff --git a/engines/glk/agt/savegame.cpp b/engines/glk/agt/savegame.cpp
new file mode 100644
index 0000000000..68285d2575
--- /dev/null
+++ b/engines/glk/agt/savegame.cpp
@@ -0,0 +1,429 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#include "agility.h"
+#include "interp.h"
+#include "exec.h"
+
+namespace Glk {
+namespace AGT {
+
+#define SAVE_UNDO
+#define DEBUG_SAVE_SIZE 0
+
+long state_size;
+
+
+/*-------------------------------------------------------------------*/
+/* INITIALISATION ROUTINES */
+/* These initialize all of the values that can be derived from */
+/* other data in the game file or that are reset when a game */
+/* is restored */
+/* See parser.c for the interpreter's main initialisation routines */
+
+void init_vals(void)
+/* Compute quantities that can be deduced from existing data */
+{
+ int i;
+
+ quitflag = winflag = deadflag = endflag = 0;
+ cmd_saveable = 0;
+ last_he = last_she = last_it = 0;
+ totwt = totsize = 0;
+ for (i = 0; i <= maxroom - first_room; i++)
+ room[i].contents = 0;
+ player_contents = player_worn = 0;
+ for (i = 0; i <= maxnoun - first_noun; i++) {
+ if (player_has(i + first_noun)) totwt += noun[i].weight;
+ if (noun[i].location == 1) totsize += noun[i].size;
+ noun[i].something_pos_near_noun = 0;
+ noun[i].contents = noun[i].next = 0;
+ }
+ for (i = 0; i <= maxcreat - first_creat; i++)
+ creature[i].contents = creature[i].next = 0;
+ for (i = 0; i <= maxnoun - first_noun; i++) {
+ add_object(noun[i].location, i + first_noun);
+ if (noun[i].nearby_noun >= first_noun &&
+ noun[i].nearby_noun <= maxnoun)
+ noun[noun[i].nearby_noun - first_noun].something_pos_near_noun = 1;
+ }
+ for (i = 0; i <= maxcreat - first_creat; i++)
+ add_object(creature[i].location, i + first_creat);
+ objscore = 0; /* Will need to recompute this ... */
+}
+
+
+
+
+/*-------------------------------------------------------------------*/
+/* ROUTINES TO SAVE/RESTORE THE GAME STATE */
+/* These are used by RESTART and UNDO as well as SAVE and RESTORE */
+
+/* Game State format: */
+/* The first two bytes indicate the length of the block (unsigned).*/
+/* The next two bytes indicate the game file somehow (so we don't try to */
+/* restore to a different game). */
+/* After this comes the game information itself. */
+/* All values are still little-endian (that is, LSB first) */
+
+/* These are the macros for writing game information to the state block */
+/* There is no difference between signed and unsigned when storing them;
+ there will be problems when recovering them again. */
+
+#define g(ft,var) {ft,DT_DEFAULT,&var,0}
+#define r(ft,str,f) {ft,DT_DEFAULT,NULL,offsetof(str,f)}
+#define dptype {FT_DESCPTR,DT_DESCPTR,NULL,0}
+
+static file_info fi_savehead[] = {
+ g(FT_INT16, loc), g(FT_INT32, tscore), g(FT_INT16, turncnt),
+ g(FT_BYTE, statusmode),
+ g(FT_BOOL, first_visit_flag), g(FT_BOOL, newlife_flag),
+ g(FT_BOOL, room_firstdesc), g(FT_BOOL, verboseflag),
+ g(FT_BOOL, notify_flag), g(FT_BOOL, listexit_flag),
+ g(FT_BOOL, menu_mode), g(FT_BOOL, sound_on),
+ g(FT_BOOL, agt_answer), g(FT_INT32, agt_number),
+ g(FT_INT16, curr_time), g(FT_INT16, curr_lives),
+ g(FT_INT16, delta_time),
+ endrec
+};
+
+static file_info fi_saveroom[] = {
+ dptype,
+ r(FT_BOOL, room_rec, seen),
+ r(FT_BOOL, room_rec, locked_door),
+ r(FT_INT16, room_rec, oclass),
+ r(FT_INT16, room_rec, points),
+ r(FT_INT16, room_rec, light),
+ r(FT_PATHARRAY, room_rec, path),
+ r(FT_UINT32, room_rec, flag_noun_bits),
+ endrec
+};
+
+static file_info fi_savenoun[] = {
+ dptype,
+ r(FT_INT16, noun_rec, location),
+ r(FT_INT16, noun_rec, nearby_noun),
+ r(FT_INT16, noun_rec, num_shots),
+ r(FT_INT16, noun_rec, initdesc),
+ r(FT_INT16, noun_rec, oclass),
+ r(FT_INT16, noun_rec, points),
+ r(FT_INT16, noun_rec, weight),
+ r(FT_INT16, noun_rec, size),
+ r(FT_BOOL, noun_rec, on),
+ r(FT_BOOL, noun_rec, open),
+ r(FT_BOOL, noun_rec, locked),
+ r(FT_BOOL, noun_rec, movable),
+ r(FT_BOOL, noun_rec, seen),
+ r(FT_WORD, noun_rec, pos_prep),
+ r(FT_WORD, noun_rec, pos_name),
+ endrec
+};
+
+static file_info fi_savecreat[] = {
+ dptype,
+ r(FT_INT16, creat_rec, location),
+ r(FT_INT16, creat_rec, counter),
+ r(FT_INT16, creat_rec, timecounter),
+ r(FT_INT16, creat_rec, initdesc),
+ r(FT_INT16, creat_rec, oclass),
+ r(FT_INT16, creat_rec, points),
+ r(FT_BOOL, creat_rec, groupmemb),
+ r(FT_BOOL, creat_rec, hostile),
+ r(FT_BOOL, creat_rec, seen),
+ endrec
+};
+
+static file_info fi_saveustr[] = {
+ {FT_TLINE, DT_DEFAULT, NULL, 0},
+ endrec
+};
+
+
+
+uchar *getstate(uchar *gs)
+/* Returns block containing game state.
+ If gs!=NULL, uses that space as a buffer;
+ if gs==NULL, we malloc a new block and return it */
+{
+ rbool new_block; /* True if we allocate a new block */
+ long bp;
+
+ if (gs == NULL) {
+ rm_trap = 0; /* Don't exit on out-of-memory condition */
+ gs = (uchar *)rmalloc(state_size); /* This should be enough. */
+ rm_trap = 1;
+ if (gs == NULL) /* This is why we set rm_trap to 0 before calling rmalloc */
+ return NULL;
+ new_block = 1;
+ } else new_block = 0;
+
+ /* First two bytes reserved for block size, which we don't know yet.*/
+ gs[4] = game_sig & 0xFF;
+ gs[5] = (game_sig >> 8) & 0xFF;
+
+ tscore -= objscore; /* Only include "permanent" part of score;
+ objscore we can recompute on RESTORE */
+
+ /* Need to setup here */
+ set_internal_buffer(gs);
+ fi_saveroom[0].ptr = room_ptr;
+ fi_savenoun[0].ptr = noun_ptr;
+ fi_savecreat[0].ptr = creat_ptr;
+
+ bp = 6;
+ bp += write_globalrec(fi_savehead, bp);
+ bp += write_recblock(flag, FT_BYTE, FLAG_NUM + 1, bp);
+ bp += write_recblock(agt_counter, FT_INT16, CNT_NUM + 1, bp);
+ bp += write_recblock(agt_var, FT_INT32, VAR_NUM + 1, bp);
+ bp += write_recarray(room, sizeof(room_rec), rangefix(maxroom - first_room + 1),
+ fi_saveroom, bp);
+ bp += write_recarray(noun, sizeof(noun_rec), rangefix(maxnoun - first_noun + 1),
+ fi_savenoun, bp);
+ bp += write_recarray(creature, sizeof(creat_rec),
+ rangefix(maxcreat - first_creat + 1),
+ fi_savecreat, bp);
+ if (userstr != NULL)
+ bp += write_recarray(userstr, sizeof(tline), MAX_USTR, fi_saveustr, bp);
+ if (objflag != NULL)
+ bp += write_recblock(objflag, FT_BYTE, objextsize(0), bp);
+ if (objprop != NULL)
+ bp += write_recblock(objprop, FT_INT32, objextsize(1), bp);
+ set_internal_buffer(NULL);
+ gs[0] = bp & 0xFF;
+ gs[1] = (bp >> 8) & 0xFF;
+ gs[2] = (bp >> 16) & 0xFF;
+ gs[3] = (bp >> 24) & 0x7F; /* Don't trust top bit */
+ if (new_block)
+ gs = (uchar *)rrealloc(gs, bp);
+ tscore += objscore;
+ return gs;
+}
+
+
+
+void putstate(uchar *gs) { /* Restores games state. */
+ long size, bp, numrec, i;
+
+
+ size = gs[0] + (((long)gs[1]) << 8) + (((long)gs[2]) << 16) + (((long)gs[3]) << 24);
+ if (size != state_size) {
+ writeln("Size difference in save files!");
+ agt_delay(3);
+ return;
+ }
+ if (gs[4] + (((long)gs[5]) << 8) != game_sig) {
+ writestr("This appears to be a save file for a different game. Is this"
+ " from an earlier chapter in a multi-part game such as"
+ " Klaustrophobia");
+ if (yesno("?"))
+ skip_descr = 1; /* We don't want to overwrite the descriptions
+ with the pointers from the save file. */
+ else {
+ writestr("Do you want to try using it anyhow (WARNING: This could"
+ " crash the interpreter)");
+ if (!(yesno("?"))) {
+ writeln("Command cancelled!");
+ agt_delay(3);
+ return;
+ }
+ }
+ }
+
+
+ /* setup... */
+ set_internal_buffer(gs);
+ fi_saveroom[0].ptr = room_ptr;
+ fi_savenoun[0].ptr = noun_ptr;
+ fi_savecreat[0].ptr = creat_ptr;
+ bp = 6;
+
+ read_globalrec(fi_savehead, 0, bp, 0);
+ bp += compute_recsize(fi_savehead);
+ read_recblock(flag, FT_BYTE, FLAG_NUM + 1, bp, 0);
+ bp += ft_leng[FT_BYTE] * (FLAG_NUM + 1);
+ read_recblock(agt_counter, FT_INT16, CNT_NUM + 1, bp, 0);
+ bp += ft_leng[FT_INT16] * (CNT_NUM + 1);
+ read_recblock(agt_var, FT_INT32, VAR_NUM + 1, bp, 0);
+ bp += ft_leng[FT_INT32] * (VAR_NUM + 1);
+
+ numrec = rangefix(maxroom - first_room + 1);
+ read_recarray(room, sizeof(room_rec), numrec, fi_saveroom, 0, bp, 0);
+ bp += compute_recsize(fi_saveroom) * numrec;
+ numrec = rangefix(maxnoun - first_noun + 1);
+ read_recarray(noun, sizeof(noun_rec), numrec, fi_savenoun, 0, bp, 0);
+ bp += compute_recsize(fi_savenoun) * numrec;
+ numrec = rangefix(maxcreat - first_creat + 1);
+ read_recarray(creature, sizeof(creat_rec), numrec, fi_savecreat, 0, bp, 0);
+ bp += compute_recsize(fi_savecreat) * numrec;
+ if (userstr != NULL) {
+ read_recarray(userstr, sizeof(tline), MAX_USTR, fi_saveustr, 0, bp, 0);
+ bp += ft_leng[FT_TLINE] * MAX_USTR;
+ }
+ if (objflag != NULL) {
+ i = objextsize(0);
+ read_recblock(objflag, FT_BYTE, i, bp, 0);
+ bp += ft_leng[FT_BYTE] * i;
+ }
+ if (objprop != NULL) {
+ i = objextsize(1);
+ read_recblock(objprop, FT_INT32, i, bp, 0);
+ bp += ft_leng[FT_INT32] * i;
+ }
+ set_internal_buffer(NULL);
+
+ if (skip_descr) /* Need to "fix" position information. This is a hack. */
+ /* Basically, this sets the position of each object to its default */
+ /* The problem here is that the usual position info is invalid-- we've
+ changed games, and hence dictionaries */
+ for (i = 0; i < maxnoun - first_noun; i++) {
+ if (noun[i].position != NULL && noun[i].position[0] != 0)
+ noun[i].pos_prep = -1;
+ else noun[i].pos_prep = 0;
+ }
+ else /* Rebuild position information */
+ for (i = 0; i < maxnoun - first_noun; i++)
+ if (noun[i].pos_prep == -1)
+ noun[i].position = noun[i].initpos;
+ else
+ noun[i].position = NULL;
+
+ init_vals();
+ skip_descr = 0; /* If we set this to 1, restore it to its original state */
+ /* Now do some simple consistancy checking on major variables */
+ if (loc > maxroom || loc < 0 || turncnt < 0 ||
+ curr_lives < 0 || curr_lives > max_lives) {
+ error("Error: Save file inconsistent.");
+ }
+}
+
+void init_state_sys(void)
+/* Initializes the state saving mechanisms */
+/* Mainly it just computes the size of a state block */
+{
+ state_size = compute_recsize(fi_savehead)
+ + compute_recsize(fi_saveroom) * rangefix(maxroom - first_room + 1)
+ + compute_recsize(fi_savenoun) * rangefix(maxnoun - first_noun + 1)
+ + compute_recsize(fi_savecreat) * rangefix(maxcreat - first_creat + 1)
+ + ft_leng[FT_BYTE] * (FLAG_NUM + 1)
+ + ft_leng[FT_INT16] * (CNT_NUM + 1)
+ + ft_leng[FT_INT32] * (VAR_NUM + 1)
+ + ft_leng[FT_BYTE] * objextsize(0)
+ + ft_leng[FT_INT32] * objextsize(1)
+ + 6; /* Six bytes in header */
+ if (userstr != NULL) state_size += ft_leng[FT_TLINE] * MAX_USTR;
+}
+
+
+/*-------------------------------------------------------------------*/
+/* SAVE FILE ROUTINES */
+
+void savegame(void) {
+ genfile savefile;
+ uchar *gs;
+ long size;
+
+#ifndef UNDO_SAVE
+ gs = getstate(NULL);
+#else
+ gs = undo_state;
+#endif
+ if (gs == NULL) {
+ writeln("Insufficiant memory to support SAVE.");
+ return;
+ }
+ savefile = get_user_file(1);
+ if (!filevalid(savefile, fSAV)) {
+ writeln("That is not a valid save file.");
+ return;
+ }
+ size = gs[0] + (((long)gs[1]) << 8) + (((long)gs[2]) << 16) + (((long)gs[3]) << 24);
+ if (!binwrite(savefile, gs, size, 1, 0))
+ writeln("Error writing save file.");
+#ifndef UNDO_SAVE
+ rfree(gs);
+#endif
+ writeclose(savefile, NO_FILE_ID);
+}
+
+
+rbool loadgame(void)
+/* 1=success, 0=failure */
+{
+ genfile loadfile;
+ long size;
+ uchar *gs;
+ const char *errstr;
+
+ loadfile = get_user_file(2);
+ if (!filevalid(loadfile, fSAV)) {
+ writeln("Unable to open file.");
+ return 0;
+ }
+ size = binsize(loadfile);
+ if (size == -1) {
+ writeln("Could not access file.");
+ readclose(loadfile);
+ return 0;
+ }
+ gs = (uchar *)rmalloc(size);
+ if (!binread(loadfile, gs, size, 1, &errstr)) {
+ writeln("Error reading file.");
+ rfree(gs);
+ readclose(loadfile);
+ return 0;
+ }
+ readclose(loadfile);
+ if (size != gs[0] + (((long)gs[1]) << 8) + (((long)gs[2]) << 16) + (((long)gs[3]) << 24)) {
+ if (size == gs[0] + (((long)gs[1]) << 8)) {
+ /* Old save file format; patch to look like new format */
+ gs = (uchar *)rrealloc(gs, size + 2);
+ memmove(gs + 4, gs + 2, size - 2);
+ gs[2] = gs[3] = 0;
+ } else {
+ writeln("Save file corrupted or invalid.");
+ rfree(gs);
+ return 0;
+ }
+ }
+ putstate(gs);
+ rfree(gs);
+ set_statline();
+ look_room();
+ return 1;
+}
+
+void restart_game(void) {
+ putstate(restart_state);
+ agt_clrscr();
+ set_statline();
+ do_look = do_autoverb = 1;
+ if (intro_ptr.size > 0) {
+ print_descr(intro_ptr, 1);
+ wait_return();
+ agt_clrscr();
+ }
+ newroom();
+}
+
+} // End of namespace AGT
+} // End of namespace Glk