From b9082c113c43afe5c6a11c2b72f845ee2f8c6aea Mon Sep 17 00:00:00 2001
From: Christian Heller <c.heller@plomlompom.de>
Date: Wed, 14 Aug 2013 12:58:48 +0200
Subject: [PATCH] Built error checking into file reading/writing routines and
 calls of them.

---
 src/main.c        | 48 ++++++++++++-----------
 src/map_objects.c | 48 +++++++++++++----------
 src/map_objects.h |  5 ++-
 src/misc.c        | 23 +++++------
 src/readwrite.c   | 98 +++++++++++++++++++++++++++++++++++++----------
 src/readwrite.h   | 16 +++++---
 6 files changed, 158 insertions(+), 80 deletions(-)

diff --git a/src/main.c b/src/main.c
index 7096f6c..d2fb9bf 100644
--- a/src/main.c
+++ b/src/main.c
@@ -13,9 +13,7 @@
                         * draw_log_win()
                         */
 #include "keybindings.h" /* for initkeybindings(), get_action_key() */
-#include "readwrite.h" /* for read_uint16_bigendian, read_uint32_bigendian,
-                        * write_uint32_bigendian
-                        */
+#include "readwrite.h" /* for [read/write]_uint[8/16/32][_bigendian]() */
 #include "map_objects.h" /* for structs Monster, Item, Player,
                           * init_map_object_defs(), read_map_objects(),
                           * build_map_objects()
@@ -62,19 +60,22 @@ int main(int argc, char *argv[])
     world.monster = 0;
     world.item = 0;
     init_map_object_defs(&world, "defs");
+    uint8_t fail = 0;
 
     /* For interactive mode, try to load world state from savefile. */
     FILE * file;
     if (1 == world.interactive && 0 == access("savefile", F_OK))
     {
         file = fopen("savefile", "r");
-        world.seed = read_uint32_bigendian(file);
-        world.turn = read_uint32_bigendian(file);
-        player.pos.y = read_uint16_bigendian(file) - 1;
-        player.pos.x = read_uint16_bigendian(file) - 1;
-        player.hitpoints = fgetc(file);
-        read_map_objects(&world, &world.monster, file);
-        read_map_objects(&world, &world.item,    file);
+        fail = fail | read_uint32_bigendian(file, &world.seed);
+        fail = fail | read_uint32_bigendian(file, &world.turn);
+        fail = fail | read_uint16_bigendian(file, &player.pos.y);
+        fail = fail | read_uint16_bigendian(file, &player.pos.x);
+        player.pos.y--;
+        player.pos.x--;
+        fail = fail | read_uint8(file, &player.hitpoints);
+        fail = fail | read_map_objects(&world, &world.monster, file);
+        fail = fail | read_map_objects(&world, &world.item,    file);
         fclose(file);
     }
 
@@ -85,20 +86,23 @@ int main(int argc, char *argv[])
         if (0 == world.interactive)
         {
             file = fopen("record", "r");
-            world.seed = read_uint32_bigendian(file);
+            fail = fail | read_uint32_bigendian(file, &world.seed);
+        }
+
+        /* For interactive-mode in newly started world, generate a start seed
+         * from the current time.
+         */
+        else
+        {
+            file = fopen("record", "w");
+            world.seed = time(NULL);
+            fail = fail | write_uint32_bigendian(world.seed, file);
+            fclose(file);
         }
+    }
+
+    exit_err(fail, &world, "Failure initializing game.");
 
-      /* For interactive-mode in newly started world, generate a start seed
-       * from the current time.
-       */
-      else
-      {
-          file = fopen("record", "w");
-          world.seed = time(NULL);
-          write_uint32_bigendian(world.seed, file);
-          fclose(file);
-      }
-  }
 
     /* Generate map from seed and, if newly generated world, start positions of
      * actors.
diff --git a/src/map_objects.c b/src/map_objects.c
index 4340694..c4b9048 100644
--- a/src/map_objects.c
+++ b/src/map_objects.c
@@ -4,7 +4,7 @@
 #include <stdlib.h> /* for malloc(), calloc(), free(), atoi() */
 #include <stdio.h> /* for FILE typedef */
 #include <string.h> /* for strchr(), strlen(), memcpy()  */
-#include "readwrite.h" /* for all the map object (def) loading/saving */
+#include "readwrite.h" /* for [read/write]_uint[8/16/23][_bigendian]() */
 #include "misc.h" /* for textfile_sizes(), find_passable_pos() */
 #include "main.h" /* for World struct */
 
@@ -23,8 +23,8 @@ static void build_map_objects_itemdata(struct MapObjDef * map_obj_def,
                                        void * start);
 static void build_map_objects_monsterdata(struct MapObjDef * map_obj_def,
                                           void * start);
-static void write_map_objects_monsterdata(void * start, FILE * file);
-static void read_map_objects_monsterdata( void * start, FILE * file);
+static uint8_t write_map_objects_monsterdata(void * start, FILE * file);
+static uint8_t read_map_objects_monsterdata( void * start, FILE * file);
 
 
 
@@ -68,18 +68,18 @@ static void build_map_objects_monsterdata(struct MapObjDef * map_obj_def,
 
 
 
-static void write_map_objects_monsterdata(void * start, FILE * file)
+static uint8_t write_map_objects_monsterdata(void * start, FILE * file)
 {
     struct Monster * m = (struct Monster *) start;
-    fputc(m->hitpoints, file);
+    return write_uint8(m->hitpoints, file);
 }
 
 
 
-static void read_map_objects_monsterdata (void * start, FILE * file)
+static uint8_t read_map_objects_monsterdata (void * start, FILE * file)
 {
     struct Monster * m = (struct Monster *) start;
-    m->hitpoints = fgetc(file);
+    return read_uint8(file, &m->hitpoints);
 }
 
 
@@ -137,43 +137,48 @@ extern void init_map_object_defs(struct World * world, char * filename)
 
 
 
-extern void write_map_objects(struct World * world, void * start, FILE * file)
+extern uint8_t write_map_objects(struct World * world, void * start,
+                                 FILE * file)
 {
     struct MapObj * map_obj;
     struct MapObjDef * mod;
+    uint8_t fail = 0;
     for (map_obj = start; map_obj != 0; map_obj = map_obj->next)
     {
-        fputc(map_obj->type, file);
-        write_uint16_bigendian(map_obj->pos.y + 1, file);
-        write_uint16_bigendian(map_obj->pos.x + 1, file);
+        fail = fail | write_uint8(map_obj->type, file);
+        fail = fail | write_uint16_bigendian(map_obj->pos.y + 1, file);
+        fail = fail | write_uint16_bigendian(map_obj->pos.x + 1, file);
         mod = get_map_obj_def(world, map_obj->type);
         if ('m' == mod->m_or_i)
         {
-            write_map_objects_monsterdata(map_obj, file);
+            fail = fail | write_map_objects_monsterdata(map_obj, file);
         }
     }
-    write_uint16_bigendian(0, file);
+    return (fail | write_uint16_bigendian(0, file));
 }
 
 
 
-extern void read_map_objects(struct World * world, void * start, FILE * file)
+extern uint8_t read_map_objects(struct World * world, void * start, FILE * file)
 {
     struct MapObj * map_obj;
     struct MapObjDef * mod;
     size_t size;
-    char type;
+    uint8_t type;
     char first = 1;
     long pos;
+    uint16_t read_uint16 = 0;
+    uint8_t fail = 0;
     while (1)
     {
         pos = ftell(file);
-        if (0 == read_uint16_bigendian(file))
+        fail = fail | read_uint16_bigendian(file, &read_uint16);
+        if (0 == read_uint16)
         {
             break;
         }
         fseek(file, pos, SEEK_SET);
-        type = fgetc(file);
+        fail = fail | read_uint8(file, &type);
         mod = get_map_obj_def(world, type);
         if ('m' == mod->m_or_i)
         {
@@ -185,17 +190,20 @@ extern void read_map_objects(struct World * world, void * start, FILE * file)
         }
         map_obj = get_next_map_obj(start, &first, size, map_obj);
         map_obj->type = type;
-        map_obj->pos.y = read_uint16_bigendian(file) - 1;
-        map_obj->pos.x = read_uint16_bigendian(file) - 1;
+        fail = fail | read_uint16_bigendian(file, &map_obj->pos.y);
+        fail = fail | read_uint16_bigendian(file, &map_obj->pos.x);
+        map_obj->pos.y--;
+        map_obj->pos.x--;
         if ('m' == mod->m_or_i)
         {
-            read_map_objects_monsterdata(map_obj, file);
+            fail = fail | read_map_objects_monsterdata(map_obj, file);
         }
     }
     if (!first)
     {
         map_obj->next = 0;
     }
+    return fail;
 }
 
 
diff --git a/src/map_objects.h b/src/map_objects.h
index a5156f4..7fcf1c7 100644
--- a/src/map_objects.h
+++ b/src/map_objects.h
@@ -88,8 +88,9 @@ extern void * build_map_objects(struct World * world, void * start, char def_id,
 /* Write to/read from file chain of map objects starting/to start in memory at
  * "start".
  */
-extern void write_map_objects(struct World * world, void * start, FILE * file);
-extern void read_map_objects(struct World * world, void * start, FILE * file);
+extern uint8_t write_map_objects(struct World * world, void * start,
+                                 FILE * file);
+extern uint8_t read_map_objects(struct World * world, void * start, FILE * file);
 
 
 
diff --git a/src/misc.c b/src/misc.c
index 0bb50a7..ec05acc 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -10,15 +10,14 @@
 #include "keybindings.h" /* for get_action_key(), save_keybindings(),
                           * keyswin_move_selection(), keyswin_mod_key()
                           */
-#include "readwrite.h" /* for write_uint16_bigendian(), write_uint32_bigendian()
-                        */
+#include "readwrite.h" /* for [read/write]_uint[8/16/32][_bigendian]() */
 #include "map_objects.h" /* for struct Monster, write_map_objects(), */
 #include "map_object_actions.h" /* for is_passable(), move_monster() */
 #include "map.h" /* for map_scroll(),map_center_player(), Map struct,dir enum */
 #include "main.h" /* for World struct */
 #include "yx_uint16.h" /* for yx_uint16 */
 #include "rrand.h" /* for rrand(), rrand_seed() */
-
+#include "rexit.h" /* for exit_err() */
 
 
 extern void textfile_sizes(FILE * file, uint16_t * linemax_p,
@@ -115,7 +114,7 @@ extern void turn_over(struct World * world, char action)
     if (1 == world->interactive)
     {
         FILE * file = fopen("record", "a");
-        fputc(action, file);
+        exit_err(write_uint8(action, file), world, "Record writing failure.");
         fclose(file);
     }
     world->turn++;
@@ -133,14 +132,16 @@ extern void turn_over(struct World * world, char action)
 
 extern void save_game(struct World * world)
 {
+    uint8_t fail;
     FILE * file = fopen("savefile", "w");
-    write_uint32_bigendian(world->seed, file);
-    write_uint32_bigendian(world->turn, file);
-    write_uint16_bigendian(world->player->pos.y + 1, file);
-    write_uint16_bigendian(world->player->pos.x + 1, file);
-    fputc(world->player->hitpoints, file);
-    write_map_objects(world, world->monster, file);
-    write_map_objects(world, world->item, file);
+    fail = write_uint32_bigendian(world->seed, file);
+    fail = fail | write_uint32_bigendian(world->turn, file);
+    fail = fail | write_uint16_bigendian(world->player->pos.y + 1, file);
+    fail = fail | write_uint16_bigendian(world->player->pos.x + 1, file);
+    fail = fail | write_uint8(world->player->hitpoints, file);
+    fail = fail | write_map_objects(world, world->monster, file);
+    fail = fail | write_map_objects(world, world->item, file);
+    exit_err(fail, world, "Error saving game.");
     fclose(file);
 }
 
diff --git a/src/readwrite.c b/src/readwrite.c
index 40fc63f..6d24992 100644
--- a/src/readwrite.c
+++ b/src/readwrite.c
@@ -6,40 +6,98 @@
 
 
 
-extern uint16_t read_uint16_bigendian(FILE * file)
+/* Read/write "x" from/to "file" as bigendian representation of "size" bits. */
+static uint8_t read_uintX_bigendian(FILE * file, uint32_t * x, uint8_t size);
+static uint8_t write_uintX_bigendian(FILE * file, uint32_t x, uint8_t size);
+
+
+
+static uint8_t read_uintX_bigendian(FILE * file, uint32_t * x, uint8_t size)
+{
+    if (0 != size % 8)
+    {
+        return 1;
+    }
+    int16_t bitshift = size - 8;
+
+    * x = 0;
+    int test;
+    for (; bitshift >= 0; bitshift = bitshift - 8)
+    {
+        test = fgetc(file);
+        if (EOF == test)
+        {
+            return 1;
+        }
+        * x = * x + ((uint32_t) test << bitshift);
+    }
+    return 0;
+}
+
+
+
+static uint8_t write_uintX_bigendian(FILE * file, uint32_t x, uint8_t size)
+{
+    if (0 != size % 8)
+    {
+        return 1;
+    }
+    int16_t bitshift = size - 8;
+
+    for (; bitshift >= 0; bitshift = bitshift - 8)
+    {
+        if (EOF == fputc((x >> bitshift) & 0xFF, file))
+        {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+
+extern uint8_t read_uint8(FILE * file, uint8_t * x)
+{
+    uint32_t y = * x;
+    uint8_t fail = read_uintX_bigendian(file, &y, 8);
+    * x = (uint8_t) y;
+    return fail;
+}
+
+
+
+extern uint8_t read_uint16_bigendian(FILE * file, uint16_t * x)
+{
+    uint32_t y = * x;
+    uint8_t fail = read_uintX_bigendian(file, &y, 16);
+    * x = (uint16_t) y;
+    return fail;
+}
+
+
+
+extern uint8_t read_uint32_bigendian(FILE * file, uint32_t * x)
 {
-    uint16_t x;
-    x =     (uint16_t) fgetc(file) << 8;
-    x = x + (uint16_t) fgetc(file);
-    return x;
+    return read_uintX_bigendian(file, x, 32);
 }
 
 
 
-extern uint32_t read_uint32_bigendian(FILE * file)
+extern uint8_t write_uint8(uint8_t x, FILE * file)
 {
-    uint32_t x;
-    x =       (uint32_t) fgetc(file) << 24;
-    x = x + ( (uint32_t) fgetc(file) << 16 );
-    x = x + ( (uint32_t) fgetc(file) <<  8 );
-    x = x +   (uint32_t) fgetc(file);
-    return x;
+    return write_uintX_bigendian(file, x, 8);
 }
 
 
 
-extern void write_uint16_bigendian(uint16_t x, FILE * file)
+extern uint8_t write_uint16_bigendian(uint16_t x, FILE * file)
 {
-    fputc( x >> 8,   file );
-    fputc( x & 0xFF, file );
+    return write_uintX_bigendian(file, x, 16);
 }
 
 
 
-extern void write_uint32_bigendian(uint32_t x, FILE * file)
+extern uint8_t write_uint32_bigendian(uint32_t x, FILE * file)
 {
-    fputc(   x >> 24,          file);
-    fputc( ( x >> 16 ) & 0xFF, file);
-    fputc( ( x >>  8 ) & 0xFF, file);
-    fputc(   x         & 0xFF, file);
+    return write_uintX_bigendian(file, x, 32);
 }
diff --git a/src/readwrite.h b/src/readwrite.h
index a0c8dba..42a7c3a 100644
--- a/src/readwrite.h
+++ b/src/readwrite.h
@@ -1,6 +1,6 @@
 /* readwrite.h:
  *
- * Routines for reading/writing multibyte data from/to files. They ensure a
+ * Routines for reading/writing (multi-)byte data from/to files. They ensure a
  * defined endianness.
  */
 
@@ -14,9 +14,15 @@
 
 
 
-extern uint16_t read_uint16_bigendian(FILE * file);
-extern uint32_t read_uint32_bigendian(FILE * file);
-extern void write_uint16_bigendian(uint16_t x, FILE * file);
-extern void write_uint32_bigendian(uint32_t x, FILE * file);
+/* Each function returns 0 on success and 1 on failure. "x" is the value to be
+ * written to "file" for write_* functions and for read_* functions the pointer
+ * to where the value read from "file" is to be written.
+ */
+extern uint8_t read_uint8(FILE * file, uint8_t * x);
+extern uint8_t read_uint16_bigendian(FILE * file, uint16_t * x);
+extern uint8_t read_uint32_bigendian(FILE * file, uint32_t * x);
+extern uint8_t write_uint8(uint8_t x, FILE * file);
+extern uint8_t write_uint16_bigendian(uint16_t x, FILE * file);
+extern uint8_t write_uint32_bigendian(uint32_t x, FILE * file);
 
 #endif
-- 
2.30.2