added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / libgame / setup.c
index fa696c581e2d1a69c966a0d4fe54d3c153aaf010..b419875a580e8fd1f87eab476ec4b496f232db7c 100644 (file)
@@ -19,6 +19,7 @@
 #include "platform.h"
 
 #include "setup.h"
+#include "sound.h"
 #include "joystick.h"
 #include "text.h"
 #include "misc.h"
@@ -47,8 +48,6 @@ static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
 #define TOKEN_VALUE_POSITION_DEFAULT           40
 #define TOKEN_COMMENT_POSITION_DEFAULT         60
 
-#define MAX_COOKIE_LEN                         256
-
 #define TREE_NODE_TYPE_DEFAULT                 0
 #define TREE_NODE_TYPE_PARENT                  1
 #define TREE_NODE_TYPE_GROUP                   2
@@ -820,9 +819,34 @@ char *getHelpTextFilename(void)
   return filename;
 }
 
-char *getLevelSetInfoFilename(void)
+static char *getLevelSetInfoBasename(int nr)
+{
+  static char basename[32];
+
+  sprintf(basename, "levelset_%d.txt", nr + 1);
+
+  return basename;
+}
+
+char *getLevelSetInfoFilename(int nr)
 {
+  char *basename = getLevelSetInfoBasename(nr);
+  static char *info_subdir = NULL;
   static char *filename = NULL;
+
+  if (info_subdir == NULL)
+    info_subdir = getPath2(DOCS_DIRECTORY, LEVELSET_INFO_DIRECTORY);
+
+  checked_free(filename);
+
+  // look for level set info file the current level set directory
+  filename = getPath3(getCurrentLevelDir(), info_subdir, basename);
+  if (fileExists(filename))
+    return filename;
+
+  if (nr > 0)
+    return NULL;
+
   char *basenames[] =
   {
     "README",
@@ -1230,7 +1254,58 @@ char *getCustomArtworkLevelConfigFilename(int type)
   return filename;
 }
 
-char *getCustomMusicDirectory(void)
+static boolean directoryExists_CheckMusic(char *directory, boolean check_music)
+{
+  if (!directoryExists(directory))
+    return FALSE;
+
+  if (!check_music)
+    return TRUE;
+
+  Directory *dir;
+  DirectoryEntry *dir_entry;
+  int num_music = getMusicListSize();
+  boolean music_found = FALSE;
+
+  if ((dir = openDirectory(directory)) == NULL)
+    return FALSE;
+
+  while ((dir_entry = readDirectory(dir)) != NULL)     // loop all entries
+  {
+    char *basename = dir_entry->basename;
+    boolean music_already_used = FALSE;
+    int i;
+
+    // skip all music files that are configured in music config file
+    for (i = 0; i < num_music; i++)
+    {
+      struct FileInfo *music = getMusicListEntry(i);
+
+      if (strEqual(basename, music->filename))
+      {
+       music_already_used = TRUE;
+
+       break;
+      }
+    }
+
+    if (music_already_used)
+      continue;
+
+    if (FileIsMusic(dir_entry->filename))
+    {
+      music_found = TRUE;
+
+      break;
+    }
+  }
+
+  closeDirectory(dir);
+
+  return music_found;
+}
+
+static char *getCustomMusicDirectoryExt(boolean check_music)
 {
   static char *directory = NULL;
   boolean skip_setup_artwork = FALSE;
@@ -1241,7 +1316,7 @@ char *getCustomMusicDirectory(void)
   {
     // 1st try: look for special artwork in current level series directory
     directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
-    if (directoryExists(directory))
+    if (directoryExists_CheckMusic(directory, check_music))
       return directory;
 
     free(directory);
@@ -1251,7 +1326,9 @@ char *getCustomMusicDirectory(void)
     {
       // 2nd try: look for special artwork configured in level series config
       directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
-      if (directoryExists(directory))
+
+      // directory also valid if no unconfigured music found (no game music)
+      if (directoryExists_CheckMusic(directory, FALSE))
        return directory;
 
       free(directory);
@@ -1265,7 +1342,9 @@ char *getCustomMusicDirectory(void)
   {
     // 3rd try: look for special artwork in configured artwork directory
     directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
-    if (directoryExists(directory))
+
+    // directory also valid if no unconfigured music found (no game music)
+    if (directoryExists_CheckMusic(directory, FALSE))
       return directory;
 
     free(directory);
@@ -1273,19 +1352,29 @@ char *getCustomMusicDirectory(void)
 
   // 4th try: look for default artwork in new default artwork directory
   directory = getStringCopy(getDefaultMusicDir(MUS_DEFAULT_SUBDIR));
-  if (directoryExists(directory))
+  if (directoryExists_CheckMusic(directory, check_music))
     return directory;
 
   free(directory);
 
   // 5th try: look for default artwork in old default artwork directory
   directory = getStringCopy(options.music_directory);
-  if (directoryExists(directory))
+  if (directoryExists_CheckMusic(directory, check_music))
     return directory;
 
   return NULL;         // cannot find specified artwork file anywhere
 }
 
+char *getCustomMusicDirectory(void)
+{
+  return getCustomMusicDirectoryExt(FALSE);
+}
+
+char *getCustomMusicDirectory_NoConf(void)
+{
+  return getCustomMusicDirectoryExt(TRUE);
+}
+
 void MarkTapeDirectoryUploadsAsComplete(char *level_subdir)
 {
   char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
@@ -1620,30 +1709,56 @@ static void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets)
   *ti_new = ti_cloned;
 }
 
-static boolean adjustTreeGraphicsForEMC(TreeInfo *node)
+static boolean adjustTreeArtworkForEMC(char **artwork_set_1,
+                                      char **artwork_set_2,
+                                      char **artwork_set, boolean prefer_2)
 {
-  boolean settings_changed = FALSE;
+  // do nothing if neither special artwork set 1 nor 2 are defined
+  if (!*artwork_set_1 && !*artwork_set_2)
+    return FALSE;
 
-  while (node)
+  boolean want_1 = (prefer_2 == FALSE);
+  boolean want_2 = (prefer_2 == TRUE);
+  boolean has_only_1 = (!*artwork_set && !*artwork_set_2);
+  boolean has_only_2 = (!*artwork_set && !*artwork_set_1);
+  char *artwork_set_new = NULL;
+
+  // replace missing special artwork 1 or 2 with (optional) standard artwork
+
+  if (!*artwork_set_1)
+    setString(artwork_set_1, *artwork_set);
+
+  if (!*artwork_set_2)
+    setString(artwork_set_2, *artwork_set);
+
+  // set standard artwork to either special artwork 1 or 2, as requested
+
+  if (*artwork_set_1 && (want_1 || has_only_1))
+    artwork_set_new = *artwork_set_1;
+
+  if (*artwork_set_2 && (want_2 || has_only_2))
+    artwork_set_new = *artwork_set_2;
+
+  if (artwork_set_new && !strEqual(*artwork_set, artwork_set_new))
   {
-    boolean want_ecs = (setup.prefer_aga_graphics == FALSE);
-    boolean want_aga = (setup.prefer_aga_graphics == TRUE);
-    boolean has_only_ecs = (!node->graphics_set && !node->graphics_set_aga);
-    boolean has_only_aga = (!node->graphics_set && !node->graphics_set_ecs);
-    char *graphics_set = NULL;
+    setString(artwork_set, artwork_set_new);
 
-    if (node->graphics_set_ecs && (want_ecs || has_only_ecs))
-      graphics_set = node->graphics_set_ecs;
+    return TRUE;
+  }
 
-    if (node->graphics_set_aga && (want_aga || has_only_aga))
-      graphics_set = node->graphics_set_aga;
+  return FALSE;
+}
 
-    if (graphics_set && !strEqual(node->graphics_set, graphics_set))
-    {
-      setString(&node->graphics_set, graphics_set);
-      settings_changed = TRUE;
-    }
+static boolean adjustTreeGraphicsForEMC(TreeInfo *node)
+{
+  boolean settings_changed = FALSE;
 
+  while (node)
+  {
+    settings_changed |= adjustTreeArtworkForEMC(&node->graphics_set_ecs,
+                                               &node->graphics_set_aga,
+                                               &node->graphics_set,
+                                               setup.prefer_aga_graphics);
     if (node->node_group != NULL)
       settings_changed |= adjustTreeGraphicsForEMC(node->node_group);
 
@@ -1659,24 +1774,10 @@ static boolean adjustTreeSoundsForEMC(TreeInfo *node)
 
   while (node)
   {
-    boolean want_default = (setup.prefer_lowpass_sounds == FALSE);
-    boolean want_lowpass = (setup.prefer_lowpass_sounds == TRUE);
-    boolean has_only_default = (!node->sounds_set && !node->sounds_set_lowpass);
-    boolean has_only_lowpass = (!node->sounds_set && !node->sounds_set_default);
-    char *sounds_set = NULL;
-
-    if (node->sounds_set_default && (want_default || has_only_default))
-      sounds_set = node->sounds_set_default;
-
-    if (node->sounds_set_lowpass && (want_lowpass || has_only_lowpass))
-      sounds_set = node->sounds_set_lowpass;
-
-    if (sounds_set && !strEqual(node->sounds_set, sounds_set))
-    {
-      setString(&node->sounds_set, sounds_set);
-      settings_changed = TRUE;
-    }
-
+    settings_changed |= adjustTreeArtworkForEMC(&node->sounds_set_default,
+                                               &node->sounds_set_lowpass,
+                                               &node->sounds_set,
+                                               setup.prefer_lowpass_sounds);
     if (node->node_group != NULL)
       settings_changed |= adjustTreeSoundsForEMC(node->node_group);
 
@@ -2003,21 +2104,6 @@ void SetFilePermissions(char *filename, int permission_class)
   chmod(filename, perms);
 }
 
-char *getCookie(char *file_type)
-{
-  static char cookie[MAX_COOKIE_LEN + 1];
-
-  if (strlen(program.cookie_prefix) + 1 +
-      strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
-    return "[COOKIE ERROR]";   // should never happen
-
-  sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
-         program.cookie_prefix, file_type,
-         program.version_super, program.version_major);
-
-  return cookie;
-}
-
 void fprintFileHeader(FILE *file, char *basename)
 {
   char *prefix = "# ";
@@ -2695,6 +2781,7 @@ SetupFileHash *loadSetupFileHash(char *filename)
 // ============================================================================
 
 #define TOKEN_STR_LAST_LEVEL_SERIES            "last_level_series"
+#define TOKEN_STR_LAST_PLAYED_MENU_USED                "last_played_menu_used"
 #define TOKEN_STR_LAST_PLAYED_LEVEL            "last_played_level"
 #define TOKEN_STR_HANDICAP_LEVEL               "handicap_level"
 #define TOKEN_STR_LAST_USER                    "last_user"
@@ -2730,10 +2817,12 @@ SetupFileHash *loadSetupFileHash(char *filename)
 #define LEVELINFO_TOKEN_EMPTY_LEVEL_NAME       27
 #define LEVELINFO_TOKEN_FORCE_LEVEL_NAME       28
 #define LEVELINFO_TOKEN_HANDICAP               29
-#define LEVELINFO_TOKEN_SKIP_LEVELS            30
-#define LEVELINFO_TOKEN_USE_EMC_TILES          31
+#define LEVELINFO_TOKEN_TIME_LIMIT             30
+#define LEVELINFO_TOKEN_SKIP_LEVELS            31
+#define LEVELINFO_TOKEN_USE_EMC_TILES          32
+#define LEVELINFO_TOKEN_INFO_SCREENS_FROM_MAIN 33
 
-#define NUM_LEVELINFO_TOKENS                   32
+#define NUM_LEVELINFO_TOKENS                   34
 
 static LevelDirTree ldi;
 
@@ -2770,8 +2859,10 @@ static struct TokenInfo levelinfo_tokens[] =
   { TYPE_STRING,       &ldi.empty_level_name,  "empty_level_name"      },
   { TYPE_BOOLEAN,      &ldi.force_level_name,  "force_level_name"      },
   { TYPE_BOOLEAN,      &ldi.handicap,          "handicap"              },
+  { TYPE_BOOLEAN,      &ldi.time_limit,        "time_limit"            },
   { TYPE_BOOLEAN,      &ldi.skip_levels,       "skip_levels"           },
-  { TYPE_BOOLEAN,      &ldi.use_emc_tiles,     "use_emc_tiles"         }
+  { TYPE_BOOLEAN,      &ldi.use_emc_tiles,     "use_emc_tiles"         },
+  { TYPE_BOOLEAN,      &ldi.info_screens_from_main, "info_screens_from_main" }
 };
 
 static struct TokenInfo artworkinfo_tokens[] =
@@ -2876,9 +2967,11 @@ static void setTreeInfoToDefaults(TreeInfo *ti, int type)
     ti->handicap_level = 0;
     ti->readonly = TRUE;
     ti->handicap = TRUE;
+    ti->time_limit = TRUE;
     ti->skip_levels = FALSE;
 
     ti->use_emc_tiles = FALSE;
+    ti->info_screens_from_main = FALSE;
   }
 }
 
@@ -2961,9 +3054,11 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ti, TreeInfo *parent)
     ti->handicap_level = parent->handicap_level;
     ti->readonly = parent->readonly;
     ti->handicap = parent->handicap;
+    ti->time_limit = parent->time_limit;
     ti->skip_levels = parent->skip_levels;
 
     ti->use_emc_tiles = parent->use_emc_tiles;
+    ti->info_screens_from_main = parent->info_screens_from_main;
   }
 }
 
@@ -3033,9 +3128,11 @@ static TreeInfo *getTreeInfoCopy(TreeInfo *ti)
   ti_copy->user_defined                = ti->user_defined;
   ti_copy->readonly            = ti->readonly;
   ti_copy->handicap            = ti->handicap;
+  ti_copy->time_limit          = ti->time_limit;
   ti_copy->skip_levels         = ti->skip_levels;
 
   ti_copy->use_emc_tiles       = ti->use_emc_tiles;
+  ti_copy->info_screens_from_main = ti->info_screens_from_main;
 
   ti_copy->color               = ti->color;
   ti_copy->class_desc          = getStringCopy(ti->class_desc);
@@ -4497,6 +4594,12 @@ static boolean AddTreeSetToTreeInfoExt(TreeInfo *tree_node_old, char *tree_dir,
   TreeInfo *tree_node_new = getTreeInfoFromIdentifier(*tree_node_first,
                                                      tree_subdir_new);
 
+  // if not found, check if added node is level group or artwork group
+  if (tree_node_new == NULL)
+    tree_node_new = getTreeInfoFromIdentifierExt(*tree_node_first,
+                                                tree_subdir_new,
+                                                TREE_NODE_TYPE_GROUP);
+
   if (tree_node_new == NULL)           // should not happen
     return FALSE;
 
@@ -4926,33 +5029,51 @@ static void UpdateLastPlayedLevels_List(void)
   setString(&last_level_series[0], leveldir_current->identifier);
 }
 
-static TreeInfo *StoreOrRestoreLastPlayedLevels(TreeInfo *node, boolean store)
+#define LAST_PLAYED_MODE_SET                   1
+#define LAST_PLAYED_MODE_SET_FORCED            2
+#define LAST_PLAYED_MODE_GET                   3
+
+static TreeInfo *StoreOrRestoreLastPlayedLevels(TreeInfo *node, int mode)
 {
   static char *identifier = NULL;
 
-  if (store)
+  if (mode == LAST_PLAYED_MODE_SET)
   {
     setString(&identifier, (node && node->is_copy ? node->identifier : NULL));
-
-    return NULL;       // not used
   }
-  else
+  else if (mode == LAST_PLAYED_MODE_SET_FORCED)
+  {
+    setString(&identifier, (node ? node->identifier : NULL));
+  }
+  else if (mode == LAST_PLAYED_MODE_GET)
   {
     TreeInfo *node_new = getTreeInfoFromIdentifierExt(leveldir_first,
                                                      identifier,
                                                      TREE_NODE_TYPE_COPY);
     return (node_new != NULL ? node_new : node);
   }
+
+  return NULL;         // not used
 }
 
 void StoreLastPlayedLevels(TreeInfo *node)
 {
-  StoreOrRestoreLastPlayedLevels(node, TRUE);
+  StoreOrRestoreLastPlayedLevels(node, LAST_PLAYED_MODE_SET);
+}
+
+void ForcedStoreLastPlayedLevels(TreeInfo *node)
+{
+  StoreOrRestoreLastPlayedLevels(node, LAST_PLAYED_MODE_SET_FORCED);
 }
 
 void RestoreLastPlayedLevels(TreeInfo **node)
 {
-  *node = StoreOrRestoreLastPlayedLevels(*node, FALSE);
+  *node = StoreOrRestoreLastPlayedLevels(*node, LAST_PLAYED_MODE_GET);
+}
+
+boolean CheckLastPlayedLevels(void)
+{
+  return (StoreOrRestoreLastPlayedLevels(NULL, LAST_PLAYED_MODE_GET) != NULL);
 }
 
 void LoadLevelSetup_LastSeries(void)
@@ -4990,6 +5111,13 @@ void LoadLevelSetup_LastSeries(void)
     if (leveldir_current == NULL)
       leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
 
+    char *last_played_menu_used =
+      getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_MENU_USED);
+
+    // store if last level set was selected from "last played" menu
+    if (strEqual(last_played_menu_used, "true"))
+      ForcedStoreLastPlayedLevels(leveldir_current);
+
     for (i = 0; i < MAX_LEVELDIR_HISTORY; i++)
     {
       char token[strlen(TOKEN_STR_LAST_LEVEL_SERIES) + 10];
@@ -5052,7 +5180,14 @@ static void SaveLevelSetup_LastSeries_Ext(boolean deactivate_last_level_series)
     fprintf(file, "# %s\n# ", "the following level set may have caused a problem and was deactivated");
 
   fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
-                                              leveldir_current->identifier));
+                                                leveldir_current->identifier));
+
+  // store if last level set was selected from "last played" menu
+  boolean last_played_menu_used = CheckLastPlayedLevels();
+  char *setup_value = getSetupValue(TYPE_BOOLEAN, &last_played_menu_used);
+
+  fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_MENU_USED,
+                                                setup_value));
 
   for (i = 0; last_level_series[i] != NULL; i++)
   {