rnd-20020313-2-src
[rocksndiamonds.git] / src / files.c
index 2c0117ddb77d3a9792fe362c25ba8d62b58a102d..22638f7652e2da0b94f2e67c9100d9c55d4ba038 100644 (file)
@@ -1,14 +1,14 @@
 /***********************************************************
-*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-*  (c) 1995-98 Artsoft Entertainment                       *
-*              Holger Schemel                              *
-*              Oststrasse 11a                              *
-*              33604 Bielefeld                             *
-*              phone: ++49 +521 290471                     *
-*              email: aeglos@valinor.owl.de                *
+* (c) 1995-2001 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
 *----------------------------------------------------------*
-*  files.h                                                 *
+* files.c                                                  *
 ***********************************************************/
 
 #include <ctype.h>
 #include "tape.h"
 #include "joystick.h"
 
-#define MAX_FILENAME_LEN       256     /* maximal filename length */
+#define MAX_FILENAME_LEN       256     /* maximal filename length   */
 #define MAX_LINE_LEN           1000    /* maximal input line length */
 #define CHUNK_ID_LEN           4       /* IFF style chunk id length */
 #define LEVEL_HEADER_SIZE      80      /* size of level file header */
 #define LEVEL_HEADER_UNUSED    15      /* unused level header bytes */
-#define TAPE_HEADER_SIZE       20      /* size of tape file header */
-#define TAPE_HEADER_UNUSED     7       /* unused tape header bytes */
-#define FILE_VERSION_1_0       10      /* 1.0 file version (old) */
+#define TAPE_HEADER_SIZE       20      /* size of tape file header  */
+#define TAPE_HEADER_UNUSED     7       /* unused tape header bytes  */
+
+#if 0
+#define FILE_VERSION_1_0       10      /* 1.0 file version (old)          */
 #define FILE_VERSION_1_2       12      /* 1.2 file version (still in use) */
-#define FILE_VERSION_1_4       14      /* 1.4 file version (new) */
+#define FILE_VERSION_1_4       14      /* 1.4 file version (new)          */
+#endif
 
 /* file identifier strings */
-#define LEVEL_COOKIE           "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
+#define LEVEL_COOKIE           "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_2.0"
 #define SCORE_COOKIE           "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
 #define TAPE_COOKIE            "ROCKSNDIAMONDS_TAPE_FILE_VERSION_1.2"
 #define SETUP_COOKIE           "ROCKSNDIAMONDS_SETUP_FILE_VERSION_1.2"
@@ -43,6 +46,7 @@
 /* old file identifiers for backward compatibility */
 #define LEVEL_COOKIE_10                "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
 #define LEVEL_COOKIE_12                "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
+#define LEVEL_COOKIE_14                "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.4"
 #define TAPE_COOKIE_10         "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
 
 /* file names and filename extensions */
@@ -191,6 +195,53 @@ char *levelclass_desc[NUM_LEVELCLASS_DESC] =
                         IS_LEVELCLASS_USER(n) ?                7 : \
                         9)
 
+static int getFileVersionFromCookieString(const char *cookie)
+{
+  const char *ptr_cookie1, *ptr_cookie2;
+  const char *pattern1 = "_FILE_VERSION_";
+  const char *pattern2 = "?.?";
+  const int len_cookie = strlen(cookie);
+  const int len_pattern1 = strlen(pattern1);
+  const int len_pattern2 = strlen(pattern2);
+  const int len_pattern = len_pattern1 + len_pattern2;
+  int version_major, version_minor;
+
+  if (len_cookie <= len_pattern)
+    return -1;
+
+  ptr_cookie1 = &cookie[len_cookie - len_pattern];
+  ptr_cookie2 = &cookie[len_cookie - len_pattern2];
+
+  if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
+    return -1;
+
+  if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
+      ptr_cookie2[1] != '.' ||
+      ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
+    return -1;
+
+  version_major = ptr_cookie2[0] - '0';
+  version_minor = ptr_cookie2[2] - '0';
+
+  return (version_major * 10 + version_minor);
+}
+
+boolean checkCookieString(const char *cookie, const char *template)
+{
+  const char *pattern = "_FILE_VERSION_?.?";
+  const int len_cookie = strlen(cookie);
+  const int len_template = strlen(template);
+  const int len_pattern = strlen(pattern);
+
+  if (len_cookie != len_template)
+    return FALSE;
+
+  if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
+    return FALSE;
+
+  return TRUE;
+}
+
 char *getLevelClassDescription(struct LevelDirInfo *ldi)
 {
   int position = ldi->sort_priority / 100;
@@ -359,6 +410,11 @@ static void setLevelInfoToDefaults()
 {
   int i, x, y;
 
+  level.file_version = FILE_VERSION_ACTUAL;
+  level.game_version = GAME_VERSION_ACTUAL;
+
+  level.encoding_16bit = FALSE;                /* default: only 8-bit elements */
+
   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
 
@@ -442,21 +498,21 @@ static int checkLevelElement(int element)
   return element;
 }
 
-void LoadLevel(int level_nr)
+void OLD_LoadLevel(int level_nr)
 {
   int i, x, y;
   char *filename = getLevelFilename(level_nr);
   char cookie[MAX_LINE_LEN];
   char chunk[CHUNK_ID_LEN + 1];
   boolean encoding_16bit = FALSE;      /* default: maximal 256 elements */
-  int file_version = FILE_VERSION_1_4; /* last version of level files */
+  int file_version = FILE_VERSION_ACTUAL;
   int chunk_length;
   FILE *file;
 
   /* always start with reliable default values */
   setLevelInfoToDefaults();
 
-  if (!(file = fopen(filename, "r")))
+  if (!(file = fopen(filename, MODE_READ)))
   {
     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
     return;
@@ -467,6 +523,7 @@ void LoadLevel(int level_nr)
   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
     cookie[strlen(cookie) - 1] = '\0';
 
+#if 0
   if (strcmp(cookie, LEVEL_COOKIE_10) == 0)    /* old 1.0 level format */
     file_version = FILE_VERSION_1_0;
   else if (strcmp(cookie, LEVEL_COOKIE_12) == 0)/* 1.2 (8 bit) level format */
@@ -477,6 +534,18 @@ void LoadLevel(int level_nr)
     fclose(file);
     return;
   }
+#else
+  if (!checkCookieString(cookie, LEVEL_COOKIE))        /* unknown file format */
+  {
+    Error(ERR_WARN, "unknown format of level file '%s'", filename);
+    fclose(file);
+    return;
+  }
+
+  file_version = getFileVersionFromCookieString(cookie);
+#endif
+
+  level.file_version = file_version;
 
   /* read chunk "HEAD" */
   if (file_version >= FILE_VERSION_1_2)
@@ -576,7 +645,7 @@ void LoadLevel(int level_nr)
     }
 
     /* next check body chunk identifier and chunk length */
-    if (strcmp(chunk, "BODY") || chunk_length != lev_fieldx * lev_fieldy)
+    if (strcmp(chunk, "BODY") != 0 || chunk_length != lev_fieldx * lev_fieldy)
     {
       Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
       fclose(file);
@@ -599,13 +668,290 @@ void LoadLevel(int level_nr)
 
   fclose(file);
 
-  /* player was faster than monsters in pre-1.0 levels */
-  if (file_version == FILE_VERSION_1_0 &&
-      IS_LEVELCLASS_CONTRIBUTION(leveldir_current))
+  if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
+      IS_LEVELCLASS_USER(leveldir_current))
+  {
+    /* for user contributed and private levels, use the version of
+       the game engine the levels were created for */
+    level.game_version = file_version;
+
+    /* player was faster than monsters in pre-1.0 levels */
+    if (file_version == FILE_VERSION_1_0)
+    {
+      Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
+      Error(ERR_WARN, "using high speed movement for player");
+      level.double_speed = TRUE;
+    }
+  }
+  else
   {
-    Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
-    Error(ERR_WARN, "using high speed movement for player");
-    level.double_speed = TRUE;
+    /* always use the latest version of the game engine for all but
+       user contributed and private levels */
+    level.game_version = GAME_VERSION_ACTUAL;
+  }
+
+  /* determine border element for this level */
+  SetBorderElement();
+}
+
+static void SkipBytesInFile(FILE *file, unsigned long bytes)
+{
+  while (bytes--)
+    fgetc(file);
+}
+
+static void LoadLevel_HEAD(struct LevelInfo *level, FILE *file)
+{
+  int i, x, y;
+
+  lev_fieldx = level->fieldx = fgetc(file);
+  lev_fieldy = level->fieldy = fgetc(file);
+
+  level->time          = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
+  level->gems_needed   = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
+
+  for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+    level->name[i] = fgetc(file);
+  level->name[MAX_LEVEL_NAME_LEN] = 0;
+
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+    level->score[i] = fgetc(file);
+
+  level->num_yam_contents = STD_ELEMENT_CONTENTS;
+  for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
+  {
+    for(y=0; y<3; y++)
+    {
+      for(x=0; x<3; x++)
+      {
+       if (i < STD_ELEMENT_CONTENTS)
+         level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
+       else
+         level->yam_content[i][x][y] = EL_LEERRAUM;
+      }
+    }
+  }
+
+  level->amoeba_speed          = fgetc(file);
+  level->time_magic_wall       = fgetc(file);
+  level->time_wheel            = fgetc(file);
+  level->amoeba_content                = checkLevelElement(fgetc(file));
+  level->double_speed          = (fgetc(file) == 1 ? TRUE : FALSE);
+  level->gravity               = (fgetc(file) == 1 ? TRUE : FALSE);
+
+  level->encoding_16bit                = (fgetc(file) == 1 ? TRUE : FALSE);
+
+  SkipBytesInFile(file, LEVEL_HEADER_UNUSED);  /* skip unused header bytes */
+}
+
+static void LoadLevel_AUTH(struct LevelInfo *level, FILE *file)
+{
+  int i;
+
+  for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+    level->author[i] = fgetc(file);
+  level->author[MAX_LEVEL_NAME_LEN] = 0;
+}
+
+static void LoadLevel_CONT(struct LevelInfo *level, FILE *file)
+{
+  int i, x, y;
+
+  fgetc(file);
+  level->num_yam_contents = fgetc(file);
+  fgetc(file);
+  fgetc(file);
+
+  if (level->num_yam_contents < 1 ||
+      level->num_yam_contents > MAX_ELEMENT_CONTENTS)
+  {
+#if DEBUG
+    printf("WARNING: num_yam_contents == %d (corrected)\n",
+          level->num_yam_contents);
+#endif
+    level->num_yam_contents = STD_ELEMENT_CONTENTS;
+  }
+
+  for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
+    for(y=0; y<3; y++)
+      for(x=0; x<3; x++)
+       level->yam_content[i][x][y] =
+         checkLevelElement(level->encoding_16bit ?
+                           getFile16BitInteger(file,
+                                               BYTE_ORDER_BIG_ENDIAN) :
+                           fgetc(file));
+}
+
+static void LoadLevel_BODY(struct LevelInfo *level, FILE *file)
+{
+  int x, y;
+
+  for(y=0; y<level->fieldy; y++)
+    for(x=0; x<level->fieldx; x++)
+      Feld[x][y] = Ur[x][y] =
+       checkLevelElement(level->encoding_16bit ?
+                         getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
+                         fgetc(file));
+}
+
+void LoadLevel(int level_nr)
+{
+  char *filename = getLevelFilename(level_nr);
+  char cookie[MAX_LINE_LEN];
+  char chunk[CHUNK_ID_LEN + 1];
+  int chunk_length;
+  FILE *file;
+
+  /* always start with reliable default values */
+  setLevelInfoToDefaults();
+
+  if (!(file = fopen(filename, MODE_READ)))
+  {
+    Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
+    return;
+  }
+
+  /* check file identifier */
+  fgets(cookie, MAX_LINE_LEN, file);
+  if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
+    cookie[strlen(cookie) - 1] = '\0';
+
+  if (!checkCookieString(cookie, LEVEL_COOKIE))        /* unknown file format */
+  {
+    Error(ERR_WARN, "unknown format of level file '%s'", filename);
+    fclose(file);
+    return;
+  }
+
+  if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
+  {
+    Error(ERR_WARN, "unsupported version of level file '%s'", filename);
+    fclose(file);
+    return;
+  }
+
+  if (level.file_version < FILE_VERSION_1_2)
+  {
+    /* level files from versions before 1.2.0 without chunk structure */
+    LoadLevel_HEAD(&level, file);
+    LoadLevel_BODY(&level, file);
+  }
+  else
+  {
+    static struct
+    {
+      char *chunk_name;
+      void (*chunk_loader)(struct LevelInfo *, FILE *);
+      int chunk_length;
+    }
+    chunk_info[] =
+    {
+      { "HEAD", LoadLevel_HEAD, LEVEL_HEADER_SIZE },
+      { "AUTH", LoadLevel_AUTH, MAX_LEVEL_AUTHOR_LEN },
+      { "CONT", LoadLevel_CONT, 4 + MAX_ELEMENT_CONTENTS * 3 * 3 },
+      { "BODY", LoadLevel_BODY, 0 },   /* depends on contents of "HEAD" */
+      {  NULL,  NULL,           0 }
+    };
+
+    while (getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN))
+    {
+      int i = 0;
+
+      while (chunk_info[i].chunk_name != NULL &&
+            strcmp(chunk, chunk_info[i].chunk_name) != 0)
+       i++;
+
+      if (chunk_info[i].chunk_name == NULL)
+      {
+       Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
+             chunk, filename);
+       SkipBytesInFile(file, chunk_length);
+      }
+      else if (chunk_length != chunk_info[i].chunk_length)
+      {
+       Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
+             chunk_length, chunk, filename);
+       SkipBytesInFile(file, chunk_length);
+      }
+      else
+      {
+       /* call function to load this level chunk */
+       (chunk_info[i].chunk_loader)(&level, file);
+
+       if (strcmp(chunk, "HEAD") == 0)
+       {
+         /* Note: "chunk_length" for CONT and BODY is wrong when elements are
+            stored with 16-bit encoding (and should be twice as big then). */
+
+         chunk_info[3].chunk_length = level.fieldx * level.fieldy;
+       }
+      }
+    }
+
+#if 0
+    getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
+    if (strcmp(chunk, "HEAD") || chunk_length != LEVEL_HEADER_SIZE)
+    {
+      Error(ERR_WARN, "wrong 'HEAD' chunk of level file '%s'", filename);
+      fclose(file);
+      return;
+    }
+
+    LoadLevel_HEAD(&level, file);
+
+    getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
+
+    /* look for optional author chunk */
+    if (strcmp(chunk, "AUTH") == 0 && chunk_length == MAX_LEVEL_AUTHOR_LEN)
+    {
+      LoadLevel_AUTH(&level, file);
+
+      getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
+    }
+
+    /* look for optional content chunk */
+    if (strcmp(chunk, "CONT") == 0 &&
+       chunk_length == 4 + MAX_ELEMENT_CONTENTS * 3 * 3)
+    {
+      LoadLevel_CONT(&level, file);
+
+      getFileChunk(file, chunk, &chunk_length, BYTE_ORDER_BIG_ENDIAN);
+    }
+
+    /* next check body chunk identifier and chunk length */
+    if (strcmp(chunk, "BODY") != 0 || chunk_length != lev_fieldx * lev_fieldy)
+    {
+      Error(ERR_WARN, "wrong 'BODY' chunk of level file '%s'", filename);
+      fclose(file);
+      return;
+    }
+
+    LoadLevel_BODY(&level, file);
+#endif
+  }
+
+  fclose(file);
+
+  if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
+      IS_LEVELCLASS_USER(leveldir_current))
+  {
+    /* for user contributed and private levels, use the version of
+       the game engine the levels were created for */
+    level.game_version = level.file_version;
+
+    /* player was faster than monsters in pre-1.0 levels */
+    if (level.file_version == FILE_VERSION_1_0)
+    {
+      Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
+      Error(ERR_WARN, "using high speed movement for player");
+      level.double_speed = TRUE;
+    }
+  }
+  else
+  {
+    /* always use the latest version of the game engine for all but
+       user contributed and private levels */
+    level.game_version = GAME_VERSION_ACTUAL;
   }
 
   /* determine border element for this level */
@@ -616,11 +962,15 @@ void SaveLevel(int level_nr)
 {
   int i, x, y;
   char *filename = getLevelFilename(level_nr);
-  boolean encoding_16bit = FALSE;      /* default: maximal 256 elements */
+#if 0
+  boolean encoding_16bit_amoeba = FALSE;
+  boolean encoding_16bit_yamyam = FALSE;
+#endif
+  boolean encoding_16bit = FALSE;      /* default: only 8-bit elements */
   char *oldest_possible_cookie;
   FILE *file;
 
-  if (!(file = fopen(filename, "w")))
+  if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot save level file '%s'", filename);
     return;
@@ -716,10 +1066,12 @@ void LoadTape(int level_nr)
   char chunk[CHUNK_ID_LEN + 1];
   FILE *file;
   int num_participating_players;
-  int file_version = FILE_VERSION_1_2; /* last version of tape files */
+  int file_version = FILE_VERSION_ACTUAL; /* last version of tape files */
   int chunk_length;
 
   /* always start with reliable default values (empty tape) */
+  tape.file_version = FILE_VERSION_ACTUAL;
+  tape.game_version = GAME_VERSION_ACTUAL;
   TapeErase();
 
   /* default values (also for pre-1.2 tapes) with only the first player */
@@ -730,7 +1082,7 @@ void LoadTape(int level_nr)
   /* at least one (default: the first) player participates in every tape */
   num_participating_players = 1;
 
-  if (!(file = fopen(filename, "r")))
+  if (!(file = fopen(filename, MODE_READ)))
     return;
 
   /* check file identifier */
@@ -738,6 +1090,7 @@ void LoadTape(int level_nr)
   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
     cookie[strlen(cookie) - 1] = '\0';
 
+#if 0
   if (strcmp(cookie, TAPE_COOKIE_10) == 0)     /* old 1.0 tape format */
     file_version = FILE_VERSION_1_0;
   else if (strcmp(cookie, TAPE_COOKIE) != 0)   /* unknown tape format */
@@ -746,6 +1099,19 @@ void LoadTape(int level_nr)
     fclose(file);
     return;
   }
+#else
+  if (!checkCookieString(cookie, TAPE_COOKIE)) /* unknown file format */
+  {
+    Error(ERR_WARN, "unknown format of tape file '%s'", filename);
+    fclose(file);
+    return;
+  }
+
+  file_version = getFileVersionFromCookieString(cookie);
+#endif
+
+  tape.file_version = file_version;
+  tape.game_version = file_version;
 
   /* read chunk "HEAD" */
   if (file_version >= FILE_VERSION_1_2)
@@ -892,7 +1258,7 @@ void SaveTape(int level_nr)
     }
   }
 
-  if (!(file = fopen(filename, "w")))
+  if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
     return;
@@ -952,7 +1318,7 @@ void LoadScore(int level_nr)
     highscore[i].Score = 0;
   }
 
-  if (!(file = fopen(filename, "r")))
+  if (!(file = fopen(filename, MODE_READ)))
     return;
 
   /* check file identifier */
@@ -960,12 +1326,21 @@ void LoadScore(int level_nr)
   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
     cookie[strlen(cookie) - 1] = '\0';
 
+#if 0
   if (strcmp(cookie, SCORE_COOKIE) != 0)
   {
     Error(ERR_WARN, "wrong file identifier of score file '%s'", filename);
     fclose(file);
     return;
   }
+#else
+  if (!checkCookieString(cookie, SCORE_COOKIE))        /* unknown file format */
+  {
+    Error(ERR_WARN, "unknown format of score file '%s'", filename);
+    fclose(file);
+    return;
+  }
+#endif
 
   for(i=0; i<MAX_SCORE_ENTRIES; i++)
   {
@@ -997,7 +1372,7 @@ void SaveScore(int level_nr)
 
   InitScoreDirectory(leveldir_current->filename);
 
-  if (!(file = fopen(filename, "w")))
+  if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot save score for level %d", level_nr);
     return;
@@ -1304,7 +1679,7 @@ static struct SetupFileList *loadSetupFileList(char *filename)
 
   FILE *file;
 
-  if (!(file = fopen(filename, "r")))
+  if (!(file = fopen(filename, MODE_READ)))
   {
     Error(ERR_WARN, "cannot open configuration file '%s'", filename);
     return NULL;
@@ -1782,7 +2157,7 @@ static void SaveUserLevelInfo()
 
   filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
 
-  if (!(file = fopen(filename, "w")))
+  if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot write level info file '%s'", filename);
     free(filename);
@@ -1926,7 +2301,7 @@ void SaveSetup()
 
   filename = getPath2(getSetupDir(), SETUP_FILENAME);
 
-  if (!(file = fopen(filename, "w")))
+  if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot write setup file '%s'", filename);
     free(filename);
@@ -1973,7 +2348,7 @@ void LoadLevelSetup_LastSeries()
   struct SetupFileList *level_setup_list = NULL;
 
   /* always start with reliable default values */
-  leveldir_current = leveldir_first;
+  leveldir_current = getFirstValidLevelSeries(leveldir_first);
 
   /* ----------------------------------------------------------------------- */
   /* ~/.rocksndiamonds/levelsetup.conf                                       */
@@ -2014,7 +2389,7 @@ void SaveLevelSetup_LastSeries()
 
   filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
 
-  if (!(file = fopen(filename, "w")))
+  if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot write setup file '%s'", filename);
     free(filename);
@@ -2159,7 +2534,7 @@ void SaveLevelSetup_SeriesInfo()
 
   filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
 
-  if (!(file = fopen(filename, "w")))
+  if (!(file = fopen(filename, MODE_WRITE)))
   {
     Error(ERR_WARN, "cannot write setup file '%s'", filename);
     free(filename);
@@ -2178,3 +2553,5 @@ void SaveLevelSetup_SeriesInfo()
 
   chmod(filename, SETUP_PERMS);
 }
+/*  LocalWords:  Rocks'n
+ */