added definition for persistent directory name for Emscripten platform
[rocksndiamonds.git] / src / libgame / setup.c
index dbfec24dd8202d1a924230408950d822064c0dd6..60bb56853b59b6016a0454730a43111b71316fca 100644 (file)
@@ -88,6 +88,16 @@ static char *getLevelClassDescription(TreeInfo *ti)
     return "Unknown Level Class";
 }
 
+static char *getCacheDir(void)
+{
+  static char *cache_dir = NULL;
+
+  if (cache_dir == NULL)
+    cache_dir = getPath2(getMainUserGameDataDir(), CACHE_DIRECTORY);
+
+  return cache_dir;
+}
+
 static char *getScoreDir(char *level_subdir)
 {
   static char *score_dir = NULL;
@@ -95,13 +105,29 @@ static char *getScoreDir(char *level_subdir)
   char *score_subdir = SCORES_DIRECTORY;
 
   if (score_dir == NULL)
+    score_dir = getPath2(getMainUserGameDataDir(), score_subdir);
+
+  if (level_subdir != NULL)
   {
-    if (program.global_scores)
-      score_dir = getPath2(getCommonDataDir(),       score_subdir);
-    else
-      score_dir = getPath2(getMainUserGameDataDir(), score_subdir);
+    checked_free(score_level_dir);
+
+    score_level_dir = getPath2(score_dir, level_subdir);
+
+    return score_level_dir;
   }
 
+  return score_dir;
+}
+
+static char *getScoreCacheDir(char *level_subdir)
+{
+  static char *score_dir = NULL;
+  static char *score_level_dir = NULL;
+  char *score_subdir = SCORES_DIRECTORY;
+
+  if (score_dir == NULL)
+    score_dir = getPath2(getCacheDir(), score_subdir);
+
   if (level_subdir != NULL)
   {
     checked_free(score_level_dir);
@@ -169,16 +195,6 @@ static char *getLevelSetupDir(char *level_subdir)
   return levelsetup_dir;
 }
 
-static char *getCacheDir(void)
-{
-  static char *cache_dir = NULL;
-
-  if (cache_dir == NULL)
-    cache_dir = getPath2(getMainUserGameDataDir(), CACHE_DIRECTORY);
-
-  return cache_dir;
-}
-
 static char *getNetworkDir(void)
 {
   static char *network_dir = NULL;
@@ -263,7 +279,7 @@ char *getNewUserLevelSubdir(void)
   return new_level_subdir;
 }
 
-static char *getTapeDir(char *level_subdir)
+char *getTapeDir(char *level_subdir)
 {
   static char *tape_dir = NULL;
   char *data_dir = getUserGameDataDir();
@@ -518,8 +534,8 @@ char *getProgramConfigFilename(char *command_filename)
     if (strSuffix(command_filename_1, ".exe"))
       command_filename_1[strlen(command_filename_1) - 4] = '\0';
 
-    char *ro_base_path = getProgramMainDataPath(command_filename, RO_BASE_PATH);
-    char *conf_directory = getPath2(ro_base_path, CONF_DIRECTORY);
+    char *base_path = getProgramMainDataPath(command_filename, BASE_PATH);
+    char *conf_directory = getPath2(base_path, CONF_DIRECTORY);
 
     char *command_basepath = getBasePath(command_filename);
     char *command_basename = getBaseNameNoSuffix(command_filename);
@@ -529,7 +545,7 @@ char *getProgramConfigFilename(char *command_filename)
     config_filename_2 = getStringCat2(command_filename_2, ".conf");
     config_filename_3 = getPath2(conf_directory, SETUP_FILENAME);
 
-    checked_free(ro_base_path);
+    checked_free(base_path);
     checked_free(conf_directory);
 
     checked_free(command_basepath);
@@ -566,7 +582,20 @@ char *getTapeFilename(int nr)
   return filename;
 }
 
-char *getSolutionTapeFilename(int nr)
+char *getTemporaryTapeFilename(void)
+{
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
+
+  checked_free(filename);
+
+  sprintf(basename, "tmp.%s", TAPEFILE_EXTENSION);
+  filename = getPath2(getTapeDir(NULL), basename);
+
+  return filename;
+}
+
+char *getDefaultSolutionTapeFilename(int nr)
 {
   static char *filename = NULL;
   char basename[MAX_FILENAME_LEN];
@@ -576,17 +605,32 @@ char *getSolutionTapeFilename(int nr)
   sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
   filename = getPath2(getSolutionTapeDir(), basename);
 
-  if (!fileExists(filename))
-  {
-    static char *filename_sln = NULL;
+  return filename;
+}
 
-    checked_free(filename_sln);
+char *getSokobanSolutionTapeFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
 
-    sprintf(basename, "%03d.sln", nr);
-    filename_sln = getPath2(getSolutionTapeDir(), basename);
+  checked_free(filename);
 
-    if (fileExists(filename_sln))
-      return filename_sln;
+  sprintf(basename, "%03d.sln", nr);
+  filename = getPath2(getSolutionTapeDir(), basename);
+
+  return filename;
+}
+
+char *getSolutionTapeFilename(int nr)
+{
+  char *filename = getDefaultSolutionTapeFilename(nr);
+
+  if (!fileExists(filename))
+  {
+    char *filename2 = getSokobanSolutionTapeFilename(nr);
+
+    if (fileExists(filename2))
+      return filename2;
   }
 
   return filename;
@@ -607,6 +651,21 @@ char *getScoreFilename(int nr)
   return filename;
 }
 
+char *getScoreCacheFilename(int nr)
+{
+  static char *filename = NULL;
+  char basename[MAX_FILENAME_LEN];
+
+  checked_free(filename);
+
+  sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
+
+  // used instead of "leveldir_current->subdir" (for network games)
+  filename = getPath2(getScoreCacheDir(levelset.identifier), basename);
+
+  return filename;
+}
+
 char *getScoreTapeBasename(char *name)
 {
   static char basename[MAX_FILENAME_LEN];
@@ -794,6 +853,65 @@ char *getLevelSetTitleMessageFilename(int nr, boolean initial)
   return NULL;         // cannot find specified artwork file anywhere
 }
 
+static char *getCreditsBasename(int nr)
+{
+  static char basename[32];
+
+  sprintf(basename, "credits_%d.txt", nr + 1);
+
+  return basename;
+}
+
+char *getCreditsFilename(int nr, boolean global)
+{
+  char *basename = getCreditsBasename(nr);
+  char *basepath = NULL;
+  static char *credits_subdir = NULL;
+  static char *filename = NULL;
+
+  if (credits_subdir == NULL)
+    credits_subdir = getPath2(DOCS_DIRECTORY, CREDITS_DIRECTORY);
+
+  checked_free(filename);
+
+  // look for credits file in the game's base or current level set directory
+  basepath = (global ? options.base_directory : getCurrentLevelDir());
+
+  filename = getPath3(basepath, credits_subdir, basename);
+  if (fileExists(filename))
+    return filename;
+
+  return NULL;         // cannot find credits file
+}
+
+static char *getProgramInfoBasename(int nr)
+{
+  static char basename[32];
+
+  sprintf(basename, "program_%d.txt", nr + 1);
+
+  return basename;
+}
+
+char *getProgramInfoFilename(int nr)
+{
+  char *basename = getProgramInfoBasename(nr);
+  static char *info_subdir = NULL;
+  static char *filename = NULL;
+
+  if (info_subdir == NULL)
+    info_subdir = getPath2(DOCS_DIRECTORY, INFO_DIRECTORY);
+
+  checked_free(filename);
+
+  // look for program info file in the game's base directory
+  filename = getPath3(options.base_directory, info_subdir, basename);
+  if (fileExists(filename))
+    return filename;
+
+  return NULL;         // cannot find program info file
+}
+
 static char *getCorrectedArtworkBasename(char *basename)
 {
   return basename;
@@ -1096,33 +1214,66 @@ char *getCustomMusicDirectory(void)
   return NULL;         // cannot find specified artwork file anywhere
 }
 
+void MarkTapeDirectoryUploadsAsComplete(char *level_subdir)
+{
+  char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+
+  touchFile(filename);
+
+  checked_free(filename);
+}
+
+void MarkTapeDirectoryUploadsAsIncomplete(char *level_subdir)
+{
+  char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+
+  unlink(filename);
+
+  checked_free(filename);
+}
+
+boolean CheckTapeDirectoryUploadsComplete(char *level_subdir)
+{
+  char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+  boolean success = fileExists(filename);
+
+  checked_free(filename);
+
+  return success;
+}
+
 void InitTapeDirectory(char *level_subdir)
 {
+  boolean new_tape_dir = !directoryExists(getTapeDir(level_subdir));
+
   createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
   createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
   createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
+
+  if (new_tape_dir)
+    MarkTapeDirectoryUploadsAsComplete(level_subdir);
 }
 
 void InitScoreDirectory(char *level_subdir)
 {
-  int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
-
-  if (program.global_scores)
-    createDirectory(getCommonDataDir(), "common data", permissions);
-  else
-    createDirectory(getMainUserGameDataDir(), "main user data", permissions);
+  createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
+  createDirectory(getScoreDir(NULL), "main score", PERMS_PRIVATE);
+  createDirectory(getScoreDir(level_subdir), "level score", PERMS_PRIVATE);
+}
 
-  createDirectory(getScoreDir(NULL), "main score", permissions);
-  createDirectory(getScoreDir(level_subdir), "level score", permissions);
+void InitScoreCacheDirectory(char *level_subdir)
+{
+  createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
+  createDirectory(getCacheDir(), "cache data", PERMS_PRIVATE);
+  createDirectory(getScoreCacheDir(NULL), "main score", PERMS_PRIVATE);
+  createDirectory(getScoreCacheDir(level_subdir), "level score", PERMS_PRIVATE);
 }
 
 void InitScoreTapeDirectory(char *level_subdir, int nr)
 {
-  int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
-
   InitScoreDirectory(level_subdir);
 
-  createDirectory(getScoreTapeDir(level_subdir, nr), "score tape", permissions);
+  createDirectory(getScoreTapeDir(level_subdir, nr), "score tape", PERMS_PRIVATE);
 }
 
 static void SaveUserLevelInfo(void);
@@ -1231,22 +1382,35 @@ TreeInfo *getValidLevelSeries(TreeInfo *node, TreeInfo *default_node)
     return getFirstValidTreeInfoEntry(default_node);
 }
 
-TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
+static TreeInfo *getValidTreeInfoEntryExt(TreeInfo *node, boolean get_next_node)
 {
   if (node == NULL)
     return NULL;
 
-  if (node->node_group)                // enter level group (step down into tree)
+  if (node->node_group)                // enter node group (step down into tree)
     return getFirstValidTreeInfoEntry(node->node_group);
-  else if (node->parent_link)  // skip start entry of level group
-  {
-    if (node->next)            // get first real level series entry
-      return getFirstValidTreeInfoEntry(node->next);
-    else                       // leave empty level group and go on
-      return getFirstValidTreeInfoEntry(node->node_parent->next);
-  }
-  else                         // this seems to be a regular level series
+
+  if (node->parent_link)       // skip first node (back link) of node group
+    get_next_node = TRUE;
+
+  if (!get_next_node)          // get current regular tree node
     return node;
+
+  // get next regular tree node, or step up until one is found
+  while (node->next == NULL && node->node_parent != NULL)
+    node = node->node_parent;
+
+  return getFirstValidTreeInfoEntry(node->next);
+}
+
+TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
+{
+  return getValidTreeInfoEntryExt(node, FALSE);
+}
+
+TreeInfo *getNextValidTreeInfoEntry(TreeInfo *node)
+{
+  return getValidTreeInfoEntryExt(node, TRUE);
 }
 
 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
@@ -1432,9 +1596,10 @@ static boolean adjustTreeSoundsForEMC(TreeInfo *node)
   return settings_changed;
 }
 
-void dumpTreeInfo(TreeInfo *node, int depth)
+int dumpTreeInfo(TreeInfo *node, int depth)
 {
   char bullet_list[] = { '-', '*', 'o' };
+  int num_leaf_nodes = 0;
   int i;
 
   if (depth == 0)
@@ -1450,7 +1615,11 @@ void dumpTreeInfo(TreeInfo *node, int depth)
     DebugContinued("tree", "%c '%s' ['%s] [PARENT: '%s'] %s\n",
                   bullet, node->name, node->identifier,
                   (node->node_parent ? node->node_parent->identifier : "-"),
-                  (node->node_group ? "[GROUP]" : ""));
+                  (node->node_group ? "[GROUP]" :
+                   node->is_copy ? "[COPY]" : ""));
+
+    if (!node->node_group && !node->parent_link)
+      num_leaf_nodes++;
 
     /*
     // use for dumping artwork info tree
@@ -1459,10 +1628,15 @@ void dumpTreeInfo(TreeInfo *node, int depth)
     */
 
     if (node->node_group != NULL)
-      dumpTreeInfo(node->node_group, depth + 1);
+      num_leaf_nodes += dumpTreeInfo(node->node_group, depth + 1);
 
     node = node->next;
   }
+
+  if (depth == 0)
+    Debug("tree", "Summary: %d leaf nodes found", num_leaf_nodes);
+
+  return num_leaf_nodes;
 }
 
 void sortTreeInfoBySortFunction(TreeInfo **node_first,
@@ -1582,7 +1756,7 @@ char *getHomeDir(void)
       strcpy(dir, ".");
   }
 #elif defined(PLATFORM_EMSCRIPTEN)
-  dir = "/persistent";
+  dir = PERSISTENT_DIRECTORY;
 #elif defined(PLATFORM_UNIX)
   if (dir == NULL)
   {
@@ -1603,29 +1777,6 @@ char *getHomeDir(void)
   return dir;
 }
 
-char *getCommonDataDir(void)
-{
-  static char *common_data_dir = NULL;
-
-#if defined(PLATFORM_WIN32)
-  if (common_data_dir == NULL)
-  {
-    char *dir = checked_malloc(MAX_PATH + 1);
-
-    if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
-       && !strEqual(dir, ""))          // empty for Windows 95/98
-      common_data_dir = getPath2(dir, program.userdata_subdir);
-    else
-      common_data_dir = options.rw_base_directory;
-  }
-#else
-  if (common_data_dir == NULL)
-    common_data_dir = options.rw_base_directory;
-#endif
-
-  return common_data_dir;
-}
-
 char *getPersonalDataDir(void)
 {
   static char *personal_data_dir = NULL;
@@ -1983,7 +2134,7 @@ unsigned int get_hash_from_key(void *key)
   return hash;
 }
 
-static int keys_are_equal(void *key1, void *key2)
+int hash_keys_are_equal(void *key1, void *key2)
 {
   return (strEqual((char *)key1, (char *)key2));
 }
@@ -1991,7 +2142,7 @@ static int keys_are_equal(void *key1, void *key2)
 SetupFileHash *newSetupFileHash(void)
 {
   SetupFileHash *new_hash =
-    create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
+    create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
 
   if (new_hash == NULL)
     Fail("create_hashtable() failed -- out of memory");
@@ -2988,6 +3139,17 @@ static void setTreeInfoParentNodes(TreeInfo *node, TreeInfo *node_parent)
   }
 }
 
+TreeInfo *addTopTreeInfoNode(TreeInfo *node_first)
+{
+  // add top tree node with back link node in previous tree
+  node_first = createTopTreeInfoNode(node_first);
+
+  // set all parent links (back links) in complete tree
+  setTreeInfoParentNodes(node_first, NULL);
+
+  return node_first;
+}
+
 
 // ----------------------------------------------------------------------------
 // functions for handling level and custom artwork info cache
@@ -3540,7 +3702,7 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
     (leveldir_new->user_defined || !leveldir_new->handicap ?
      leveldir_new->last_level : leveldir_new->first_level);
 
-  DrawInitText(leveldir_new->name, 150, FC_YELLOW);
+  DrawInitTextItem(leveldir_new->name);
 
   pushTreeInfo(node_first, leveldir_new);
 
@@ -3656,7 +3818,7 @@ void LoadLevelInfo(void)
 {
   InitUserLevelDirectory(getLoginName());
 
-  DrawInitText("Loading level series", 120, FC_GREEN);
+  DrawInitTextHead("Loading level series");
 
   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
   LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
@@ -3929,7 +4091,7 @@ void LoadArtworkInfo(void)
 {
   LoadArtworkInfoCache();
 
-  DrawInitText("Looking for custom artwork", 120, FC_GREEN);
+  DrawInitTextHead("Looking for custom artwork");
 
   LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
                                options.graphics_directory,
@@ -4064,7 +4226,7 @@ static void LoadArtworkInfoFromLevelInfoExt(ArtworkDirTree **artwork_node,
        setArtworkInfoCacheEntry(artwork_new, level_node, type);
     }
 
-    DrawInitText(level_node->name, 150, FC_YELLOW);
+    DrawInitTextItem(level_node->name);
 
     if (level_node->node_group != NULL)
     {
@@ -4122,18 +4284,15 @@ static void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node)
   LoadArtworkInfoFromLevelInfoExt(artwork_node, NULL, leveldir_first_all, TRUE);
   LoadArtworkInfoFromLevelInfoExt(artwork_node, NULL, leveldir_first_all, FALSE);
 
-  // add top tree node over all three separate sub-trees
-  *artwork_node = createTopTreeInfoNode(*artwork_node);
-
-  // set all parent links (back links) in complete artwork tree
-  setTreeInfoParentNodes(*artwork_node, NULL);
+  // add top tree node over all sub-trees and set parent links
+  *artwork_node = addTopTreeInfoNode(*artwork_node);
 }
 
 void LoadLevelArtworkInfo(void)
 {
   print_timestamp_init("LoadLevelArtworkInfo");
 
-  DrawInitText("Looking for custom level artwork", 120, FC_GREEN);
+  DrawInitTextHead("Looking for custom level artwork");
 
   print_timestamp_time("DrawTimeText");
 
@@ -4789,7 +4948,7 @@ static void SaveLevelSetup_LastSeries_Ext(boolean deactivate_last_level_series)
 
   for (i = 0; last_level_series[i] != NULL; i++)
   {
-    char token[strlen(TOKEN_STR_LAST_LEVEL_SERIES) + 10];
+    char token[strlen(TOKEN_STR_LAST_LEVEL_SERIES) + 1 + 10 + 1];
 
     sprintf(token, "%s.%03d", TOKEN_STR_LAST_LEVEL_SERIES, i);