rnd-19981123-5
[rocksndiamonds.git] / src / files.c
index cb1199d340bb58212659431caadeb900800719ca..d28c1f48279502d5dcfa181d4f271e802c212ef0 100644 (file)
 #include "tape.h"
 #include "joystick.h"
 
-#define MAX_LINE_LEN                   1000    /* file 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            18      /* 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      /* old 1.0 file version */
-#define FILE_VERSION_1_2               12      /* actual file version */
+#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    18      /* 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      /* old 1.0 file version */
+#define FILE_VERSION_1_2       12      /* actual file version */
+
+/* file identifier strings */
+#define LEVEL_COOKIE           "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.2"
+#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"
+#define LEVELSETUP_COOKIE      "ROCKSNDIAMONDS_LEVELSETUP_FILE_VERSION_1.2"
+#define LEVELINFO_COOKIE       "ROCKSNDIAMONDS_LEVELINFO_FILE_VERSION_1.2"
+/* old file identifiers for backward compatibility */
+#define LEVEL_COOKIE_10                "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_1.0"
+#define TAPE_COOKIE_10         "ROCKSNDIAMONDS_LEVELREC_FILE_VERSION_1.0"
+
+/* file names and filename extensions */
+#ifndef MSDOS
+#define USERDATA_DIRECTORY     ".rocksndiamonds"
+#define SETUP_FILENAME         "setup.conf"
+#define LEVELSETUP_FILENAME    "levelsetup.conf"
+#define LEVELINFO_FILENAME     "levelinfo.conf"
+#define LEVELFILE_EXTENSION    "level"
+#define TAPEFILE_EXTENSION     "tape"
+#define SCOREFILE_EXTENSION    "score"
+#else
+#define USERDATA_DIRECTORY     "userdata"
+#define SETUP_FILENAME         "setup.cnf"
+#define LEVELSETUP_FILENAME    "lvlsetup.cnf"
+#define LEVELINFO_FILENAME     "lvlinfo.cnf"
+#define LEVELFILE_EXTENSION    "lvl"
+#define TAPEFILE_EXTENSION     "rec"
+#define SCOREFILE_EXTENSION    "sco"
+#endif
+
+/* file permissions for newly written files */
+#define MODE_R_ALL             (S_IRUSR | S_IRGRP | S_IROTH)
+#define MODE_W_ALL             (S_IWUSR | S_IWGRP | S_IWOTH)
+#define MODE_X_ALL             (S_IXUSR | S_IXGRP | S_IXOTH)
+#define USERDATA_DIR_MODE      (MODE_R_ALL | MODE_X_ALL | S_IWUSR)
+#define LEVEL_PERMS            (MODE_R_ALL | MODE_W_ALL)
+#define SCORE_PERMS            LEVEL_PERMS
+#define TAPE_PERMS             LEVEL_PERMS
+#define SETUP_PERMS            LEVEL_PERMS
 
 static void SaveUserLevelInfo();               /* for 'InitUserLevelDir()' */
 static char *getSetupLine(char *, int);                /* for 'SaveUserLevelInfo()' */
@@ -111,6 +152,52 @@ static char *getScoreDir(char *level_subdir)
   return score_dir;
 }
 
+static char *getLevelFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[20 + strlen(LEVELFILE_EXTENSION)];
+
+  if (filename != NULL)
+    free(filename);
+
+  sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
+  filename = getPath3((leveldir[leveldir_nr].user_defined ?
+                      getUserLevelDir("") :
+                      options.level_directory),
+                     leveldir[leveldir_nr].filename,
+                     basename);
+
+  return filename;
+}
+
+static char *getTapeFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[20 + strlen(LEVELFILE_EXTENSION)];
+
+  if (filename != NULL)
+    free(filename);
+
+  sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
+  filename = getPath2(getTapeDir(leveldir[leveldir_nr].filename), basename);
+
+  return filename;
+}
+
+static char *getScoreFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[20 + strlen(LEVELFILE_EXTENSION)];
+
+  if (filename != NULL)
+    free(filename);
+
+  sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
+  filename = getPath2(getScoreDir(leveldir[leveldir_nr].filename), basename);
+
+  return filename;
+}
+
 static void createDirectory(char *dir, char *text)
 {
   if (access(dir, F_OK) != 0)
@@ -125,6 +212,7 @@ static void InitUserDataDirectory()
 
 static void InitTapeDirectory(char *level_subdir)
 {
+  createDirectory(getUserDataDir(), "user data");
   createDirectory(getTapeDir(""), "main tape");
   createDirectory(getTapeDir(level_subdir), "level tape");
 }
@@ -139,6 +227,7 @@ static void InitUserLevelDirectory(char *level_subdir)
 {
   if (access(getUserLevelDir(level_subdir), F_OK) != 0)
   {
+    createDirectory(getUserDataDir(), "user data");
     createDirectory(getUserLevelDir(""), "main user level");
     createDirectory(getUserLevelDir(level_subdir), "user level");
 
@@ -166,7 +255,7 @@ static void setLevelInfoToDefaults()
 
   strcpy(level.name, "Nameless Level");
 
-  for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     level.score[i] = 10;
 
   for(i=0; i<4; i++)
@@ -182,7 +271,7 @@ static void setLevelInfoToDefaults()
 void LoadLevel(int level_nr)
 {
   int i, x, y;
-  char filename[MAX_FILENAME_LEN];
+  char *filename = getLevelFilename(level_nr);
   char cookie[MAX_LINE_LEN];
   char chunk[CHUNK_ID_LEN + 1];
   int file_version = FILE_VERSION_1_2; /* last version of level files */
@@ -192,13 +281,6 @@ void LoadLevel(int level_nr)
   /* always start with reliable default values */
   setLevelInfoToDefaults();
 
-  if (leveldir[leveldir_nr].user_defined)
-    sprintf(filename, "%s/%s/%d",
-           getUserLevelDir(""), leveldir[leveldir_nr].filename, level_nr);
-  else
-    sprintf(filename, "%s/%s/%d",
-           options.level_directory, leveldir[leveldir_nr].filename, level_nr);
-
   if (!(file = fopen(filename, "r")))
   {
     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
@@ -244,7 +326,7 @@ void LoadLevel(int level_nr)
     level.name[i]      = fgetc(file);
   level.name[MAX_LEVNAMLEN - 1] = 0;
 
-  for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     level.score[i]     = fgetc(file);
 
   for(i=0; i<4; i++)
@@ -288,17 +370,10 @@ void LoadLevel(int level_nr)
 void SaveLevel(int level_nr)
 {
   int i, x, y;
-  char filename[MAX_FILENAME_LEN];
+  char *filename = getLevelFilename(level_nr);
   FILE *file;
   int chunk_length;
 
-  if (leveldir[leveldir_nr].user_defined)
-    sprintf(filename, "%s/%s/%d",
-           getUserLevelDir(""), leveldir[leveldir_nr].filename, level_nr);
-  else
-    sprintf(filename, "%s/%s/%d",
-           options.level_directory, leveldir[leveldir_nr].filename, level_nr);
-
   if (!(file = fopen(filename, "w")))
   {
     Error(ERR_WARN, "cannot save level file '%s'", filename);
@@ -326,7 +401,7 @@ void SaveLevel(int level_nr)
 
   for(i=0; i<MAX_LEVNAMLEN; i++)
     fputc(level.name[i], file);
-  for(i=0; i<MAX_LEVSCORE_ENTRIES; i++)
+  for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
     fputc(level.score[i], file);
   for(i=0; i<4; i++)
     for(y=0; y<3; y++)
@@ -360,19 +435,14 @@ void SaveLevel(int level_nr)
 void LoadTape(int level_nr)
 {
   int i, j;
-  char filename[MAX_FILENAME_LEN];
+  char *filename = getTapeFilename(level_nr);
   char cookie[MAX_LINE_LEN];
   char chunk[CHUNK_ID_LEN + 1];
   FILE *file;
-  boolean player_participates[MAX_PLAYERS];
   int num_participating_players;
   int file_version = FILE_VERSION_1_2; /* last version of tape files */
   int chunk_length;
 
-  sprintf(filename, "%s/%d.%s",
-         getTapeDir(leveldir[leveldir_nr].filename),
-         level_nr, TAPEFILE_EXTENSION);
-
   if (!(file = fopen(filename, "r")))
     return;
 
@@ -425,11 +495,11 @@ void LoadTape(int level_nr)
     num_participating_players = 0;
     for(i=0; i<MAX_PLAYERS; i++)
     {
-      player_participates[i] = FALSE;
+      tape.player_participates[i] = FALSE;
 
       if (store_participating_players & (1 << i))
       {
-       player_participates[i] = TRUE;
+       tape.player_participates[i] = TRUE;
        num_participating_players++;
       }
     }
@@ -472,7 +542,7 @@ void LoadTape(int level_nr)
       if (file_version == FILE_VERSION_1_0 && j > 0)
        continue;
 
-      if (player_participates[j])
+      if (tape.player_participates[j])
        tape.pos[i].action[j] = fgetc(file);
     }
 
@@ -520,46 +590,30 @@ void LoadTape(int level_nr)
 
 void SaveTape(int level_nr)
 {
-  int i, j;
-  char filename[MAX_FILENAME_LEN];
+  int i;
+  char *filename = getTapeFilename(level_nr);
   FILE *file;
   boolean new_tape = TRUE;
-  boolean player_participates[MAX_PLAYERS];
   byte store_participating_players;
   int num_participating_players;
   int chunk_length;
 
   InitTapeDirectory(leveldir[leveldir_nr].filename);
 
-  sprintf(filename, "%s/%d.%s",
-         getTapeDir(leveldir[leveldir_nr].filename),
-         level_nr, TAPEFILE_EXTENSION);
-
   /* if a tape still exists, ask to overwrite it */
-  if ((file = fopen(filename, "r")))
+  if (access(filename, F_OK) == 0)
   {
     new_tape = FALSE;
-    fclose(file);
-
     if (!Request("Replace old tape ?", REQ_ASK))
       return;
   }
 
-  for(i=0; i<MAX_PLAYERS; i++)
-    player_participates[i] = FALSE;
-
-  /* check which players participate in this tape recording */
-  for(i=0; i<tape.length; i++)
-    for(j=0; j<MAX_PLAYERS; j++)
-      if (tape.pos[i].action[j] != 0)
-       player_participates[j] = TRUE;
-
   /* count number of players and set corresponding bits for compact storage */
   store_participating_players = 0;
   num_participating_players = 0;
   for(i=0; i<MAX_PLAYERS; i++)
   {
-    if (player_participates[i])
+    if (tape.player_participates[i])
     {
       num_participating_players++;
       store_participating_players |= (1 << i);
@@ -617,7 +671,7 @@ void SaveTape(int level_nr)
     int j;
 
     for(j=0; j<MAX_PLAYERS; j++)
-      if (player_participates[j])
+      if (tape.player_participates[j])
        fputc(tape.pos[i].action[j], file);
 
     fputc(tape.pos[i].delay, file);
@@ -625,7 +679,7 @@ void SaveTape(int level_nr)
 
   fclose(file);
 
-  chmod(filename, LEVREC_PERMS);
+  chmod(filename, TAPE_PERMS);
 
   tape.changed = FALSE;
 
@@ -636,28 +690,26 @@ void SaveTape(int level_nr)
 void LoadScore(int level_nr)
 {
   int i;
-  char filename[MAX_FILENAME_LEN];
+  char *filename = getScoreFilename(level_nr);
   char cookie[MAX_LINE_LEN];
   char line[MAX_LINE_LEN];
   char *line_ptr;
   FILE *file;
 
-  /* start with empty score table */
+  /* always start with reliable default values */
   for(i=0; i<MAX_SCORE_ENTRIES; i++)
   {
     strcpy(highscore[i].Name, EMPTY_ALIAS);
     highscore[i].Score = 0;
   }
 
-  sprintf(filename, "%s/%d.%s",
-         getScoreDir(leveldir[leveldir_nr].filename),
-         level_nr, SCOREFILE_EXTENSION);
-
   if (!(file = fopen(filename, "r")))
     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 (strcmp(cookie, SCORE_COOKIE) != 0)
   {
@@ -691,15 +743,11 @@ void LoadScore(int level_nr)
 void SaveScore(int level_nr)
 {
   int i;
-  char filename[MAX_FILENAME_LEN];
+  char *filename = getScoreFilename(level_nr);
   FILE *file;
 
   InitScoreDirectory(leveldir[leveldir_nr].filename);
 
-  sprintf(filename, "%s/%d.%s",
-         getScoreDir(leveldir[leveldir_nr].filename),
-         level_nr, SCOREFILE_EXTENSION);
-
   if (!(file = fopen(filename, "w")))
   {
     Error(ERR_WARN, "cannot save score for level %d", level_nr);
@@ -981,7 +1029,7 @@ static struct SetupFileList *loadSetupFileList(char *filename)
 
   if (!(file = fopen(filename, "r")))
   {
-    Error(ERR_WARN, "cannot open setup/info file '%s'", filename);
+    Error(ERR_WARN, "cannot open configuration file '%s'", filename);
     return NULL;
   }
 
@@ -1050,7 +1098,7 @@ static struct SetupFileList *loadSetupFileList(char *filename)
   freeSetupFileList(setup_file_list);
 
   if (first_valid_list_entry == NULL)
-    Error(ERR_WARN, "setup/info file '%s' is empty", filename);
+    Error(ERR_WARN, "configuration file '%s' is empty", filename);
 
   return first_valid_list_entry;
 }
@@ -1065,7 +1113,7 @@ static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
   {
     if (strcmp(setup_file_list->value, identifier) != 0)
     {
-      Error(ERR_WARN, "setup/info file has wrong version");
+      Error(ERR_WARN, "configuration file has wrong version");
       return;
     }
     else
@@ -1076,7 +1124,7 @@ static void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
     checkSetupFileListIdentifier(setup_file_list->next, identifier);
   else
   {
-    Error(ERR_WARN, "setup/info file has no version information");
+    Error(ERR_WARN, "configuration file has no version information");
     return;
   }
 }
@@ -1269,7 +1317,10 @@ static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
   int i, current_entry = start_entry;
 
   if ((dir = opendir(level_directory)) == NULL)
-    Error(ERR_EXIT, "cannot read level directory '%s'", level_directory);
+  {
+    Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
+    return current_entry;
+  }
 
   while (current_entry < MAX_LEVDIR_ENTRIES)
   {
@@ -1290,13 +1341,6 @@ static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
       continue;
     }
 
-    if (strlen(dir_entry->d_name) >= MAX_LEVDIR_FILENAME)
-    {
-      Error(ERR_WARN, "filename of level directory '%s' too long -- ignoring",
-           dir_entry->d_name);
-      continue;
-    }
-
     filename = getPath2(directory, LEVELINFO_FILENAME);
     setup_file_list = loadSetupFileList(filename);
 
@@ -1330,8 +1374,8 @@ static int LoadLevelInfoFromLevelDir(char *level_directory, int start_entry)
 
   closedir(dir);
 
-  if (current_entry == start_entry && start_entry != -1)
-    Error(ERR_EXIT, "cannot find any valid level series in directory '%s'",
+  if (current_entry == start_entry)
+    Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
          level_directory);
 
   return current_entry;
@@ -1348,6 +1392,10 @@ void LoadLevelInfo()
                                            num_leveldirs);
   num_leveldirs = LoadLevelInfoFromLevelDir(getUserLevelDir(""),
                                            num_leveldirs);
+
+  if (num_leveldirs == 0)
+    Error(ERR_EXIT, "cannot find any valid level series in any directory");
+
   if (num_leveldirs > 1)
     qsort(leveldir, num_leveldirs, sizeof(struct LevelDirInfo),
          compareLevelDirInfoEntries);