#include <stdlib.h>
+#include <string.h>
#include "advent.h"
#include "database.h"
#include "linenoise/linenoise.h"
-#define VRSION 25 /* bump on save format change */
+/*
+ * (ESR) This replaces a bunch of particularly nasty FORTRAN-derived code;
+ * see the history.adoc file in the source distribution for discussion.
+ */
-static void fSAVWDS(long*,long*,long*,long*,long*,long*,long*);
-#define SAVWDS(W1,W2,W3,W4,W5,W6,W7) fSAVWDS(&W1,&W2,&W3,&W4,&W5,&W6,&W7)
-static void fSAVARR(long*,long);
-#define SAVARR(ARR,N) fSAVARR(ARR,N)
-static void fSAVWRD(long,long*);
-#define SAVWRD(OP,WORD) fSAVWRD(OP,&WORD)
-static void fSAVEIO(long,long,long*);
-#define SAVEIO(OP,IN,ARR) fSAVEIO(OP,IN,ARR)
+#define VRSION 26 /* bump on save format change */
-/* Suspend/resume I/O routines (SAVWDS, SAVARR, SAVWRD) */
-
-static void fSAVWDS(long *W1, long *W2, long *W3, long *W4,
- long *W5, long *W6, long *W7)
-/* Write or read 7 variables. See SAVWRD. */
-{
- SAVWRD(0,(*W1));
- SAVWRD(0,(*W2));
- SAVWRD(0,(*W3));
- SAVWRD(0,(*W4));
- SAVWRD(0,(*W5));
- SAVWRD(0,(*W6));
- SAVWRD(0,(*W7));
-}
-
-static void fSAVARR(long arr[], long n)
-/* Write or read an array of n words. See SAVWRD. */
-{
- long i;
-
- for (i=1; i<=n; i++) {
- SAVWRD(0,arr[i]);
- }
- return;
-}
-
-static void fSAVWRD(long op, long *pword)
-/* If OP<0, start writing a file, using word to initialise encryption; save
- * word in the file. If OP>0, start reading a file; read the file to find
- * the value with which to decrypt the rest. In either case, if a file is
- * already open, finish writing/reading it and don't start a new one. If OP=0,
- * read/write a single word. Words are buffered in case that makes for more
- * efficient disk use. We also compute a simple checksum to catch elementary
- * poking within the saved file. When we finish reading/writing the file,
- * we store zero into *PWORD if there's no checksum error, else nonzero. */
-{
- static long buf[250], cksum = 0, h1, hash = 0, n = 0, state = 0;
-
- if (op != 0)
- {
- long ifvar = state;
- switch (ifvar<0 ? -1 : (ifvar>0 ? 1 : 0))
- {
- case -1:
- case 1:
- if (n == 250)SAVEIO(1,state > 0,buf);
- n=MOD(n,250)+1;
- if (state <= 0) {
- n--; buf[n]=cksum; n++;
- SAVEIO(1,false,buf);
- }
- n--; *pword=buf[n]-cksum; n++;
- SAVEIO(-1,state > 0,buf);
- state=0;
- break;
- case 0: /* FIXME: Huh? should be impossible */
- state=op;
- SAVEIO(0,state > 0,buf);
- n=1;
- if (state <= 0) {
- hash=MOD(*pword,1048576L);
- buf[0]=1234L*5678L-hash;
- }
- SAVEIO(1,true,buf);
- hash=MOD(1234L*5678L-buf[0],1048576L);
- cksum=buf[0];
- return;
- }
- }
- if (state == 0)
- return;
- if (n == 250)
- SAVEIO(1,state > 0,buf);
- n=MOD(n,250)+1;
- h1=MOD(hash*1093L+221573L,1048576L);
- hash=MOD(h1*1093L+221573L,1048576L);
- h1=MOD(h1,1234)*765432+MOD(hash,123);
- n--;
- if (state > 0)
- *pword=buf[n]+h1;
- buf[n]=*pword-h1;
- n++;
- cksum=MOD(cksum*13+*pword,1000000000L);
-}
-
-static void fSAVEIO(long op, long in, long arr[])
-/* If OP=0, ask for a file name and open a file. (If IN=true, the file is for
- * input, else output.) If OP>0, read/write ARR from/into the previously-opened
- * file. (ARR is a 250-integer array.) If OP<0, finish reading/writing the
- * file. (Finishing writing can be a no-op if a "stop" statement does it
- * automatically. Finishing reading can be a no-op as long as a subsequent
- * SAVEIO(0,false,X) will still work.) */
-{
- static FILE *fp = NULL;
- char* name;
-
- switch (op < 0 ? -1 : (op > 0 ? 1 : 0))
- {
- case -1:
- fclose(fp);
- break;
- case 0:
- while (fp == NULL) {
- name = linenoise("File name: ");
- fp = fopen(name,(in ? READ_MODE : WRITE_MODE));
- if (fp == NULL)
- printf("Can't open file %s, try again.\n", name);
- }
- linenoiseFree(name);
- break;
- case 1:
- if (in)
- IGNORE(fread(arr,sizeof(long),250,fp));
- else
- IGNORE(fwrite(arr,sizeof(long),250,fp));
- break;
- }
-}
+/*
+ * If you change the first three members, the resume function may not properly
+ * reject saves from older versions. Yes, this glues us to a hardware-
+ * dependent length of long. Later members can change, but bump the version
+ * when you do that.
+ */
+struct save_t {
+ long savetime;
+ long mode; /* not used, must be present for version detection */
+ long version;
+ struct game_t game;
+ long bird;
+ long bivalve;
+};
+struct save_t save;
int saveresume(FILE *input, bool resume)
/* Suspend and resume */
{
- int kk;
- long i;
+ long i, k;
+ FILE *fp = NULL;
+ char *name;
+
if (!resume) {
/* Suspend. Offer to save things in a file, but charging
* some points (so can't win by using saved games to retry
* battles or to start over after learning zzword). */
- SPK=201;
RSPEAK(260);
if (!YES(input,200,54,54)) return(2012);
game.saved=game.saved+5;
- kk= -1;
}
else
{
/* Resume. Read a suspended game back from a file. */
- kk=1;
if (game.loc != 1 || game.abbrev[1] != 1) {
RSPEAK(268);
if (!YES(input,200,54,54)) return(2012);
}
}
- /* Suspend vs resume cases are distinguished by the value of kk
- * (-1 for suspend, +1 for resume). */
-
- /*
- * FIXME: This is way more complicated than it needs to be in C.
- * What we ought to do is define a save-block structure that
- * includes a game state block and then use a single fread/fwrite
- * for I/O. All the SAV* functions can be scrapped.
- */
-
- DATIME(&i,&K);
- K=i+650*K;
- SAVWRD(kk,K);
- K=VRSION;
- SAVWRD(0,K);
- if (K != VRSION) {
- SETPRM(1,K/10,MOD(K,10));
- SETPRM(3,VRSION/10,MOD(VRSION,10));
- RSPEAK(269);
- return(2000);
+ while (fp == NULL) {
+ name = linenoise("File name: ");
+ fp = fopen(name,(resume ? READ_MODE : WRITE_MODE));
+ if (fp == NULL)
+ printf("Can't open file %s, try again.\n", name);
}
- /* Herewith are all the variables whose values can change during a game,
- * omitting a few (such as I, J) whose values between turns are
- * irrelevant and some whose values when a game is
- * suspended or resumed are guaranteed to match. If unsure whether a value
- * needs to be saved, include it. Overkill can't hurt. Pad the last savwds
- * with junk variables to bring it up to 7 values. */
- SAVWDS(game.abbnum,game.blklin,game.bonus,game.clock1,game.clock2,game.closed,game.closng);
- SAVWDS(game.detail,game.dflag,game.dkill,game.dtotal,game.foobar,game.holdng,game.iwest);
- SAVWDS(game.knfloc,game.limit,K,game.lmwarn,game.loc,game.newloc,game.numdie);
- SAVWDS(K,game.oldlc2,game.oldloc,game.oldobj,game.panic,game.saved,game.setup);
- SAVWDS(SPK,game.tally,game.thresh,game.trndex,game.trnluz,game.turns,OBJTXT[OYSTER]);
- SAVWDS(K,WD1,WD1X,WD2,game.wzdark,game.zzword,OBJSND[BIRD]);
- SAVWDS(OBJTXT[SIGN],game.clshnt,game.novice,K,K,K,K);
- SAVARR(game.abbrev,LOCSIZ);
- SAVARR(game.atloc,LOCSIZ);
- SAVARR(game.dloc,NDWARVES);
- SAVARR(game.dseen,NDWARVES);
- SAVARR(game.fixed,NOBJECTS);
- SAVARR(game.hinted,HNTSIZ);
- SAVARR(game.hintlc,HNTSIZ);
- SAVARR(game.link,NOBJECTS*2);
- SAVARR(game.odloc,NDWARVES);
- SAVARR(game.place,NOBJECTS);
- SAVARR(game.prop,NOBJECTS);
- SAVWRD(kk,K);
- if (K != 0) {
- RSPEAK(270);
+ linenoiseFree(name);
+
+ DATIME(&i,&k);
+ k=i+650*k;
+ if (!resume)
+ {
+ save.savetime = k;
+ save.mode = -1;
+ save.version = VRSION;
+ memcpy(&save.game, &game, sizeof(struct game_t));
+ save.bird = OBJSND[BIRD];
+ save.bivalve = OBJTXT[OYSTER];
+ IGNORE(fwrite(&save, sizeof(struct save_t), 1, fp));
+ fclose(fp);
+ RSPEAK(266);
exit(0);
+ } else {
+ IGNORE(fread(&save, sizeof(struct save_t), 1, fp));
+ fclose(fp);
+ if (save.version != VRSION) {
+ SETPRM(1,k/10,MOD(k,10));
+ SETPRM(3,VRSION/10,MOD(VRSION,10));
+ RSPEAK(269);
+ return(2000);
+ }
+ memcpy(&game, &save.game, sizeof(struct game_t));
+ OBJSND[BIRD] = save.bird;
+ OBJTXT[OYSTER] = save.bivalve;
+ game.zzword=RNDVOC(3,game.zzword);
+ return(2000);
}
- K=NUL;
- game.zzword=RNDVOC(3,game.zzword);
- if (kk > 0) return(8);
- RSPEAK(266);
- exit(0);
}
+
+/* end */