#define MAX_COOKIE_LEN 256
+#define TREE_NODE_TYPE_DEFAULT 0
+#define TREE_NODE_TYPE_PARENT 1
+#define TREE_NODE_TYPE_GROUP 2
+#define TREE_NODE_TYPE_COPY 3
+
+#define TREE_NODE_TYPE(ti) (ti->node_group ? TREE_NODE_TYPE_GROUP : \
+ ti->parent_link ? TREE_NODE_TYPE_PARENT : \
+ ti->is_copy ? TREE_NODE_TYPE_COPY : \
+ TREE_NODE_TYPE_DEFAULT)
+
static void setTreeInfoToDefaults(TreeInfo *, int);
static TreeInfo *getTreeInfoCopy(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;
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);
return score_dir;
}
+static char *getScoreTapeDir(char *level_subdir, int nr)
+{
+ static char *score_tape_dir = NULL;
+ char tape_subdir[MAX_FILENAME_LEN];
+
+ checked_free(score_tape_dir);
+
+ sprintf(tape_subdir, "%03d", nr);
+ score_tape_dir = getPath2(getScoreDir(level_subdir), tape_subdir);
+
+ return score_tape_dir;
+}
+
static char *getUserSubdir(int nr)
{
static char user_subdir[16] = { 0 };
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;
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);
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);
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];
sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
filename = getPath2(getSolutionTapeDir(), basename);
- if (!fileExists(filename))
- {
- static char *filename_sln = NULL;
+ return filename;
+}
+
+char *getSokobanSolutionTapeFilename(int nr)
+{
+ static char *filename = NULL;
+ char basename[MAX_FILENAME_LEN];
+
+ checked_free(filename);
- checked_free(filename_sln);
+ sprintf(basename, "%03d.sln", nr);
+ filename = getPath2(getSolutionTapeDir(), basename);
+
+ return filename;
+}
- sprintf(basename, "%03d.sln", nr);
- filename_sln = getPath2(getSolutionTapeDir(), basename);
+char *getSolutionTapeFilename(int nr)
+{
+ char *filename = getDefaultSolutionTapeFilename(nr);
- if (fileExists(filename_sln))
- return filename_sln;
+ if (!fileExists(filename))
+ {
+ char *filename2 = getSokobanSolutionTapeFilename(nr);
+
+ if (fileExists(filename2))
+ return filename2;
}
return filename;
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];
+ char basename_raw[MAX_FILENAME_LEN];
+ char timestamp[20];
+
+ sprintf(timestamp, "%s", getCurrentTimestamp());
+ sprintf(basename_raw, "%s-%s", timestamp, name);
+ sprintf(basename, "%s-%08x", timestamp, get_hash_from_key(basename_raw));
+
+ return basename;
+}
+
+char *getScoreTapeFilename(char *basename_no_ext, int nr)
+{
+ static char *filename = NULL;
+ char basename[MAX_FILENAME_LEN];
+
+ checked_free(filename);
+
+ sprintf(basename, "%s.%s", basename_no_ext, TAPEFILE_EXTENSION);
+
+ // used instead of "leveldir_current->subdir" (for network games)
+ filename = getPath2(getScoreTapeDir(levelset.identifier, nr), basename);
+
+ return filename;
+}
+
char *getSetupFilename(void)
{
static char *filename = NULL;
void InitScoreDirectory(char *level_subdir)
{
- int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
+ createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
+ createDirectory(getScoreDir(NULL), "main score", PERMS_PRIVATE);
+ createDirectory(getScoreDir(level_subdir), "level score", PERMS_PRIVATE);
+}
- if (program.global_scores)
- createDirectory(getCommonDataDir(), "common data", permissions);
- else
- createDirectory(getMainUserGameDataDir(), "main user data", 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)
+{
+ InitScoreDirectory(level_subdir);
- createDirectory(getScoreDir(NULL), "main score", permissions);
- createDirectory(getScoreDir(level_subdir), "level score", permissions);
+ createDirectory(getScoreTapeDir(level_subdir, nr), "score tape", PERMS_PRIVATE);
}
static void SaveUserLevelInfo(void);
boolean validLevelSeries(TreeInfo *node)
{
- return (node != NULL && !node->node_group && !node->parent_link);
+ // in a number of cases, tree node is no valid level set
+ if (node == NULL || node->node_group || node->parent_link || node->is_copy)
+ return FALSE;
+
+ return TRUE;
}
-TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
+TreeInfo *getValidLevelSeries(TreeInfo *node, TreeInfo *default_node)
+{
+ if (validLevelSeries(node))
+ return node;
+ else if (node->is_copy)
+ return getTreeInfoFromIdentifier(leveldir_first, node->identifier);
+ else
+ return getFirstValidTreeInfoEntry(default_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)
}
static TreeInfo *getTreeInfoFromIdentifierExt(TreeInfo *node, char *identifier,
- boolean include_node_groups)
+ int node_type_wanted)
{
if (identifier == NULL)
return NULL;
while (node)
{
+ if (TREE_NODE_TYPE(node) == node_type_wanted &&
+ strEqual(identifier, node->identifier))
+ return node;
+
if (node->node_group)
{
- if (include_node_groups && strEqual(identifier, node->identifier))
- return node;
-
TreeInfo *node_group = getTreeInfoFromIdentifierExt(node->node_group,
identifier,
- include_node_groups);
+ node_type_wanted);
if (node_group)
return node_group;
}
- else if (!node->parent_link)
- {
- if (strEqual(identifier, node->identifier))
- return node;
- }
node = node->next;
}
TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
{
- return getTreeInfoFromIdentifierExt(node, identifier, FALSE);
+ return getTreeInfoFromIdentifierExt(node, identifier, TREE_NODE_TYPE_DEFAULT);
}
static TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent,
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)
(node->node_parent ? node->node_parent->identifier : "-"),
(node->node_group ? "[GROUP]" : ""));
+ if (!node->node_group && !node->parent_link)
+ num_leaf_nodes++;
+
/*
// use for dumping artwork info tree
Debug("tree", "subdir == '%s' ['%s', '%s'] [%d])",
*/
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,
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;
void UpdateLastPlayedLevels_TreeInfo(void)
{
char **last_level_series = setup.level_setup.last_level_series;
- boolean reset_leveldir_current = FALSE;
LevelDirTree *leveldir_last;
TreeInfo **node_new = NULL;
int i;
InitLastPlayedLevels_ParentNode();
- // check if current level set is from "last played" sub-tree to be rebuilt
- reset_leveldir_current = strEqual(leveldir_current->node_parent->identifier,
- TOKEN_STR_LAST_LEVEL_SERIES);
-
leveldir_last = getTreeInfoFromIdentifierExt(leveldir_first,
TOKEN_STR_LAST_LEVEL_SERIES,
- TRUE);
+ TREE_NODE_TYPE_GROUP);
if (leveldir_last == NULL)
return;
node_new = &((*node_new)->next);
}
-
- if (reset_leveldir_current)
- leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
- last_level_series[0]);
}
static void UpdateLastPlayedLevels_List(void)
setString(&last_level_series[0], leveldir_current->identifier);
}
+static TreeInfo *StoreOrRestoreLastPlayedLevels(TreeInfo *node, boolean store)
+{
+ static char *identifier = NULL;
+
+ if (store)
+ {
+ setString(&identifier, (node && node->is_copy ? node->identifier : NULL));
+
+ return NULL; // not used
+ }
+ else
+ {
+ TreeInfo *node_new = getTreeInfoFromIdentifierExt(leveldir_first,
+ identifier,
+ TREE_NODE_TYPE_COPY);
+ return (node_new != NULL ? node_new : node);
+ }
+}
+
+void StoreLastPlayedLevels(TreeInfo *node)
+{
+ StoreOrRestoreLastPlayedLevels(node, TRUE);
+}
+
+void RestoreLastPlayedLevels(TreeInfo **node)
+{
+ *node = StoreOrRestoreLastPlayedLevels(*node, FALSE);
+}
+
void LoadLevelSetup_LastSeries(void)
{
// --------------------------------------------------------------------------