#include <dirent.h>
#include <string.h>
#include <unistd.h>
+#include <errno.h>
#include "platform.h"
checked_free(*artwork_set_ptr);
- if (fileExists(dir))
+ if (directoryExists(dir))
{
*artwork_path_ptr = getStringCopy(getClassicArtworkDir(ti->type));
*artwork_set_ptr = getStringCopy(getClassicArtworkSet(ti->type));
sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
filename = getPath2(getSolutionTapeDir(), basename);
+ if (!fileExists(filename))
+ {
+ static char *filename_sln = NULL;
+
+ checked_free(filename_sln);
+
+ sprintf(basename, "%03d.sln", nr);
+ filename_sln = getPath2(getSolutionTapeDir(), basename);
+
+ if (fileExists(filename_sln))
+ return filename_sln;
+ }
+
return filename;
}
static char *getCorrectedArtworkBasename(char *basename)
{
- char *basename_corrected = basename;
-
-#if defined(PLATFORM_MSDOS)
- if (program.filename_prefix != NULL)
- {
- int prefix_len = strlen(program.filename_prefix);
-
- if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
- basename_corrected = &basename[prefix_len];
-
- /* if corrected filename is still longer than standard MS-DOS filename
- size (8 characters + 1 dot + 3 characters file extension), shorten
- filename by writing file extension after 8th basename character */
- if (strlen(basename_corrected) > 8 + 1 + 3)
- {
- static char *msdos_filename = NULL;
-
- checked_free(msdos_filename);
-
- msdos_filename = getStringCopy(basename_corrected);
- strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
-
- basename_corrected = msdos_filename;
- }
- }
-#endif
-
- return basename_corrected;
+ return basename;
}
char *getCustomImageFilename(char *basename)
{
/* 1st try: look for special artwork in current level series directory */
directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
- if (fileExists(directory))
+ if (directoryExists(directory))
return directory;
free(directory);
{
/* 2nd try: look for special artwork configured in level series config */
directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
- if (fileExists(directory))
+ if (directoryExists(directory))
return directory;
free(directory);
{
/* 3rd try: look for special artwork in configured artwork directory */
directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
- if (fileExists(directory))
+ if (directoryExists(directory))
return directory;
free(directory);
/* 4th try: look for default artwork in new default artwork directory */
directory = getStringCopy(getDefaultMusicDir(MUS_DEFAULT_SUBDIR));
- if (fileExists(directory))
+ if (directoryExists(directory))
return directory;
free(directory);
/* 5th try: look for default artwork in old default artwork directory */
directory = getStringCopy(options.music_directory);
- if (fileExists(directory))
+ if (directoryExists(directory))
return directory;
return NULL; /* cannot find specified artwork file anywhere */
void InitUserLevelDirectory(char *level_subdir)
{
- if (!fileExists(getUserLevelDir(level_subdir)))
+ if (!directoryExists(getUserLevelDir(level_subdir)))
{
createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
for (i = 0; i < (depth + 1) * 3; i++)
printf(" ");
+ printf("'%s' / '%s'\n", node->identifier, node->name);
+
+ /*
+ // use for dumping artwork info tree
printf("subdir == '%s' ['%s', '%s'] [%d])\n",
node->subdir, node->fullpath, node->basepath, node->in_user_dir);
+ */
if (node->node_group != NULL)
dumpTreeInfo(node->node_group, depth + 1);
{
static char *user_game_data_dir = NULL;
+#if defined(PLATFORM_ANDROID)
+ if (user_game_data_dir == NULL)
+ user_game_data_dir = (char *)SDL_AndroidGetInternalStoragePath();
+#else
if (user_game_data_dir == NULL)
user_game_data_dir = getPath2(getPersonalDataDir(),
program.userdata_subdir);
+#endif
return user_game_data_dir;
}
char *userdata_dir_new = getUserGameDataDir(); /* do not free() this */
/* convert old Unix style game data directory to Mac OS X style, if needed */
- if (fileExists(userdata_dir_old) && !fileExists(userdata_dir_new))
+ if (directoryExists(userdata_dir_old) && !directoryExists(userdata_dir_new))
{
if (rename(userdata_dir_old, userdata_dir_new) != 0)
{
#endif
}
+static boolean posix_process_running_setgid()
+{
+#if defined(PLATFORM_UNIX)
+ return (getgid() != getegid());
+#else
+ return FALSE;
+#endif
+}
+
void createDirectory(char *dir, char *text, int permission_class)
{
/* leave "other" permissions in umask untouched, but ensure group parts
of USERDATA_DIR_MODE are not masked */
mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
- mode_t normal_umask = posix_umask(0);
+ mode_t last_umask = posix_umask(0);
mode_t group_umask = ~(dir_mode & S_IRWXG);
- posix_umask(normal_umask & group_umask);
+ int running_setgid = posix_process_running_setgid();
+
+ /* if we're setgid, protect files against "other" */
+ /* else keep umask(0) to make the dir world-writable */
- if (!fileExists(dir))
+ if (running_setgid)
+ posix_umask(last_umask & group_umask);
+ else
+ dir_mode |= MODE_W_ALL;
+
+ if (!directoryExists(dir))
if (posix_mkdir(dir, dir_mode) != 0)
- Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
+ Error(ERR_WARN, "cannot create %s directory '%s': %s",
+ text, dir, strerror(errno));
- posix_umask(normal_umask); /* reset normal umask */
+ if (permission_class == PERMS_PUBLIC && !running_setgid)
+ chmod(dir, dir_mode);
+
+ posix_umask(last_umask); /* restore previous umask */
}
void InitUserDataDirectory()
void SetFilePermissions(char *filename, int permission_class)
{
- chmod(filename, (permission_class == PERMS_PRIVATE ?
- FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
+ int running_setgid = posix_process_running_setgid();
+ int perms = (permission_class == PERMS_PRIVATE ?
+ FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC);
+
+ if (permission_class == PERMS_PUBLIC && !running_setgid)
+ perms |= MODE_W_ALL;
+
+ chmod(filename, perms);
}
char *getCookie(char *file_type)
return addListEntry(list->next, token, value);
}
+#if 0
#ifdef DEBUG
static void printSetupFileList(SetupFileList *list)
{
printSetupFileList(list->next);
}
#endif
+#endif
#ifdef DEBUG
DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
#define remove_hash_entry hashtable_remove
#endif
-static unsigned int get_hash_from_key(void *key)
+unsigned int get_hash_from_key(void *key)
{
/*
djb2
}
#if 1
+
+#if 1
+static boolean loadSetupFileData(void *setup_file_data, char *filename,
+ boolean top_recursion_level, boolean is_hash)
+{
+ static SetupFileHash *include_filename_hash = NULL;
+ char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
+ char *token, *value, *line_ptr;
+ void *insert_ptr = NULL;
+ boolean read_continued_line = FALSE;
+ File *file;
+ int line_nr = 0, token_count = 0, include_count = 0;
+
+#if CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING
+ token_value_separator_warning = FALSE;
+#endif
+
+#if CHECK_TOKEN__WARN_IF_ALREADY_EXISTS_IN_HASH
+ token_already_exists_warning = FALSE;
+#endif
+
+#if 0
+ Error(ERR_INFO, "===== opening file: '%s'", filename);
+#endif
+
+ if (!(file = openFile(filename, MODE_READ)))
+ {
+ Error(ERR_WARN, "cannot open configuration file '%s'", filename);
+
+ return FALSE;
+ }
+
+#if 0
+ Error(ERR_INFO, "===== reading file: '%s'", filename);
+#endif
+
+ /* use "insert pointer" to store list end for constant insertion complexity */
+ if (!is_hash)
+ insert_ptr = setup_file_data;
+
+ /* on top invocation, create hash to mark included files (to prevent loops) */
+ if (top_recursion_level)
+ include_filename_hash = newSetupFileHash();
+
+ /* mark this file as already included (to prevent including it again) */
+ setHashEntry(include_filename_hash, getBaseNamePtr(filename), "true");
+
+ while (!checkEndOfFile(file))
+ {
+ /* read next line of input file */
+ if (!getStringFromFile(file, line, MAX_LINE_LEN))
+ break;
+
+#if 0
+ Error(ERR_INFO, "got line: '%s'", line);
+#endif
+
+ /* check if line was completely read and is terminated by line break */
+ if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
+ line_nr++;
+
+ /* cut trailing line break (this can be newline and/or carriage return) */
+ for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
+ if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
+ *line_ptr = '\0';
+
+ /* copy raw input line for later use (mainly debugging output) */
+ strcpy(line_raw, line);
+
+ if (read_continued_line)
+ {
+#if 0
+ /* !!! ??? WHY ??? !!! */
+ /* cut leading whitespaces from input line */
+ for (line_ptr = line; *line_ptr; line_ptr++)
+ if (*line_ptr != ' ' && *line_ptr != '\t')
+ break;
+#endif
+
+ /* append new line to existing line, if there is enough space */
+ if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
+ strcat(previous_line, line_ptr);
+
+ strcpy(line, previous_line); /* copy storage buffer to line */
+
+ read_continued_line = FALSE;
+ }
+
+ /* if the last character is '\', continue at next line */
+ if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
+ {
+ line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
+ strcpy(previous_line, line); /* copy line to storage buffer */
+
+ read_continued_line = TRUE;
+
+ continue;
+ }
+
+ if (!getTokenValueFromSetupLineExt(line, &token, &value, filename,
+ line_raw, line_nr, FALSE))
+ continue;
+
+ if (*token)
+ {
+ if (strEqual(token, "include"))
+ {
+ if (getHashEntry(include_filename_hash, value) == NULL)
+ {
+ char *basepath = getBasePath(filename);
+ char *basename = getBaseName(value);
+ char *filename_include = getPath2(basepath, basename);
+
+#if 0
+ Error(ERR_INFO, "[including file '%s']", filename_include);
+#endif
+
+ loadSetupFileData(setup_file_data, filename_include, FALSE, is_hash);
+
+ free(basepath);
+ free(basename);
+ free(filename_include);
+
+ include_count++;
+ }
+ else
+ {
+ Error(ERR_WARN, "ignoring already processed file '%s'", value);
+ }
+ }
+ else
+ {
+ if (is_hash)
+ {
+#if CHECK_TOKEN__WARN_IF_ALREADY_EXISTS_IN_HASH
+ char *old_value =
+ getHashEntry((SetupFileHash *)setup_file_data, token);
+
+ if (old_value != NULL)
+ {
+ if (!token_already_exists_warning)
+ {
+ Error(ERR_INFO_LINE, "-");
+ Error(ERR_WARN, "duplicate token(s) found in config file:");
+ Error(ERR_INFO, "- config file: '%s'", filename);
+
+ token_already_exists_warning = TRUE;
+ }
+
+ Error(ERR_INFO, "- token: '%s' (in line %d)", token, line_nr);
+ Error(ERR_INFO, " old value: '%s'", old_value);
+ Error(ERR_INFO, " new value: '%s'", value);
+ }
+#endif
+
+ setHashEntry((SetupFileHash *)setup_file_data, token, value);
+ }
+ else
+ {
+ insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
+ }
+
+ token_count++;
+ }
+ }
+ }
+
+ closeFile(file);
+
+#if CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING
+ if (token_value_separator_warning)
+ Error(ERR_INFO_LINE, "-");
+#endif
+
+#if CHECK_TOKEN__WARN_IF_ALREADY_EXISTS_IN_HASH
+ if (token_already_exists_warning)
+ Error(ERR_INFO_LINE, "-");
+#endif
+
+ if (token_count == 0 && include_count == 0)
+ Error(ERR_WARN, "configuration file '%s' is empty", filename);
+
+ if (top_recursion_level)
+ freeSetupFileHash(include_filename_hash);
+
+ return TRUE;
+}
+
+#else
+
static boolean loadSetupFileData(void *setup_file_data, char *filename,
boolean top_recursion_level, boolean is_hash)
{
return TRUE;
}
+#endif
+
#else
static boolean loadSetupFileData(void *setup_file_data, char *filename,
ti->last_level = 0;
ti->level_group = FALSE;
ti->handicap_level = 0;
+#if 1
+ ti->readonly = parent->readonly;
+#else
ti->readonly = TRUE;
+#endif
ti->handicap = TRUE;
ti->skip_levels = FALSE;
}
return ti_copy;
}
-static void freeTreeInfo(TreeInfo *ti)
+void freeTreeInfo(TreeInfo *ti)
{
if (ti == NULL)
return;
checked_free(ti->special_flags);
}
+ // recursively free child node
+ if (ti->node_group)
+ freeTreeInfo(ti->node_group);
+
+ // recursively free next node
+ if (ti->next)
+ freeTreeInfo(ti->next);
+
checked_free(ti);
}
{
const TreeInfo *entry1 = *((TreeInfo **)object1);
const TreeInfo *entry2 = *((TreeInfo **)object2);
- int class_sorting1, class_sorting2;
+ int class_sorting1 = 0, class_sorting2 = 0;
int compare_result;
if (entry1->type == TREE_TYPE_LEVEL_DIR)
class_sorting1 = LEVELSORTING(entry1);
class_sorting2 = LEVELSORTING(entry2);
}
- else
+ else if (entry1->type == TREE_TYPE_GRAPHICS_DIR ||
+ entry1->type == TREE_TYPE_SOUNDS_DIR ||
+ entry1->type == TREE_TYPE_MUSIC_DIR)
{
class_sorting1 = ARTWORKSORTING(entry1);
class_sorting2 = ARTWORKSORTING(entry2);
char *directory_name)
{
#if 0
- static unsigned long progress_delay = 0;
- unsigned long progress_delay_value = 100; /* (in milliseconds) */
+ static unsigned int progress_delay = 0;
+ unsigned int progress_delay_value = 100; /* (in milliseconds) */
#endif
char *directory_path = getPath2(level_directory, directory_name);
char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
leveldir_new->in_user_dir =
(!strEqual(leveldir_new->basepath, options.level_directory));
+#if 0
+ printf("::: '%s' -> %d\n",
+ leveldir_new->identifier,
+ leveldir_new->in_user_dir);
+#endif
+
/* adjust some settings if user's private level directory was detected */
if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
leveldir_new->in_user_dir &&
return TRUE;
}
+#if 1
static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
TreeInfo *node_parent,
char *level_directory)
{
- DIR *dir;
- struct dirent *dir_entry;
+ Directory *dir;
+ DirectoryEntry *dir_entry;
boolean valid_entry_found = FALSE;
- if ((dir = opendir(level_directory)) == NULL)
+#if 0
+ Error(ERR_INFO, "looking for levels in '%s' ...", level_directory);
+#endif
+
+ if ((dir = openDirectory(level_directory)) == NULL)
{
Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
+
return;
}
- while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
+#if 0
+ Error(ERR_INFO, "opening '%s' succeeded ...", level_directory);
+#endif
+
+ while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
{
- struct stat file_status;
- char *directory_name = dir_entry->d_name;
+ char *directory_name = dir_entry->basename;
char *directory_path = getPath2(level_directory, directory_name);
+#if 0
+ Error(ERR_INFO, "checking entry '%s' ...", directory_name);
+#endif
+
/* skip entries for current and parent directory */
if (strEqual(directory_name, ".") ||
strEqual(directory_name, ".."))
{
free(directory_path);
+
continue;
}
+#if 1
/* find out if directory entry is itself a directory */
- if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
- (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
+ if (!dir_entry->is_directory) /* not a directory */
{
free(directory_path);
- continue;
- }
- free(directory_path);
+#if 0
+ Error(ERR_INFO, "* entry '%s' is not a directory ...", directory_name);
+#endif
+
+ continue;
+ }
+#else
+ /* find out if directory entry is itself a directory */
+ struct stat file_status;
+ if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
+ (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
+ {
+ free(directory_path);
+
+ continue;
+ }
+#endif
+
+ free(directory_path);
+
+ if (strEqual(directory_name, GRAPHICS_DIRECTORY) ||
+ strEqual(directory_name, SOUNDS_DIRECTORY) ||
+ strEqual(directory_name, MUSIC_DIRECTORY))
+ continue;
+
+ valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
+ level_directory,
+ directory_name);
+ }
+
+ closeDirectory(dir);
+
+ /* special case: top level directory may directly contain "levelinfo.conf" */
+ if (node_parent == NULL && !valid_entry_found)
+ {
+ /* check if this directory directly contains a file "levelinfo.conf" */
+ valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
+ level_directory, ".");
+ }
+
+ if (!valid_entry_found)
+ Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
+ level_directory);
+}
+
+#else
+
+static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
+ TreeInfo *node_parent,
+ char *level_directory)
+{
+ DIR *dir;
+ struct dirent *dir_entry;
+ boolean valid_entry_found = FALSE;
+
+#if 1
+ Error(ERR_INFO, "looking for levels in '%s' ...", level_directory);
+#endif
+
+ if ((dir = opendir(level_directory)) == NULL)
+ {
+ Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
+
+ return;
+ }
+
+#if 1
+ Error(ERR_INFO, "opening '%s' succeeded ...", level_directory);
+#endif
+
+ while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
+ {
+ struct stat file_status;
+ char *directory_name = dir_entry->d_name;
+ char *directory_path = getPath2(level_directory, directory_name);
+
+#if 1
+ Error(ERR_INFO, "checking entry '%s' ...", directory_name);
+#endif
+
+ /* skip entries for current and parent directory */
+ if (strEqual(directory_name, ".") ||
+ strEqual(directory_name, ".."))
+ {
+ free(directory_path);
+ continue;
+ }
+
+ /* find out if directory entry is itself a directory */
+ if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
+ (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
+ {
+ free(directory_path);
+ continue;
+ }
+
+ free(directory_path);
if (strEqual(directory_name, GRAPHICS_DIRECTORY) ||
strEqual(directory_name, SOUNDS_DIRECTORY) ||
Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
level_directory);
}
+#endif
boolean AdjustGraphicsForEMC()
{
#endif
}
+#if 1
+
+static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
+ TreeInfo *node_parent,
+ char *base_directory,
+ char *directory_name, int type)
+{
+ char *directory_path = getPath2(base_directory, directory_name);
+ char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
+ SetupFileHash *setup_file_hash = NULL;
+ TreeInfo *artwork_new = NULL;
+ int i;
+
+ if (fileExists(filename))
+ setup_file_hash = loadSetupFileHash(filename);
+
+ if (setup_file_hash == NULL) /* no config file -- look for artwork files */
+ {
+ Directory *dir;
+ DirectoryEntry *dir_entry;
+ boolean valid_file_found = FALSE;
+
+ if ((dir = openDirectory(directory_path)) != NULL)
+ {
+ while ((dir_entry = readDirectory(dir)) != NULL)
+ {
+ char *entry_name = dir_entry->basename;
+
+ if (FileIsArtworkType(entry_name, type))
+ {
+ valid_file_found = TRUE;
+
+ break;
+ }
+ }
+
+ closeDirectory(dir);
+ }
+
+ if (!valid_file_found)
+ {
+ if (!strEqual(directory_name, "."))
+ Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
+
+ free(directory_path);
+ free(filename);
+
+ return FALSE;
+ }
+ }
+
+ artwork_new = newTreeInfo();
+
+ if (node_parent)
+ setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
+ else
+ setTreeInfoToDefaults(artwork_new, type);
+
+ artwork_new->subdir = getStringCopy(directory_name);
+
+ if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
+ {
+#if 0
+ checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
+#endif
+
+ /* set all structure fields according to the token/value pairs */
+ ldi = *artwork_new;
+ for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
+ setSetupInfo(levelinfo_tokens, i,
+ getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
+ *artwork_new = ldi;
+
+ if (strEqual(artwork_new->name, ANONYMOUS_NAME))
+ setString(&artwork_new->name, artwork_new->subdir);
+
+ if (artwork_new->identifier == NULL)
+ artwork_new->identifier = getStringCopy(artwork_new->subdir);
+
+ if (artwork_new->name_sorting == NULL)
+ artwork_new->name_sorting = getStringCopy(artwork_new->name);
+ }
+
+ if (node_parent == NULL) /* top level group */
+ {
+ artwork_new->basepath = getStringCopy(base_directory);
+ artwork_new->fullpath = getStringCopy(artwork_new->subdir);
+ }
+ else /* sub level group */
+ {
+ artwork_new->basepath = getStringCopy(node_parent->basepath);
+ artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
+ }
+
+ artwork_new->in_user_dir =
+ (!strEqual(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)));
+
+ /* (may use ".sort_priority" from "setup_file_hash" above) */
+ artwork_new->color = ARTWORKCOLOR(artwork_new);
+
+ setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
+
+ if (setup_file_hash == NULL) /* (after determining ".user_defined") */
+ {
+ if (strEqual(artwork_new->subdir, "."))
+ {
+ if (artwork_new->user_defined)
+ {
+ setString(&artwork_new->identifier, "private");
+ artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
+ }
+ else
+ {
+ setString(&artwork_new->identifier, "classic");
+ artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
+ }
+
+ /* set to new values after changing ".sort_priority" */
+ artwork_new->color = ARTWORKCOLOR(artwork_new);
+
+ setString(&artwork_new->class_desc,
+ getLevelClassDescription(artwork_new));
+ }
+ else
+ {
+ setString(&artwork_new->identifier, artwork_new->subdir);
+ }
+
+ setString(&artwork_new->name, artwork_new->identifier);
+ setString(&artwork_new->name_sorting, artwork_new->name);
+ }
+
+#if 0
+ DrawInitText(artwork_new->name, 150, FC_YELLOW);
+#endif
+
+ pushTreeInfo(node_first, artwork_new);
+
+ freeSetupFileHash(setup_file_hash);
+
+ free(directory_path);
+ free(filename);
+
+ return TRUE;
+}
+
+#else
+
static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
TreeInfo *node_parent,
char *base_directory,
return TRUE;
}
+#endif
+
+#if 1
+
+static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
+ TreeInfo *node_parent,
+ char *base_directory, int type)
+{
+ Directory *dir;
+ DirectoryEntry *dir_entry;
+ boolean valid_entry_found = FALSE;
+
+ if ((dir = openDirectory(base_directory)) == NULL)
+ {
+ /* display error if directory is main "options.graphics_directory" etc. */
+ if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
+ Error(ERR_WARN, "cannot read directory '%s'", base_directory);
+
+ return;
+ }
+
+ while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
+ {
+ char *directory_name = dir_entry->basename;
+ char *directory_path = getPath2(base_directory, directory_name);
+
+ /* skip directory entries for current and parent directory */
+ if (strEqual(directory_name, ".") ||
+ strEqual(directory_name, ".."))
+ {
+ free(directory_path);
+
+ continue;
+ }
+
+#if 1
+ /* skip directory entries which are not a directory */
+ if (!dir_entry->is_directory) /* not a directory */
+ {
+ free(directory_path);
+
+ continue;
+ }
+#else
+ /* skip directory entries which are not a directory or are not accessible */
+ struct stat file_status;
+ if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
+ (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
+ {
+ free(directory_path);
+
+ continue;
+ }
+#endif
+
+ free(directory_path);
+
+ /* check if this directory contains artwork with or without config file */
+ valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
+ base_directory,
+ directory_name, type);
+ }
+
+ closeDirectory(dir);
+
+ /* check if this directory directly contains artwork itself */
+ valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
+ base_directory, ".",
+ type);
+ if (!valid_entry_found)
+ Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
+ base_directory);
+}
+
+#else
+
static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
TreeInfo *node_parent,
char *base_directory, int type)
base_directory);
}
+#endif
+
static TreeInfo *getDummyArtworkInfo(int type)
{
/* this is only needed when there is completely no artwork available */
LevelDirTree *level_node)
{
#if 0
- static unsigned long progress_delay = 0;
- unsigned long progress_delay_value = 100; /* (in milliseconds) */
+ static unsigned int progress_delay = 0;
+ unsigned int progress_delay_value = 100; /* (in milliseconds) */
#endif
int type = (*artwork_node)->type;
void LoadLevelArtworkInfo()
{
+ print_timestamp_init("LoadLevelArtworkInfo");
+
DrawInitText("Looking for custom level artwork", 120, FC_GREEN);
+ print_timestamp_time("DrawTimeText");
+
LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
+ print_timestamp_time("LoadArtworkInfoFromLevelInfo (gfx)");
LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
+ print_timestamp_time("LoadArtworkInfoFromLevelInfo (snd)");
LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
+ print_timestamp_time("LoadArtworkInfoFromLevelInfo (mus)");
SaveArtworkInfoCache();
+ print_timestamp_time("SaveArtworkInfoCache");
+
/* needed for reloading level artwork not known at ealier stage */
if (!strEqual(artwork.gfx_current_identifier, setup.graphics_set))
artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
}
+ print_timestamp_time("getTreeInfoFromIdentifier");
+
sortTreeInfo(&artwork.gfx_first);
sortTreeInfo(&artwork.snd_first);
sortTreeInfo(&artwork.mus_first);
+ print_timestamp_time("sortTreeInfo");
+
#if 0
dumpTreeInfo(artwork.gfx_first, 0);
dumpTreeInfo(artwork.snd_first, 0);
dumpTreeInfo(artwork.mus_first, 0);
#endif
+
+ print_timestamp_done("LoadLevelArtworkInfo");
}
static void SaveUserLevelInfo()
free(filename);
}
-void SaveLevelSetup_LastSeries()
+static void SaveLevelSetup_LastSeries_Ext(boolean deactivate_last_level_series)
{
/* ----------------------------------------------------------------------- */
/* ~/.<program>/levelsetup.conf */
/* ----------------------------------------------------------------------- */
+ // check if the current level directory structure is available at this point
+ if (leveldir_current == NULL)
+ return;
+
char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
char *level_subdir = leveldir_current->subdir;
FILE *file;
if (!(file = fopen(filename, MODE_WRITE)))
{
Error(ERR_WARN, "cannot write setup file '%s'", filename);
+
free(filename);
+
return;
}
fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
getCookie("LEVELSETUP")));
+
+ if (deactivate_last_level_series)
+ fprintf(file, "# %s\n# ", "the following level set may have caused a problem and was deactivated");
+
fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
level_subdir));
free(filename);
}
+void SaveLevelSetup_LastSeries()
+{
+ SaveLevelSetup_LastSeries_Ext(FALSE);
+}
+
+void SaveLevelSetup_LastSeries_Deactivate()
+{
+ SaveLevelSetup_LastSeries_Ext(TRUE);
+}
+
+#if 1
+
+static void checkSeriesInfo()
+{
+ static char *level_directory = NULL;
+ Directory *dir;
+#if 0
+ DirectoryEntry *dir_entry;
+#endif
+
+ /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
+
+ level_directory = getPath2((leveldir_current->in_user_dir ?
+ getUserLevelDir(NULL) :
+ options.level_directory),
+ leveldir_current->fullpath);
+
+ if ((dir = openDirectory(level_directory)) == NULL)
+ {
+ Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
+
+ return;
+ }
+
+#if 0
+ while ((dir_entry = readDirectory(dir)) != NULL) /* last directory entry */
+ {
+ if (strlen(dir_entry->basename) > 4 &&
+ dir_entry->basename[3] == '.' &&
+ strEqual(&dir_entry->basename[4], LEVELFILE_EXTENSION))
+ {
+ char levelnum_str[4];
+ int levelnum_value;
+
+ strncpy(levelnum_str, dir_entry->basename, 3);
+ levelnum_str[3] = '\0';
+
+ levelnum_value = atoi(levelnum_str);
+
+ if (levelnum_value < leveldir_current->first_level)
+ {
+ Error(ERR_WARN, "additional level %d found", levelnum_value);
+ leveldir_current->first_level = levelnum_value;
+ }
+ else if (levelnum_value > leveldir_current->last_level)
+ {
+ Error(ERR_WARN, "additional level %d found", levelnum_value);
+ leveldir_current->last_level = levelnum_value;
+ }
+ }
+ }
+#endif
+
+ closeDirectory(dir);
+}
+
+#else
+
static void checkSeriesInfo()
{
static char *level_directory = NULL;
DIR *dir;
+#if 0
struct dirent *dir_entry;
+#endif
/* check for more levels besides the 'levels' field of 'levelinfo.conf' */
if ((dir = opendir(level_directory)) == NULL)
{
Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
+
return;
}
+#if 0
while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
{
if (strlen(dir_entry->d_name) > 4 &&
levelnum_value = atoi(levelnum_str);
-#if 0
if (levelnum_value < leveldir_current->first_level)
{
Error(ERR_WARN, "additional level %d found", levelnum_value);
Error(ERR_WARN, "additional level %d found", levelnum_value);
leveldir_current->last_level = levelnum_value;
}
-#endif
}
}
+#endif
closedir(dir);
}
+#endif
+
void LoadLevelSetup_SeriesInfo()
{
char *filename;
SetupFileHash *level_setup_hash = NULL;
char *level_subdir = leveldir_current->subdir;
+ int i;
/* always start with reliable default values */
level_nr = leveldir_current->first_level;
+ for (i = 0; i < MAX_LEVELS; i++)
+ {
+ LevelStats_setPlayed(i, 0);
+ LevelStats_setSolved(i, 0);
+ }
+
checkSeriesInfo(leveldir_current);
/* ----------------------------------------------------------------------- */
{
char *token_value;
+ /* get last played level in this level set */
+
token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
if (token_value)
level_nr = leveldir_current->last_level;
}
+ /* get handicap level in this level set */
+
token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
if (token_value)
leveldir_current->handicap_level = level_nr;
}
+ /* get number of played and solved levels in this level set */
+
+ BEGIN_HASH_ITERATION(level_setup_hash, itr)
+ {
+ char *token = HASH_ITERATION_TOKEN(itr);
+ char *value = HASH_ITERATION_VALUE(itr);
+
+ if (strlen(token) == 3 &&
+ token[0] >= '0' && token[0] <= '9' &&
+ token[1] >= '0' && token[1] <= '9' &&
+ token[2] >= '0' && token[2] <= '9')
+ {
+ int level_nr = atoi(token);
+
+ if (value != NULL)
+ LevelStats_setPlayed(level_nr, atoi(value)); /* read 1st column */
+
+ value = strchr(value, ' ');
+
+ if (value != NULL)
+ LevelStats_setSolved(level_nr, atoi(value)); /* read 2nd column */
+ }
+ }
+ END_HASH_ITERATION(hash, itr)
+
checkSetupFileHashIdentifier(level_setup_hash, filename,
getCookie("LEVELSETUP"));
char *level_nr_str = int2str(level_nr, 0);
char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
FILE *file;
+ int i;
/* ----------------------------------------------------------------------- */
/* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
getCookie("LEVELSETUP")));
fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
level_nr_str));
- fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
- handicap_level_str));
+ fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
+ handicap_level_str));
+
+ for (i = leveldir_current->first_level; i <= leveldir_current->last_level;
+ i++)
+ {
+ if (LevelStats_getPlayed(i) > 0 ||
+ LevelStats_getSolved(i) > 0)
+ {
+ char token[16];
+ char value[16];
+
+ sprintf(token, "%03d", i);
+ sprintf(value, "%d %d", LevelStats_getPlayed(i), LevelStats_getSolved(i));
+
+ fprintf(file, "%s\n", getFormattedSetupEntry(token, value));
+ }
+ }
fclose(file);
free(filename);
}
+
+int LevelStats_getPlayed(int nr)
+{
+ return (nr >= 0 && nr < MAX_LEVELS ? level_stats[nr].played : 0);
+}
+
+int LevelStats_getSolved(int nr)
+{
+ return (nr >= 0 && nr < MAX_LEVELS ? level_stats[nr].solved : 0);
+}
+
+void LevelStats_setPlayed(int nr, int value)
+{
+ if (nr >= 0 && nr < MAX_LEVELS)
+ level_stats[nr].played = value;
+}
+
+void LevelStats_setSolved(int nr, int value)
+{
+ if (nr >= 0 && nr < MAX_LEVELS)
+ level_stats[nr].solved = value;
+}
+
+void LevelStats_incPlayed(int nr)
+{
+ if (nr >= 0 && nr < MAX_LEVELS)
+ level_stats[nr].played++;
+}
+
+void LevelStats_incSolved(int nr)
+{
+ if (nr >= 0 && nr < MAX_LEVELS)
+ level_stats[nr].solved++;
+}