adventures: Commit regenerated games with save/load and bug fixes
authorAlan Cox <alan@linux.intel.com>
Wed, 1 Jun 2016 17:27:00 +0000 (18:27 +0100)
committerAlan Cox <alan@linux.intel.com>
Wed, 1 Jun 2016 17:27:00 +0000 (18:27 +0100)
24 files changed:
Applications/games/adv01.c
Applications/games/adv02.c
Applications/games/adv03.c
Applications/games/adv04.c
Applications/games/adv05.c
Applications/games/adv06.c
Applications/games/adv07.c
Applications/games/adv08.c
Applications/games/adv09.c
Applications/games/adv10.c
Applications/games/adv11.c
Applications/games/adv12.c
Applications/games/adv13.c
Applications/games/myst01.c
Applications/games/myst02.c
Applications/games/myst03.c
Applications/games/myst04.c
Applications/games/myst05.c
Applications/games/myst06.c
Applications/games/myst07.c
Applications/games/myst08.c
Applications/games/myst09.c
Applications/games/myst10.c
Applications/games/myst11.c

index db87e7b..ec8b45a 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 66
 #define WORDSIZE 3
+#define GAME_MAGIC 416
 #include <stdint.h>
 
 struct location {
@@ -1386,6 +1387,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1398,18 +1412,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1420,6 +1427,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1433,7 +1443,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1686,13 +1696,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1704,6 +1707,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1858,7 +1867,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1870,19 +1879,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1890,27 +1899,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -1922,11 +1931,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -1938,7 +1947,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -1950,10 +1959,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -1975,8 +1984,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -1998,9 +2007,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2021,8 +2030,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2036,11 +2045,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2061,15 +2070,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2084,12 +2093,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2115,16 +2164,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2132,17 +2181,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2163,24 +2212,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2191,37 +2241,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2234,10 +2284,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2332,7 +2382,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2348,10 +2398,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2368,7 +2418,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2378,23 +2428,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2447,7 +2497,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2458,7 +2508,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2478,21 +2528,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index ebba3a8..0de5941 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 67
 #define WORDSIZE 3
+#define GAME_MAGIC 408
 #include <stdint.h>
 
 struct location {
@@ -1462,6 +1463,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1474,18 +1488,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1496,6 +1503,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1509,7 +1519,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1762,13 +1772,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1780,6 +1783,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1934,7 +1943,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1946,19 +1955,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1966,27 +1975,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -1998,11 +2007,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2014,7 +2023,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2026,10 +2035,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2051,8 +2060,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2074,9 +2083,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2097,8 +2106,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2112,11 +2121,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2137,15 +2146,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2160,12 +2169,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2191,16 +2240,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2208,17 +2257,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2239,24 +2288,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2267,37 +2317,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2310,10 +2360,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2408,7 +2458,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2424,10 +2474,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2444,7 +2494,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2454,23 +2504,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2523,7 +2573,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2534,7 +2584,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2554,21 +2604,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 5497db8..c381119 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 54
 #define WORDSIZE 3
+#define GAME_MAGIC 306
 #include <stdint.h>
 
 struct location {
@@ -1250,6 +1251,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1262,18 +1276,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1284,6 +1291,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1297,7 +1307,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1550,13 +1560,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1568,6 +1571,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1722,7 +1731,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1734,19 +1743,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1754,27 +1763,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -1786,11 +1795,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -1802,7 +1811,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -1814,10 +1823,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -1839,8 +1848,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -1862,9 +1871,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -1885,8 +1894,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -1900,11 +1909,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -1925,15 +1934,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -1948,12 +1957,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -1979,16 +2028,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -1996,17 +2045,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2027,24 +2076,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2055,37 +2105,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2098,10 +2148,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2196,7 +2246,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2212,10 +2262,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2232,7 +2282,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2242,23 +2292,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2311,7 +2361,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2322,7 +2372,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2342,21 +2392,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 5367272..6b2ddda 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 66
 #define WORDSIZE 3
+#define GAME_MAGIC 119
 #include <stdint.h>
 
 struct location {
@@ -1526,6 +1527,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1538,18 +1552,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1560,6 +1567,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1573,7 +1583,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1826,13 +1836,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1844,6 +1847,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1998,7 +2007,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2010,19 +2019,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2030,27 +2039,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2062,11 +2071,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2078,7 +2087,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2090,10 +2099,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2115,8 +2124,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2138,9 +2147,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2161,8 +2170,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2176,11 +2185,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2201,15 +2210,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2224,12 +2233,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2255,16 +2304,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2272,17 +2321,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2303,24 +2352,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2331,37 +2381,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2374,10 +2424,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2472,7 +2522,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2488,10 +2538,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2508,7 +2558,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2518,23 +2568,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2587,7 +2637,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2598,7 +2648,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2618,21 +2668,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index b133b78..8e10495 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 73
 #define WORDSIZE 3
+#define GAME_MAGIC 115
 #include <stdint.h>
 
 struct location {
@@ -1592,6 +1593,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1604,18 +1618,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1626,6 +1633,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1639,7 +1649,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1892,13 +1902,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1910,6 +1913,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2064,7 +2073,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2076,19 +2085,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2096,27 +2105,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2128,11 +2137,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2144,7 +2153,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2156,10 +2165,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2181,8 +2190,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2204,9 +2213,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2227,8 +2236,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2242,11 +2251,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2267,15 +2276,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2290,12 +2299,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2321,16 +2370,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2338,17 +2387,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2369,24 +2418,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2397,37 +2447,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2440,10 +2490,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2538,7 +2588,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2554,10 +2604,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2574,7 +2624,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2584,23 +2634,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2653,7 +2703,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2664,7 +2714,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2684,21 +2734,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 53d3219..20aa30c 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 56
 #define WORDSIZE 4
+#define GAME_MAGIC 119
 #include <stdint.h>
 
 struct location {
@@ -1565,6 +1566,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1577,18 +1591,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1599,6 +1606,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1612,7 +1622,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1865,13 +1875,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1883,6 +1886,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2037,7 +2046,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2049,19 +2058,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2069,27 +2078,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2101,11 +2110,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2117,7 +2126,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2129,10 +2138,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2154,8 +2163,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2177,9 +2186,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2200,8 +2209,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2215,11 +2224,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2240,15 +2249,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2263,12 +2272,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2294,16 +2343,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2311,17 +2360,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2342,24 +2391,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2370,37 +2420,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2413,10 +2463,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2511,7 +2561,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2527,10 +2577,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2547,7 +2597,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2557,23 +2607,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2626,7 +2676,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2637,7 +2687,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2657,21 +2707,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 22d642a..55c786a 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 75
 #define WORDSIZE 4
+#define GAME_MAGIC 145
 #include <stdint.h>
 
 struct location {
@@ -1640,6 +1641,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1652,18 +1666,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1674,6 +1681,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1687,7 +1697,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1940,13 +1950,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1958,6 +1961,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2112,7 +2121,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2124,19 +2133,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2144,27 +2153,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2176,11 +2185,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2192,7 +2201,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2204,10 +2213,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2229,8 +2238,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2252,9 +2261,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2275,8 +2284,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2290,11 +2299,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2315,15 +2324,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2338,12 +2347,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2369,16 +2418,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2386,17 +2435,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2417,24 +2466,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2445,37 +2495,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2488,10 +2538,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2586,7 +2636,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2602,10 +2652,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2622,7 +2672,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2632,23 +2682,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2701,7 +2751,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2712,7 +2762,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2732,21 +2782,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 044fb8f..9d162d6 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 101
 #define WORDSIZE 3
+#define GAME_MAGIC 125
 #include <stdint.h>
 
 struct location {
@@ -1780,6 +1781,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1792,18 +1806,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1814,6 +1821,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1827,7 +1837,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -2080,13 +2090,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -2098,6 +2101,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2252,7 +2261,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2264,19 +2273,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2284,27 +2293,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2316,11 +2325,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2332,7 +2341,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2344,10 +2353,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2369,8 +2378,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2392,9 +2401,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2415,8 +2424,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2430,11 +2439,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2455,15 +2464,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2478,12 +2487,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2509,16 +2558,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2526,17 +2575,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2557,24 +2606,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2585,37 +2635,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2628,10 +2678,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2726,7 +2776,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2742,10 +2792,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2762,7 +2812,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2772,23 +2822,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2841,7 +2891,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2852,7 +2902,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2872,21 +2922,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index d85dd3d..755f385 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 78
 #define WORDSIZE 4
+#define GAME_MAGIC 223
 #include <stdint.h>
 
 struct location {
@@ -1827,6 +1828,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1839,18 +1853,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1861,6 +1868,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1874,7 +1884,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -2127,13 +2137,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -2145,6 +2148,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2299,7 +2308,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2311,19 +2320,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2331,27 +2340,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2363,11 +2372,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2379,7 +2388,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2391,10 +2400,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2416,8 +2425,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2439,9 +2448,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2462,8 +2471,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2477,11 +2486,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2502,15 +2511,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2525,12 +2534,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2556,16 +2605,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2573,17 +2622,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2604,24 +2653,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2632,37 +2682,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2675,10 +2725,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2773,7 +2823,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2789,10 +2839,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2809,7 +2859,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2819,23 +2869,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2888,7 +2938,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2899,7 +2949,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2919,21 +2969,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index e68dee4..dec5658 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 59
 #define WORDSIZE 4
+#define GAME_MAGIC 123
 #include <stdint.h>
 
 struct location {
@@ -1707,6 +1708,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1719,18 +1733,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1741,6 +1748,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1754,7 +1764,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -2007,13 +2017,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -2025,6 +2028,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2179,7 +2188,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2191,19 +2200,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2211,27 +2220,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2243,11 +2252,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2259,7 +2268,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2271,10 +2280,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2296,8 +2305,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2319,9 +2328,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2342,8 +2351,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2357,11 +2366,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2382,15 +2391,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2405,12 +2414,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2436,16 +2485,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2453,17 +2502,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2484,24 +2533,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2512,37 +2562,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2555,10 +2605,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2653,7 +2703,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2669,10 +2719,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2689,7 +2739,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2699,23 +2749,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2768,7 +2818,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2779,7 +2829,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2799,21 +2849,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 147e253..48071e8 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 49
 #define WORDSIZE 4
+#define GAME_MAGIC 133
 #include <stdint.h>
 
 struct location {
@@ -1582,6 +1583,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1594,18 +1608,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1616,6 +1623,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1629,7 +1639,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1882,13 +1892,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1900,6 +1903,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2054,7 +2063,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2066,19 +2075,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2086,27 +2095,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2118,11 +2127,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2134,7 +2143,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2146,10 +2155,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2171,8 +2180,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2194,9 +2203,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2217,8 +2226,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2232,11 +2241,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2257,15 +2266,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2280,12 +2289,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2311,16 +2360,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2328,17 +2377,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2359,24 +2408,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2387,37 +2437,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2430,10 +2480,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2528,7 +2578,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2544,10 +2594,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2564,7 +2614,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2574,23 +2624,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2643,7 +2693,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2654,7 +2704,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2674,21 +2724,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 2e70772..f0bec3b 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 80
 #define WORDSIZE 4
+#define GAME_MAGIC 184
 #include <stdint.h>
 
 struct location {
@@ -1727,6 +1728,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1739,18 +1753,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1761,6 +1768,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1774,7 +1784,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -2027,13 +2037,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -2045,6 +2048,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2199,7 +2208,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2211,19 +2220,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2231,27 +2240,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2263,11 +2272,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2279,7 +2288,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2291,10 +2300,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2316,8 +2325,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2339,9 +2348,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2362,8 +2371,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2377,11 +2386,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2402,15 +2411,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2425,12 +2434,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2456,16 +2505,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2473,17 +2522,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2504,24 +2553,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2532,37 +2582,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2575,10 +2625,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2673,7 +2723,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2689,10 +2739,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2709,7 +2759,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2719,23 +2769,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2788,7 +2838,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2799,7 +2849,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2819,21 +2869,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 15f0414..44a8cf4 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 76
 #define WORDSIZE 5
+#define GAME_MAGIC 126
 #include <stdint.h>
 
 struct location {
@@ -1816,6 +1817,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1828,18 +1842,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1850,6 +1857,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1863,7 +1873,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -2116,13 +2126,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -2134,6 +2137,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2288,7 +2297,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2300,19 +2309,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2320,27 +2329,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2352,11 +2361,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2368,7 +2377,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2380,10 +2389,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2405,8 +2414,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2428,9 +2437,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2451,8 +2460,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2466,11 +2475,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2491,15 +2500,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2514,12 +2523,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2545,16 +2594,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2562,17 +2611,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2593,24 +2642,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2621,37 +2671,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2664,10 +2714,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2762,7 +2812,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2778,10 +2828,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2798,7 +2848,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2808,23 +2858,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2877,7 +2927,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2888,7 +2938,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2908,21 +2958,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index c905d17..b8b7b36 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 49
 #define WORDSIZE 4
+#define GAME_MAGIC 463
 #include <stdint.h>
 
 struct location {
@@ -1393,6 +1394,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1405,18 +1419,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1427,6 +1434,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1440,7 +1450,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1693,13 +1703,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1711,6 +1714,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1865,7 +1874,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1877,19 +1886,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1897,27 +1906,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -1929,11 +1938,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -1945,7 +1954,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -1957,10 +1966,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -1982,8 +1991,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2005,9 +2014,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2028,8 +2037,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2043,11 +2052,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2068,15 +2077,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2091,12 +2100,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2122,16 +2171,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2139,17 +2188,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2170,24 +2219,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2198,37 +2248,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2241,10 +2291,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2339,7 +2389,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2355,10 +2405,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2375,7 +2425,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2385,23 +2435,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2454,7 +2504,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2465,7 +2515,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2485,21 +2535,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index ad980ca..5c5d50c 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 63
 #define WORDSIZE 4
+#define GAME_MAGIC 515
 #include <stdint.h>
 
 struct location {
@@ -1409,6 +1410,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1421,18 +1435,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1443,6 +1450,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1456,7 +1466,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1709,13 +1719,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1727,6 +1730,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1881,7 +1890,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1893,19 +1902,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1913,27 +1922,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -1945,11 +1954,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -1961,7 +1970,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -1973,10 +1982,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -1998,8 +2007,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2021,9 +2030,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2044,8 +2053,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2059,11 +2068,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2084,15 +2093,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2107,12 +2116,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2138,16 +2187,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2155,17 +2204,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2186,24 +2235,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2214,37 +2264,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2257,10 +2307,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2355,7 +2405,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2371,10 +2421,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2391,7 +2441,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2401,23 +2451,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2470,7 +2520,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2481,7 +2531,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2501,21 +2551,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 104083c..62f0bd9 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 65
 #define WORDSIZE 4
+#define GAME_MAGIC 33061
 #include <stdint.h>
 
 struct location {
@@ -1426,6 +1427,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1438,18 +1452,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1460,6 +1467,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1473,7 +1483,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1726,13 +1736,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1744,6 +1747,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1898,7 +1907,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1910,19 +1919,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1930,27 +1939,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -1962,11 +1971,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -1978,7 +1987,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -1990,10 +1999,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2015,8 +2024,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2038,9 +2047,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2061,8 +2070,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2076,11 +2085,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2101,15 +2110,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2124,12 +2133,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2155,16 +2204,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2172,17 +2221,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2203,24 +2252,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2231,37 +2281,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2274,10 +2324,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2372,7 +2422,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2388,10 +2438,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2408,7 +2458,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2418,23 +2468,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2487,7 +2537,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2498,7 +2548,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2518,21 +2568,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 0b10444..6cff466 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 91
 #define WORDSIZE 4
+#define GAME_MAGIC 734
 #include <stdint.h>
 
 struct location {
@@ -1674,6 +1675,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1686,18 +1700,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1708,6 +1715,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1721,7 +1731,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1974,13 +1984,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1992,6 +1995,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2146,7 +2155,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2158,19 +2167,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2178,27 +2187,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2210,11 +2219,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2226,7 +2235,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2238,10 +2247,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2263,8 +2272,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2286,9 +2295,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2309,8 +2318,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2324,11 +2333,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2349,15 +2358,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2372,12 +2381,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2403,16 +2452,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2420,17 +2469,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2451,24 +2500,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2479,37 +2529,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2522,10 +2572,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2620,7 +2670,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2636,10 +2686,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2656,7 +2706,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2666,23 +2716,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2735,7 +2785,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2746,7 +2796,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2766,21 +2816,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 341677f..c6597b8 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 91
 #define WORDSIZE 4
+#define GAME_MAGIC 562
 #include <stdint.h>
 
 struct location {
@@ -1756,6 +1757,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1768,18 +1782,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1790,6 +1797,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1803,7 +1813,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -2056,13 +2066,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -2074,6 +2077,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2228,7 +2237,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2240,19 +2249,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2260,27 +2269,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2292,11 +2301,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2308,7 +2317,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2320,10 +2329,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2345,8 +2354,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2368,9 +2377,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2391,8 +2400,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2406,11 +2415,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2431,15 +2440,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2454,12 +2463,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2485,16 +2534,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2502,17 +2551,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2533,24 +2582,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2561,37 +2611,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2604,10 +2654,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2702,7 +2752,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2718,10 +2768,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2738,7 +2788,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2748,23 +2798,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2817,7 +2867,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2828,7 +2878,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2848,21 +2898,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 148fce4..0e9b486 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 66
 #define WORDSIZE 4
+#define GAME_MAGIC 427
 #include <stdint.h>
 
 struct location {
@@ -1417,6 +1418,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1429,18 +1443,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1451,6 +1458,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1464,7 +1474,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1717,13 +1727,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1735,6 +1738,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1889,7 +1898,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1901,19 +1910,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1921,27 +1930,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -1953,11 +1962,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -1969,7 +1978,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -1981,10 +1990,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2006,8 +2015,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2029,9 +2038,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2052,8 +2061,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2067,11 +2076,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2092,15 +2101,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2115,12 +2124,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2146,16 +2195,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2163,17 +2212,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2194,24 +2243,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2222,37 +2272,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2265,10 +2315,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2363,7 +2413,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2379,10 +2429,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2399,7 +2449,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2409,23 +2459,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2478,7 +2528,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2489,7 +2539,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2509,21 +2559,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 003dd82..181bb56 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 66
 #define WORDSIZE 4
+#define GAME_MAGIC 494
 #include <stdint.h>
 
 struct location {
@@ -1410,6 +1411,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1422,18 +1436,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1444,6 +1451,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1457,7 +1467,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1710,13 +1720,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1728,6 +1731,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1882,7 +1891,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1894,19 +1903,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1914,27 +1923,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -1946,11 +1955,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -1962,7 +1971,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -1974,10 +1983,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -1999,8 +2008,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2022,9 +2031,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2045,8 +2054,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2060,11 +2069,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2085,15 +2094,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2108,12 +2117,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2139,16 +2188,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2156,17 +2205,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2187,24 +2236,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2215,37 +2265,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2258,10 +2308,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2356,7 +2406,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2372,10 +2422,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2392,7 +2442,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2402,23 +2452,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2471,7 +2521,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2482,7 +2532,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2502,21 +2552,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index e6da026..56774df 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 50
 #define WORDSIZE 4
+#define GAME_MAGIC 784
 #include <stdint.h>
 
 struct location {
@@ -1523,6 +1524,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1535,18 +1549,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1557,6 +1564,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1570,7 +1580,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1823,13 +1833,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1841,6 +1844,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1995,7 +2004,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2007,19 +2016,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2027,27 +2036,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2059,11 +2068,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2075,7 +2084,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2087,10 +2096,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2112,8 +2121,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2135,9 +2144,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2158,8 +2167,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2173,11 +2182,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2198,15 +2207,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2221,12 +2230,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2252,16 +2301,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2269,17 +2318,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2300,24 +2349,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2328,37 +2378,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2371,10 +2421,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2469,7 +2519,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2485,10 +2535,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2505,7 +2555,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2515,23 +2565,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2584,7 +2634,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2595,7 +2645,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2615,21 +2665,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 03a421c..aa0e7c3 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 60
 #define WORDSIZE 4
+#define GAME_MAGIC 532
 #include <stdint.h>
 
 struct location {
@@ -1527,6 +1528,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1539,18 +1553,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1561,6 +1568,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1574,7 +1584,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1827,13 +1837,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1845,6 +1848,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1999,7 +2008,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2011,19 +2020,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2031,27 +2040,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2063,11 +2072,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2079,7 +2088,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2091,10 +2100,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2116,8 +2125,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2139,9 +2148,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2162,8 +2171,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2177,11 +2186,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2202,15 +2211,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2225,12 +2234,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2256,16 +2305,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2273,17 +2322,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2304,24 +2353,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2332,37 +2382,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2375,10 +2425,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2473,7 +2523,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2489,10 +2539,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2509,7 +2559,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2519,23 +2569,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2588,7 +2638,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2599,7 +2649,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2619,21 +2669,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 4771a8b..5ef8097 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 74
 #define WORDSIZE 4
+#define GAME_MAGIC 854
 #include <stdint.h>
 
 struct location {
@@ -1479,6 +1480,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1491,18 +1505,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1513,6 +1520,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1526,7 +1536,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1779,13 +1789,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1797,6 +1800,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -1951,7 +1960,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -1963,19 +1972,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -1983,27 +1992,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2015,11 +2024,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2031,7 +2040,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2043,10 +2052,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2068,8 +2077,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2091,9 +2100,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2114,8 +2123,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2129,11 +2138,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2154,15 +2163,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2177,12 +2186,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2208,16 +2257,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2225,17 +2274,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2256,24 +2305,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2284,37 +2334,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2327,10 +2377,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2425,7 +2475,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2441,10 +2491,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2461,7 +2511,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2471,23 +2521,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2540,7 +2590,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2551,7 +2601,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2571,21 +2621,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }
index 417363e..b4efa67 100644 (file)
@@ -1,5 +1,6 @@
 #define NUM_OBJ 58
 #define WORDSIZE 4
+#define GAME_MAGIC 551
 #include <stdint.h>
 
 struct location {
@@ -1559,6 +1560,19 @@ const uint8_t automap[] = {
 
 static jmp_buf restart;
 
+struct savearea {
+  uint16_t magic;
+  uint8_t carried;
+  uint8_t lighttime;
+  uint8_t location;
+  uint8_t objloc[NUM_OBJ];
+  uint8_t roomsave[6];
+  uint8_t savedroom;
+  uint32_t bitflags;
+  int16_t counter;
+  int16_t counter_array[16];
+};
+
 static char linebuf[81];
 static char *nounbuf;
 static char wordbuf[WORDSIZE + 1];
@@ -1571,18 +1585,11 @@ static uint8_t actmatch;
 static uint8_t continuation;
 static uint16_t *param;
 static uint16_t param_buf[5];
-static uint8_t carried;
-static uint8_t lighttime;
-static uint8_t location;
-static uint8_t objloc[NUM_OBJ];
-static uint8_t roomsave[6];
-static uint8_t savedroom;
-static uint32_t bitflags;
-static int16_t counter;
-static int16_t counter_array[16];
 static uint8_t redraw;
 static uint8_t rows, cols;
 
+static struct savearea game;
+
 static void error(const char *p);
 
 #define VERB_GO                1
@@ -1593,6 +1600,9 @@ static void error(const char *p);
 #define DARKFLAG       15
 #define LIGHT_SOURCE   9
 
+/* Define this because 1 << n might be 16bit */
+#define ONEBIT         ((uint32_t)1)
+
 #define REDRAW         1
 #define REDRAW_MAYBE   2
 
@@ -1606,7 +1616,7 @@ static char wbuf[81];
 static int wbp = 0;
 static int xpos = 0, ypos = 0;
 static int bottom;
-WINDOW *topwin, *botwin, *curwin;
+static WINDOW *topwin, *botwin, *curwin;
 
 static void flush_word(void)
 {
@@ -1859,13 +1869,6 @@ static void strout_upper(const uint8_t *p)
   strout_lower(p);
 }
 
-static char readchar(void)
-{
-  char c;
-  if (read(0, &c, 1) < 1)
-    return -1;
-  return c;
-}
 
 static void line_input(void)
 {
@@ -1877,6 +1880,12 @@ static void line_input(void)
     linebuf[l-1] = 0;
 }
 
+static char readchar(void)
+{
+  line_input();
+  return *linebuf;
+}
+
 static void begin_upper(void)
 {
   strout_upper("\n\n\n\n");
@@ -2031,7 +2040,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
   for (i = 0; i < n; i++) {
     uint8_t opc = *p++;
     uint16_t par = *p++ | ((opc & 0xE0) >> 5);
-    uint8_t op = objloc[par];
+    uint8_t op = game.objloc[par];
     opc &= 0x1F;
 
     switch(opc) {
@@ -2043,19 +2052,19 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 2:
-        if (op != location)
+        if (op != game.location)
           return NULL;
         break;
       case 3:
-        if (op != 255 && op != location)
+        if (op != 255 && op != game.location)
           return NULL;
         break;
       case 4:
-        if (location != par)
+        if (game.location != par)
           return NULL;
         break;
       case 5:
-        if (op == location)
+        if (op == game.location)
           return NULL;
         break;
       case 6:
@@ -2063,27 +2072,27 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 7:
-        if (location == par)
+        if (game.location == par)
           return NULL;
         break;
       case 8:
-        if (!(bitflags & (1 << par)))
+        if (!(game.bitflags & (ONEBIT << par)))
           return NULL;
         break;
       case 9:
-        if (bitflags & (1 << par))
+        if (game.bitflags & (ONEBIT << par))
           return NULL;
         break;
       case 10:
-        if (!carried)
+        if (!game.carried)
           return NULL;
         break;
       case 11:
-        if (carried)
+        if (game.carried)
           return NULL;
         break;
       case 12:
-        if (op == 255 || op == location)
+        if (op == 255 || op == game.location)
           return NULL;
         break;
       case 13:
@@ -2095,11 +2104,11 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 15:
-        if (counter > par)
+        if (game.counter > par)
           return NULL;
         break;
       case 16:
-        if (counter < par)
+        if (game.counter < par)
           return NULL;
         break;
       case 17:
@@ -2111,7 +2120,7 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
           return NULL;
         break;
       case 19:
-        if (counter != par)
+        if (game.counter != par)
           return NULL;
         break;
       default:
@@ -2123,10 +2132,10 @@ static const uint8_t *run_conditions(const uint8_t *p, uint8_t n)
 
 uint8_t islight(void)
 {
-  uint8_t l = objloc[LIGHT_SOURCE];
-  if (!(bitflags & (1 << DARKFLAG)))
+  uint8_t l = game.objloc[LIGHT_SOURCE];
+  if (!(game.bitflags & (ONEBIT << DARKFLAG)))
     return 1;
-  if (l == 255 || l == location)
+  if (l == 255 || l == game.location)
     return 1;
   return 0;
 }
@@ -2148,8 +2157,8 @@ static void action_look(void)
     end_upper();
     return;
   }
-  p = locdata[location].text;
-  e = locdata[location].exit;
+  p = locdata[game.location].text;
+  e = locdata[game.location].exit;
   if (*p == '*')
     p++;
   else
@@ -2171,9 +2180,9 @@ static void action_look(void)
     strout_upper(nonestr);
   strout_upper(dotnewline);
   f = 1;
-  e = objloc;
-  while(e < objloc + NUM_OBJ) {
-    if (*e++ == location) {
+  e = game.objloc;
+  while(e < game.objloc + NUM_OBJ) {
+    if (*e++ == game.location) {
       if (f) {
         strout_upper(canalsosee);
         f = 0;
@@ -2194,8 +2203,8 @@ static void action_delay(void)
 static void action_dead(void)
 {
   strout_lower(dead);
-  bitflags &= ~(1 << DARKFLAG);
-  location = lastloc;
+  game.bitflags &= ~(ONEBIT << DARKFLAG);
+  game.location = lastloc;
   action_look();
 }
 
@@ -2209,11 +2218,11 @@ static void action_quit(void)
 
 static void action_score(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t t = 0, s = 0;
 
-  while(p < objloc + NUM_OBJ) {
+  while(p < game.objloc + NUM_OBJ) {
     if (*m[0] == '*') {
       t++;
       if (*p == treasure)
@@ -2234,15 +2243,15 @@ static void action_score(void)
 
 static void action_inventory(void)
 {
-  uint8_t *p = objloc;
+  uint8_t *p = game.objloc;
   const uint8_t **m = objtext;
   uint8_t f = 1;
 
   strout_lower(carrying);
-  if (carried == 0)
+  if (game.carried == 0)
     strout_lower(nothing);
   else {  
-    while(p < objloc + NUM_OBJ) {
+    while(p < game.objloc + NUM_OBJ) {
       if (*p == 255) {
         if (!f)
           strout_lower(dashstr);
@@ -2257,12 +2266,52 @@ static void action_inventory(void)
   strout_lower(dotnewline);
 }
 
+static char *filename(void)
+{
+  strout_lower("File name ? ");
+  line_input();
+  return skip_spaces(linebuf);
+}
+
+static void action_save(void)
+{
+  int fd;
+  char *p = filename();
+  if (*p == 0)
+    return;
+  game.magic = GAME_MAGIC;
+  fd = open(p, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  if (fd == -1 || write(fd, &game, sizeof(game)) != sizeof(game) || close(fd) == -1)
+    strout_lower("Save failed.\n");
+  close(fd);   /* Double closing is safe for non error path */
+}
+
+static int action_restore(void)
+{
+  while(1) {
+    char *p = filename();
+    int fd;
+
+    if (*p == 0)
+      return 0;
+
+    fd = open(p, O_RDONLY, 0600);
+
+    if (fd != -1 && read(fd, &game, sizeof(game)) == sizeof(game) && close(fd) != -1 &&
+        game.magic == GAME_MAGIC)
+      return 1;
+
+    strout_lower("Load failed.\n");
+    close(fd);
+  }
+}
+  
 static void moveitem(uint8_t i, uint8_t l)
 {
-  uint8_t *p = objloc + i;
-  if (*p == location)
+  uint8_t *p = game.objloc + i;
+  if (*p == game.location)
     redraw |= REDRAW_MAYBE;
-  if (l == location)
+  if (l == game.location)
     redraw |= REDRAW;
   *p = l;
 }
@@ -2288,16 +2337,16 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 51: /* nop - check */
         break;
       case 52: /* Get */
-        if (carried >= maxcar)
+        if (game.carried >= maxcar)
           strout_lower(toomuch);
         else
           moveitem(*param++, 255);
         break;
       case 53: /* Drop */
-        moveitem(*param++, location);
+        moveitem(*param++, game.location);
         break;
       case 54: /* Go */
-        location = *param++;
+        game.location = *param++;
         redraw = REDRAW;
         break;
       case 55: /* Destroy */
@@ -2305,17 +2354,17 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 0);
         break;
       case 56: /* Set dark flag */
-        bitflags |= (1 << DARKFLAG);
+        game.bitflags |= (ONEBIT << DARKFLAG);
         break;
       case 57: /* Clear dark flag */
-        bitflags &= ~(1 << DARKFLAG);
+        game.bitflags &= ~(ONEBIT << DARKFLAG);
         break;
       case 58: /* Set bit */
-        bitflags |= (1 << *param++);
+        game.bitflags |= (ONEBIT << *param++);
         break;
       /* 59 see 55 */
       case 60: /* Clear bit */
-        bitflags &= ~(1 << *param++);
+        game.bitflags &= ~(ONEBIT << *param++);
         break;
       case 61: /* Dead */
         action_dead();
@@ -2336,24 +2385,25 @@ static void run_actions(const uint8_t *p, uint8_t n)
       case 66: /* Inventory */
         action_inventory();
       case 67: /* Set bit 0 */
-        bitflags |= (1 << 0);
+        game.bitflags |= (ONEBIT << 0);
         break;
       case 68: /* Clear bit 0 */
-        bitflags &= ~(1 << 0);
+        game.bitflags &= ~(ONEBIT << 0);
         break;
       case 69: /* Refill lamp */
-        lighttime = lightfill;
-        bitflags &= ~(1 << LIGHTOUT);
+        game.lighttime = lightfill;
+        game.bitflags &= ~(ONEBIT << LIGHTOUT);
         moveitem(LIGHT_SOURCE, 255);
         break;
       case 70: /* Wipe lower */
         /* TODO */
         break;
       case 71: /* Save */
-        /* TODO */
+        action_save();
+        break;
       case 72: /* Swap two objects */
-        tmp = objloc[*param];
-        moveitem(*param, objloc[param[1]]);
+        tmp = game.objloc[*param];
+        moveitem(*param, game.objloc[param[1]]);
         moveitem(param[1], tmp);
         param += 2;
         break;
@@ -2364,37 +2414,37 @@ static void run_actions(const uint8_t *p, uint8_t n)
         moveitem(*param++, 255);
         break;
       case 75: /* Put one item by another */
-        moveitem(*param, objloc[param[1]]);
+        moveitem(*param, game.objloc[param[1]]);
         param += 2;
         break;
       case 77: /* Decrement counter */
-        if (counter >= 0)
-          counter--;
+        if (game.counter >= 0)
+          game.counter--;
         break;
       case 78: /* Display counter */
-        decout_lower(counter);
+        decout_lower(game.counter);
         break;
       case 79: /* Set counter */
-        counter = *param++;
+        game.counter = *param++;
         break;
       case 80: /* Swap player and saved room */
-        tmp = savedroom;
-        savedroom = location;
-        location = tmp;
+        tmp = game.savedroom;
+        game.savedroom = game.location;
+        game.location = tmp;
         redraw = REDRAW;
         break;
       case 81: /* Swap counter and counter n */
-        tmp16 = counter;
-        counter = counter_array[*param];
-        counter_array[*param++] = tmp16;
+        tmp16 = game.counter;
+        game.counter = game.counter_array[*param];
+        game.counter_array[*param++] = tmp16;
         break;
       case 82: /* Add to counter */
-        counter += *param++;
+        game.counter += *param++;
         break;
       case 83: /* Subtract from counter */
-        counter -= *param++;
-        if (counter < 0)
-          counter = -1;
+        game.counter -= *param++;
+        if (game.counter < 0)
+          game.counter = -1;
         break;
       case 84: /* Print noun, newline */
         strout_lower((uint8_t *)nounbuf);
@@ -2407,10 +2457,10 @@ static void run_actions(const uint8_t *p, uint8_t n)
         break;
       case 87: /* Swap player and saveroom array entry */
         tmp16 = *param++;
-        tmp = roomsave[tmp16];
-        roomsave[tmp16] = location;
-        if (tmp != location) {
-          location = tmp;
+        tmp = game.roomsave[tmp16];
+        game.roomsave[tmp16] = game.location;
+        if (tmp != game.location) {
+          game.location = tmp;
           redraw = REDRAW;
         }
         break;
@@ -2505,7 +2555,7 @@ uint8_t autonoun(uint8_t loc)
   if (*wordbuf == ' ' || *wordbuf == 0)
     return 255;
   while(*p) {
-    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && objloc[p[WORDSIZE]] == loc)
+    if (strncasecmp((const char *)p, wordbuf, WORDSIZE) == 0 && game.objloc[p[WORDSIZE]] == loc)
       return p[WORDSIZE];
     p += WORDSIZE + 1;
   }
@@ -2521,10 +2571,10 @@ void run_command(void)
   if (verb == VERB_GET) {              /* Get */
     if (noun == 0)
       strout_lower(whatstr);
-    else if (carried >= maxcar)
+    else if (game.carried >= maxcar)
       strout_lower(toomuch);
     else {
-      tmp = autonoun(location);
+      tmp = autonoun(game.location);
       if (tmp == 255)
         strout_lower(beyondpower);
       else
@@ -2541,7 +2591,7 @@ void run_command(void)
       if (tmp == 255)
         strout_lower(beyondpower);
       else
-        moveitem(tmp, location);
+        moveitem(tmp, game.location);
     }
     actmatch = 1;
     return;
@@ -2551,23 +2601,23 @@ void run_command(void)
 void process_light(void)
 {
   uint8_t l;
-  if ((l = objloc[LIGHT_SOURCE]) == 0)
+  if ((l = game.objloc[LIGHT_SOURCE]) == 0)
     return;
-  if (lighttime == 255)
+  if (game.lighttime == 255)
     return;
-  if (!--lighttime) {
-    bitflags &= ~(1 << LIGHTOUT);      /* Check clear ! */
-    if (l == 255 || l == location) {
+  if (!--game.lighttime) {
+    game.bitflags &= ~(ONEBIT << LIGHTOUT);    /* Check clear ! */
+    if (l == 255 || l == game.location) {
       strout_lower(lightout);
       redraw = REDRAW_MAYBE;
       return;
     }
   }
-  if (lighttime > 25)
+  if (game.lighttime > 25)
     return;
   strout_lower(lightoutin);
-  decout_lower(lighttime);
-  strout_lower(lighttime == 1 ? turn : turns);
+  decout_lower(game.lighttime);
+  strout_lower(game.lighttime == 1 ? turn : turns);
 }
 
 void main_loop(void)
@@ -2620,7 +2670,7 @@ void main_loop(void)
 
         if (!light)
           strout_lower(darkdanger);
-        dir = locdata[location].exit[noun - 1];
+        dir = locdata[game.location].exit[noun - 1];
         if (!dir) {
           if (!light) {
             strout_lower(brokeneck);
@@ -2631,7 +2681,7 @@ void main_loop(void)
           strout_lower(cantgo);
           continue;
         }
-        location = dir;
+        game.location = dir;
         redraw = REDRAW;
         continue;
       }
@@ -2651,21 +2701,23 @@ void main_loop(void)
 
 void start_game(void)
 {
-  memcpy(objloc, objinit, sizeof(objloc));
-  bitflags = 0;
-  counter = 0;
-  memset(counter_array, 0, sizeof(counter_array));
-  savedroom = 0;
-  memset(roomsave, 0, sizeof(roomsave));
-  location = startloc;
-  lighttime = startlamp;
-  carried = startcarried;
+  memcpy(game.objloc, objinit, sizeof(game.objloc));
+  game.bitflags = 0;
+  game.counter = 0;
+  memset(game.counter_array, 0, sizeof(game.counter_array));
+  game.savedroom = 0;
+  memset(game.roomsave, 0, sizeof(game.roomsave));
+  game.location = startloc;
+  game.lighttime = startlamp;
+  game.carried = startcarried;
 }
 
 int main(int argc, char *argv[])
 {
   display_init();
   setjmp(restart);
-  start_game();
+  strout_lower("Restore a saved game ? ");
+  if (!yes_or_no() || !action_restore())
+    start_game();
   main_loop();
 }