X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Flibgame%2Fsetup.c;h=5cdaa87471a3c55f3b4dd5a5b18e94494fd5b27d;hp=5784ac496183c72cf174fecd4649a923f3648908;hb=9141e173a82b1105ed0f60c649bfc94dbc5a26b4;hpb=cb69ffb72a904eb0704c7f0e6c593bfee19562df diff --git a/src/libgame/setup.c b/src/libgame/setup.c index 5784ac49..5cdaa874 100644 --- a/src/libgame/setup.c +++ b/src/libgame/setup.c @@ -28,9 +28,11 @@ #include "text.h" #include "misc.h" #include "hash.h" +#include "zip/miniunz.h" -#define ENABLE_UNUSED_CODE FALSE /* for currently unused functions */ +#define ENABLE_UNUSED_CODE FALSE // for currently unused functions +#define DEBUG_NO_CONFIG_FILE FALSE // for extra-verbose debug output #define NUM_LEVELCLASS_DESC 8 @@ -100,9 +102,9 @@ static SetupFileHash *artworkinfo_cache_new = NULL; static boolean use_artworkinfo_cache = TRUE; -/* ------------------------------------------------------------------------- */ -/* file functions */ -/* ------------------------------------------------------------------------- */ +// ---------------------------------------------------------------------------- +// file functions +// ---------------------------------------------------------------------------- static char *getLevelClassDescription(TreeInfo *ti) { @@ -114,22 +116,6 @@ static char *getLevelClassDescription(TreeInfo *ti) return "Unknown Level Class"; } -static char *getUserLevelDir(char *level_subdir) -{ - static char *userlevel_dir = NULL; - char *data_dir = getUserGameDataDir(); - char *userlevel_subdir = LEVELS_DIRECTORY; - - checked_free(userlevel_dir); - - if (level_subdir != NULL) - userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir); - else - userlevel_dir = getPath2(data_dir, userlevel_subdir); - - return userlevel_dir; -} - static char *getScoreDir(char *level_subdir) { static char *score_dir = NULL; @@ -172,7 +158,7 @@ static char *getLevelSetupDir(char *level_subdir) return levelsetup_dir; } -static char *getCacheDir() +static char *getCacheDir(void) { static char *cache_dir = NULL; @@ -182,7 +168,17 @@ static char *getCacheDir() return cache_dir; } -static char *getLevelDirFromTreeInfo(TreeInfo *node) +static char *getNetworkDir(void) +{ + static char *network_dir = NULL; + + if (network_dir == NULL) + network_dir = getPath2(getUserGameDataDir(), NETWORK_DIRECTORY); + + return network_dir; +} + +char *getLevelDirFromTreeInfo(TreeInfo *node) { static char *level_dir = NULL; @@ -197,12 +193,44 @@ static char *getLevelDirFromTreeInfo(TreeInfo *node) return level_dir; } -char *getCurrentLevelDir() +char *getUserLevelDir(char *level_subdir) +{ + static char *userlevel_dir = NULL; + char *data_dir = getUserGameDataDir(); + char *userlevel_subdir = LEVELS_DIRECTORY; + + checked_free(userlevel_dir); + + if (level_subdir != NULL) + userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir); + else + userlevel_dir = getPath2(data_dir, userlevel_subdir); + + return userlevel_dir; +} + +char *getNetworkLevelDir(char *level_subdir) +{ + static char *network_level_dir = NULL; + char *data_dir = getNetworkDir(); + char *networklevel_subdir = LEVELS_DIRECTORY; + + checked_free(network_level_dir); + + if (level_subdir != NULL) + network_level_dir = getPath3(data_dir, networklevel_subdir, level_subdir); + else + network_level_dir = getPath2(data_dir, networklevel_subdir); + + return network_level_dir; +} + +char *getCurrentLevelDir(void) { return getLevelDirFromTreeInfo(leveldir_current); } -char *getNewUserLevelSubdir() +char *getNewUserLevelSubdir(void) { static char *new_level_subdir = NULL; char *subdir_prefix = getLoginName(); @@ -240,7 +268,7 @@ static char *getTapeDir(char *level_subdir) return tape_dir; } -static char *getSolutionTapeDir() +static char *getSolutionTapeDir(void) { static char *tape_dir = NULL; char *data_dir = getCurrentLevelDir(); @@ -312,7 +340,7 @@ static char *getClassicArtworkDir(int type) getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : ""); } -static char *getUserGraphicsDir() +char *getUserGraphicsDir(void) { static char *usergraphics_dir = NULL; @@ -322,7 +350,7 @@ static char *getUserGraphicsDir() return usergraphics_dir; } -static char *getUserSoundsDir() +char *getUserSoundsDir(void) { static char *usersounds_dir = NULL; @@ -332,7 +360,7 @@ static char *getUserSoundsDir() return usersounds_dir; } -static char *getUserMusicDir() +char *getUserMusicDir(void) { static char *usermusic_dir = NULL; @@ -409,7 +437,7 @@ char *setLevelArtworkDir(TreeInfo *ti) return *artwork_set_ptr; } -inline static char *getLevelArtworkSet(int type) +static char *getLevelArtworkSet(int type) { if (leveldir_current == NULL) return NULL; @@ -417,7 +445,7 @@ inline static char *getLevelArtworkSet(int type) return LEVELDIR_ARTWORK_SET(leveldir_current, type); } -inline static char *getLevelArtworkDir(int type) +static char *getLevelArtworkDir(int type) { if (leveldir_current == NULL) return UNDEFINED_FILENAME; @@ -425,27 +453,93 @@ inline static char *getLevelArtworkDir(int type) return LEVELDIR_ARTWORK_PATH(leveldir_current, type); } -char *getProgramConfigFilename(char *command_filename_ptr) +char *getProgramMainDataPath(char *command_filename, char *base_path) +{ + // check if the program's main data base directory is configured + if (!strEqual(base_path, ".")) + return getStringCopy(base_path); + + /* if the program is configured to start from current directory (default), + determine program package directory from program binary (some versions + of KDE/Konqueror and Mac OS X (especially "Mavericks") apparently do not + set the current working directory to the program package directory) */ + char *main_data_path = getBasePath(command_filename); + +#if defined(PLATFORM_MACOSX) + if (strSuffix(main_data_path, MAC_APP_BINARY_SUBDIR)) + { + char *main_data_path_old = main_data_path; + + // cut relative path to Mac OS X application binary directory from path + main_data_path[strlen(main_data_path) - + strlen(MAC_APP_BINARY_SUBDIR)] = '\0'; + + // cut trailing path separator from path (but not if path is root directory) + if (strSuffix(main_data_path, "/") && !strEqual(main_data_path, "/")) + main_data_path[strlen(main_data_path) - 1] = '\0'; + + // replace empty path with current directory + if (strEqual(main_data_path, "")) + main_data_path = "."; + + // add relative path to Mac OS X application resources directory to path + main_data_path = getPath2(main_data_path, MAC_APP_FILES_SUBDIR); + + free(main_data_path_old); + } +#endif + + return main_data_path; +} + +char *getProgramConfigFilename(char *command_filename) { - char *command_filename_1 = getStringCopy(command_filename_ptr); + static char *config_filename_1 = NULL; + static char *config_filename_2 = NULL; + static char *config_filename_3 = NULL; + static boolean initialized = FALSE; + + if (!initialized) + { + char *command_filename_1 = getStringCopy(command_filename); + + // strip trailing executable suffix from 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); - // strip trailing executable suffix from command filename - if (strSuffix(command_filename_1, ".exe")) - command_filename_1[strlen(command_filename_1) - 4] = '\0'; + char *command_basepath = getBasePath(command_filename); + char *command_basename = getBaseNameNoSuffix(command_filename); + char *command_filename_2 = getPath2(command_basepath, command_basename); - char *command_basepath = getBasePath(command_filename_ptr); - char *command_basename = getBaseNameNoSuffix(command_filename_ptr); - char *command_filename_2 = getPath2(command_basepath, command_basename); + config_filename_1 = getStringCat2(command_filename_1, ".conf"); + config_filename_2 = getStringCat2(command_filename_2, ".conf"); + config_filename_3 = getPath2(conf_directory, SETUP_FILENAME); - char *config_filename_1 = getStringCat2(command_filename_1, ".conf"); - char *config_filename_2 = getStringCat2(command_filename_2, ".conf"); + checked_free(ro_base_path); + checked_free(conf_directory); + + checked_free(command_basepath); + checked_free(command_basename); + + checked_free(command_filename_1); + checked_free(command_filename_2); + + initialized = TRUE; + } // 1st try: look for config file that exactly matches the binary filename if (fileExists(config_filename_1)) return config_filename_1; - // 2nd try: return config filename that matches binary filename without suffix - return config_filename_2; + // 2nd try: look for config file that matches binary filename without suffix + if (fileExists(config_filename_2)) + return config_filename_2; + + // 3rd try: return setup config filename in global program config directory + return config_filename_3; } char *getTapeFilename(int nr) @@ -495,12 +589,14 @@ char *getScoreFilename(int nr) checked_free(filename); sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION); - filename = getPath2(getScoreDir(leveldir_current->subdir), basename); + + // used instead of "leveldir_current->subdir" (for network games) + filename = getPath2(getScoreDir(levelset.identifier), basename); return filename; } -char *getSetupFilename() +char *getSetupFilename(void) { static char *filename = NULL; @@ -511,12 +607,12 @@ char *getSetupFilename() return filename; } -char *getDefaultSetupFilename() +char *getDefaultSetupFilename(void) { return program.config_filename; } -char *getEditorSetupFilename() +char *getEditorSetupFilename(void) { static char *filename = NULL; @@ -532,7 +628,7 @@ char *getEditorSetupFilename() return filename; } -char *getHelpAnimFilename() +char *getHelpAnimFilename(void) { static char *filename = NULL; @@ -543,7 +639,7 @@ char *getHelpAnimFilename() return filename; } -char *getHelpTextFilename() +char *getHelpTextFilename(void) { static char *filename = NULL; @@ -554,7 +650,7 @@ char *getHelpTextFilename() return filename; } -char *getLevelSetInfoFilename() +char *getLevelSetInfoFilename(void) { static char *filename = NULL; char *basenames[] = @@ -583,7 +679,7 @@ char *getLevelSetInfoFilename() return NULL; } -char *getLevelSetTitleMessageBasename(int nr, boolean initial) +static char *getLevelSetTitleMessageBasename(int nr, boolean initial) { static char basename[32]; @@ -605,38 +701,38 @@ char *getLevelSetTitleMessageFilename(int nr, boolean initial) if (!gfx.override_level_graphics) { - /* 1st try: look for special artwork in current level series directory */ + // 1st try: look for special artwork in current level series directory filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename); if (fileExists(filename)) return filename; free(filename); - /* 2nd try: look for message file in current level set directory */ + // 2nd try: look for message file in current level set directory filename = getPath2(getCurrentLevelDir(), basename); if (fileExists(filename)) return filename; free(filename); - /* check if there is special artwork configured in level series config */ + // check if there is special artwork configured in level series config if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL) { - /* 3rd try: look for special artwork configured in level series config */ + // 3rd try: look for special artwork configured in level series config filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename); if (fileExists(filename)) return filename; free(filename); - /* take missing artwork configured in level set config from default */ + // take missing artwork configured in level set config from default skip_setup_artwork = TRUE; } } if (!skip_setup_artwork) { - /* 4th try: look for special artwork in configured artwork directory */ + // 4th try: look for special artwork in configured artwork directory filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename); if (fileExists(filename)) return filename; @@ -644,19 +740,19 @@ char *getLevelSetTitleMessageFilename(int nr, boolean initial) free(filename); } - /* 5th try: look for default artwork in new default artwork directory */ + // 5th try: look for default artwork in new default artwork directory filename = getPath2(getDefaultGraphicsDir(GFX_DEFAULT_SUBDIR), basename); if (fileExists(filename)) return filename; free(filename); - /* 6th try: look for default artwork in old default artwork directory */ + // 6th try: look for default artwork in old default artwork directory filename = getPath2(options.graphics_directory, basename); if (fileExists(filename)) return filename; - return NULL; /* cannot find specified artwork file anywhere */ + return NULL; // cannot find specified artwork file anywhere } static char *getCorrectedArtworkBasename(char *basename) @@ -675,31 +771,31 @@ char *getCustomImageFilename(char *basename) if (!gfx.override_level_graphics) { - /* 1st try: look for special artwork in current level series directory */ + // 1st try: look for special artwork in current level series directory filename = getImg3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename); if (fileExists(filename)) return filename; free(filename); - /* check if there is special artwork configured in level series config */ + // check if there is special artwork configured in level series config if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL) { - /* 2nd try: look for special artwork configured in level series config */ + // 2nd try: look for special artwork configured in level series config filename = getImg2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename); if (fileExists(filename)) return filename; free(filename); - /* take missing artwork configured in level set config from default */ + // take missing artwork configured in level set config from default skip_setup_artwork = TRUE; } } if (!skip_setup_artwork) { - /* 3rd try: look for special artwork in configured artwork directory */ + // 3rd try: look for special artwork in configured artwork directory filename = getImg2(getSetupArtworkDir(artwork.gfx_current), basename); if (fileExists(filename)) return filename; @@ -707,14 +803,14 @@ char *getCustomImageFilename(char *basename) free(filename); } - /* 4th try: look for default artwork in new default artwork directory */ + // 4th try: look for default artwork in new default artwork directory filename = getImg2(getDefaultGraphicsDir(GFX_DEFAULT_SUBDIR), basename); if (fileExists(filename)) return filename; free(filename); - /* 5th try: look for default artwork in old default artwork directory */ + // 5th try: look for default artwork in old default artwork directory filename = getImg2(options.graphics_directory, basename); if (fileExists(filename)) return filename; @@ -727,14 +823,14 @@ char *getCustomImageFilename(char *basename) Error(ERR_WARN, "cannot find artwork file '%s' (using fallback)", basename); - /* 6th try: look for fallback artwork in old default artwork directory */ - /* (needed to prevent errors when trying to access unused artwork files) */ + // 6th try: look for fallback artwork in old default artwork directory + // (needed to prevent errors when trying to access unused artwork files) filename = getImg2(options.graphics_directory, GFX_FALLBACK_FILENAME); if (fileExists(filename)) return filename; } - return NULL; /* cannot find specified artwork file anywhere */ + return NULL; // cannot find specified artwork file anywhere } char *getCustomSoundFilename(char *basename) @@ -748,31 +844,31 @@ char *getCustomSoundFilename(char *basename) if (!gfx.override_level_sounds) { - /* 1st try: look for special artwork in current level series directory */ + // 1st try: look for special artwork in current level series directory filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename); if (fileExists(filename)) return filename; free(filename); - /* check if there is special artwork configured in level series config */ + // check if there is special artwork configured in level series config if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL) { - /* 2nd try: look for special artwork configured in level series config */ + // 2nd try: look for special artwork configured in level series config filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename); if (fileExists(filename)) return filename; free(filename); - /* take missing artwork configured in level set config from default */ + // take missing artwork configured in level set config from default skip_setup_artwork = TRUE; } } if (!skip_setup_artwork) { - /* 3rd try: look for special artwork in configured artwork directory */ + // 3rd try: look for special artwork in configured artwork directory filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename); if (fileExists(filename)) return filename; @@ -780,14 +876,14 @@ char *getCustomSoundFilename(char *basename) free(filename); } - /* 4th try: look for default artwork in new default artwork directory */ + // 4th try: look for default artwork in new default artwork directory filename = getPath2(getDefaultSoundsDir(SND_DEFAULT_SUBDIR), basename); if (fileExists(filename)) return filename; free(filename); - /* 5th try: look for default artwork in old default artwork directory */ + // 5th try: look for default artwork in old default artwork directory filename = getPath2(options.sounds_directory, basename); if (fileExists(filename)) return filename; @@ -800,14 +896,14 @@ char *getCustomSoundFilename(char *basename) Error(ERR_WARN, "cannot find artwork file '%s' (using fallback)", basename); - /* 6th try: look for fallback artwork in old default artwork directory */ - /* (needed to prevent errors when trying to access unused artwork files) */ + // 6th try: look for fallback artwork in old default artwork directory + // (needed to prevent errors when trying to access unused artwork files) filename = getPath2(options.sounds_directory, SND_FALLBACK_FILENAME); if (fileExists(filename)) return filename; } - return NULL; /* cannot find specified artwork file anywhere */ + return NULL; // cannot find specified artwork file anywhere } char *getCustomMusicFilename(char *basename) @@ -821,31 +917,31 @@ char *getCustomMusicFilename(char *basename) if (!gfx.override_level_music) { - /* 1st try: look for special artwork in current level series directory */ + // 1st try: look for special artwork in current level series directory filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename); if (fileExists(filename)) return filename; free(filename); - /* check if there is special artwork configured in level series config */ + // check if there is special artwork configured in level series config if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL) { - /* 2nd try: look for special artwork configured in level series config */ + // 2nd try: look for special artwork configured in level series config filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename); if (fileExists(filename)) return filename; free(filename); - /* take missing artwork configured in level set config from default */ + // take missing artwork configured in level set config from default skip_setup_artwork = TRUE; } } if (!skip_setup_artwork) { - /* 3rd try: look for special artwork in configured artwork directory */ + // 3rd try: look for special artwork in configured artwork directory filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename); if (fileExists(filename)) return filename; @@ -853,14 +949,14 @@ char *getCustomMusicFilename(char *basename) free(filename); } - /* 4th try: look for default artwork in new default artwork directory */ + // 4th try: look for default artwork in new default artwork directory filename = getPath2(getDefaultMusicDir(MUS_DEFAULT_SUBDIR), basename); if (fileExists(filename)) return filename; free(filename); - /* 5th try: look for default artwork in old default artwork directory */ + // 5th try: look for default artwork in old default artwork directory filename = getPath2(options.music_directory, basename); if (fileExists(filename)) return filename; @@ -873,14 +969,14 @@ char *getCustomMusicFilename(char *basename) Error(ERR_WARN, "cannot find artwork file '%s' (using fallback)", basename); - /* 6th try: look for fallback artwork in old default artwork directory */ - /* (needed to prevent errors when trying to access unused artwork files) */ + // 6th try: look for fallback artwork in old default artwork directory + // (needed to prevent errors when trying to access unused artwork files) filename = getPath2(options.music_directory, MUS_FALLBACK_FILENAME); if (fileExists(filename)) return filename; } - return NULL; /* cannot find specified artwork file anywhere */ + return NULL; // cannot find specified artwork file anywhere } char *getCustomArtworkFilename(char *basename, int type) @@ -920,31 +1016,31 @@ char *getCustomMusicDirectory(void) if (!gfx.override_level_music) { - /* 1st try: look for special artwork in current level series directory */ + // 1st try: look for special artwork in current level series directory directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY); if (directoryExists(directory)) return directory; free(directory); - /* check if there is special artwork configured in level series config */ + // check if there is special artwork configured in level series config if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL) { - /* 2nd try: look for special artwork configured in level series config */ + // 2nd try: look for special artwork configured in level series config directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR)); if (directoryExists(directory)) return directory; free(directory); - /* take missing artwork configured in level set config from default */ + // take missing artwork configured in level set config from default skip_setup_artwork = TRUE; } } if (!skip_setup_artwork) { - /* 3rd try: look for special artwork in configured artwork directory */ + // 3rd try: look for special artwork in configured artwork directory directory = getStringCopy(getSetupArtworkDir(artwork.mus_current)); if (directoryExists(directory)) return directory; @@ -952,19 +1048,19 @@ char *getCustomMusicDirectory(void) free(directory); } - /* 4th try: look for default artwork in new default artwork directory */ + // 4th try: look for default artwork in new default artwork directory directory = getStringCopy(getDefaultMusicDir(MUS_DEFAULT_SUBDIR)); if (directoryExists(directory)) return directory; free(directory); - /* 5th try: look for default artwork in old default artwork directory */ + // 5th try: look for default artwork in old default artwork directory directory = getStringCopy(options.music_directory); if (directoryExists(directory)) return directory; - return NULL; /* cannot find specified artwork file anywhere */ + return NULL; // cannot find specified artwork file anywhere } void InitTapeDirectory(char *level_subdir) @@ -987,7 +1083,7 @@ void InitScoreDirectory(char *level_subdir) createDirectory(getScoreDir(level_subdir), "level score", permissions); } -static void SaveUserLevelInfo(); +static void SaveUserLevelInfo(void); void InitUserLevelDirectory(char *level_subdir) { @@ -1001,6 +1097,17 @@ void InitUserLevelDirectory(char *level_subdir) } } +void InitNetworkLevelDirectory(char *level_subdir) +{ + if (!directoryExists(getNetworkLevelDir(level_subdir))) + { + createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE); + createDirectory(getNetworkDir(), "network data", PERMS_PRIVATE); + createDirectory(getNetworkLevelDir(NULL), "main network level", PERMS_PRIVATE); + createDirectory(getNetworkLevelDir(level_subdir), "network level", PERMS_PRIVATE); + } +} + void InitLevelSetupDirectory(char *level_subdir) { createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE); @@ -1008,18 +1115,18 @@ void InitLevelSetupDirectory(char *level_subdir) createDirectory(getLevelSetupDir(level_subdir), "level setup", PERMS_PRIVATE); } -void InitCacheDirectory() +static void InitCacheDirectory(void) { createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE); createDirectory(getCacheDir(), "cache data", PERMS_PRIVATE); } -/* ------------------------------------------------------------------------- */ -/* some functions to handle lists of level and artwork directories */ -/* ------------------------------------------------------------------------- */ +// ---------------------------------------------------------------------------- +// some functions to handle lists of level and artwork directories +// ---------------------------------------------------------------------------- -TreeInfo *newTreeInfo() +TreeInfo *newTreeInfo(void) { return checked_calloc(sizeof(TreeInfo)); } @@ -1062,16 +1169,16 @@ TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node) if (node == NULL) return NULL; - if (node->node_group) /* enter level group (step down into tree) */ + if (node->node_group) // enter level group (step down into tree) return getFirstValidTreeInfoEntry(node->node_group); - else if (node->parent_link) /* skip start entry of level group */ + else if (node->parent_link) // skip start entry of level group { - if (node->next) /* get first real level series entry */ + if (node->next) // get first real level series entry return getFirstValidTreeInfoEntry(node->next); - else /* leave empty level group and go on */ + else // leave empty level group and go on return getFirstValidTreeInfoEntry(node->node_parent->next); } - else /* this seems to be a regular level series */ + else // this seems to be a regular level series return node; } @@ -1080,9 +1187,9 @@ TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node) if (node == NULL) return NULL; - if (node->node_parent == NULL) /* top level group */ + if (node->node_parent == NULL) // top level group return *node->node_top; - else /* sub level group */ + else // sub level group return node->node_parent->node_group; } @@ -1153,8 +1260,8 @@ TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier) return NULL; } -TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent, - TreeInfo *node, boolean skip_sets_without_levels) +static TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent, + TreeInfo *node, boolean skip_sets_without_levels) { TreeInfo *node_new; @@ -1166,10 +1273,10 @@ TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent, return cloneTreeNode(node_top, node_parent, node->next, skip_sets_without_levels); - node_new = getTreeInfoCopy(node); /* copy complete node */ + node_new = getTreeInfoCopy(node); // copy complete node - node_new->node_top = node_top; /* correct top node link */ - node_new->node_parent = node_parent; /* correct parent node link */ + node_new->node_top = node_top; // correct top node link + node_new->node_parent = node_parent; // correct parent node link if (node->level_group) node_new->node_group = cloneTreeNode(node_top, node_new, node->node_group, @@ -1181,7 +1288,7 @@ TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent, return node_new; } -void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets) +static void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets) { TreeInfo *ti_cloned = cloneTreeNode(ti_new, NULL, ti, skip_empty_sets); @@ -1254,11 +1361,11 @@ void sortTreeInfoBySortFunction(TreeInfo **node_first, if (num_nodes == 0) return; - /* allocate array for sorting structure pointers */ + // allocate array for sorting structure pointers sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *)); - /* writing structure pointers to sorting array */ - while (i < num_nodes && node) /* double boundary check... */ + // writing structure pointers to sorting array + while (i < num_nodes && node) // double boundary check... { sort_array[i] = node; @@ -1266,21 +1373,21 @@ void sortTreeInfoBySortFunction(TreeInfo **node_first, node = node->next; } - /* sorting the structure pointers in the sorting array */ + // sorting the structure pointers in the sorting array qsort(sort_array, num_nodes, sizeof(TreeInfo *), compare_function); - /* update the linkage of list elements with the sorted node array */ + // update the linkage of list elements with the sorted node array for (i = 0; i < num_nodes - 1; i++) sort_array[i]->next = sort_array[i + 1]; sort_array[num_nodes - 1]->next = NULL; - /* update the linkage of the main list anchor pointer */ + // update the linkage of the main list anchor pointer *node_first = sort_array[0]; free(sort_array); - /* now recursively sort the level group structures */ + // now recursively sort the level group structures node = *node_first; while (node) { @@ -1297,9 +1404,9 @@ void sortTreeInfo(TreeInfo **node_first) } -/* ========================================================================= */ -/* some stuff from "files.c" */ -/* ========================================================================= */ +// ============================================================================ +// some stuff from "files.c" +// ============================================================================ #if defined(PLATFORM_WIN32) #ifndef S_IRGRP @@ -1326,9 +1433,9 @@ void sortTreeInfo(TreeInfo **node_first) #ifndef S_ISGID #define S_ISGID 0 #endif -#endif /* PLATFORM_WIN32 */ +#endif // PLATFORM_WIN32 -/* file permissions for newly written files */ +// 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) @@ -1346,7 +1453,7 @@ void sortTreeInfo(TreeInfo **node_first) #define FILE_PERMS_PUBLIC_ALL (MODE_R_ALL | MODE_W_ALL) -char *getHomeDir() +char *getHomeDir(void) { static char *dir = NULL; @@ -1388,7 +1495,7 @@ char *getCommonDataDir(void) char *dir = checked_malloc(MAX_PATH + 1); if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir)) - && !strEqual(dir, "")) /* empty for Windows 95/98 */ + && !strEqual(dir, "")) // empty for Windows 95/98 common_data_dir = getPath2(dir, program.userdata_subdir); else common_data_dir = options.rw_base_directory; @@ -1435,7 +1542,7 @@ char *getUserGameDataDir(void) return user_game_data_dir; } -char *getSetupDir() +char *getSetupDir(void) { return getUserGameDataDir(); } @@ -1458,7 +1565,7 @@ static int posix_mkdir(const char *pathname, mode_t mode) #endif } -static boolean posix_process_running_setgid() +static boolean posix_process_running_setgid(void) { #if defined(PLATFORM_UNIX) return (getgid() != getegid()); @@ -1472,8 +1579,8 @@ void createDirectory(char *dir, char *text, int permission_class) if (directoryExists(dir)) return; - /* leave "other" permissions in umask untouched, but ensure group parts - of USERDATA_DIR_MODE are not masked */ + // 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 last_umask = posix_umask(0); @@ -1482,8 +1589,8 @@ void createDirectory(char *dir, char *text, int permission_class) if (permission_class == PERMS_PUBLIC) { - /* if we're setgid, protect files against "other" */ - /* else keep umask(0) to make the dir world-writable */ + // if we're setgid, protect files against "other" + // else keep umask(0) to make the dir world-writable if (running_setgid) posix_umask(last_umask & group_umask); @@ -1498,10 +1605,10 @@ void createDirectory(char *dir, char *text, int permission_class) if (permission_class == PERMS_PUBLIC && !running_setgid) chmod(dir, dir_mode); - posix_umask(last_umask); /* restore previous umask */ + posix_umask(last_umask); // restore previous umask } -void InitUserDataDirectory() +void InitUserDataDirectory(void) { createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE); } @@ -1524,11 +1631,11 @@ char *getCookie(char *file_type) if (strlen(program.cookie_prefix) + 1 + strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN) - return "[COOKIE ERROR]"; /* should never happen */ + return "[COOKIE ERROR]"; // should never happen sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d", program.cookie_prefix, file_type, - program.version_major, program.version_minor); + program.version_super, program.version_major); return cookie; } @@ -1553,7 +1660,7 @@ int getFileVersionFromCookieString(const char *cookie) const int len_pattern1 = strlen(pattern1); const int len_pattern2 = strlen(pattern2); const int len_pattern = len_pattern1 + len_pattern2; - int version_major, version_minor; + int version_super, version_major; if (len_cookie <= len_pattern) return -1; @@ -1569,10 +1676,10 @@ int getFileVersionFromCookieString(const char *cookie) ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9') return -1; - version_major = ptr_cookie2[0] - '0'; - version_minor = ptr_cookie2[2] - '0'; + version_super = ptr_cookie2[0] - '0'; + version_major = ptr_cookie2[2] - '0'; - return VERSION_IDENT(version_major, version_minor, 0, 0); + return VERSION_IDENT(version_super, version_major, 0, 0); } boolean checkCookieString(const char *cookie, const char *template) @@ -1592,25 +1699,25 @@ boolean checkCookieString(const char *cookie, const char *template) } -/* ------------------------------------------------------------------------- */ -/* setup file list and hash handling functions */ -/* ------------------------------------------------------------------------- */ +// ---------------------------------------------------------------------------- +// setup file list and hash handling functions +// ---------------------------------------------------------------------------- char *getFormattedSetupEntry(char *token, char *value) { int i; static char entry[MAX_LINE_LEN]; - /* if value is an empty string, just return token without value */ + // if value is an empty string, just return token without value if (*value == '\0') return token; - /* start with the token and some spaces to format output line */ + // start with the token and some spaces to format output line sprintf(entry, "%s:", token); for (i = strlen(entry); i < token_value_position; i++) strcat(entry, " "); - /* continue with the token's value */ + // continue with the token's value strcat(entry, value); return entry; @@ -1735,7 +1842,7 @@ unsigned int get_hash_from_key(void *key) int c; while ((c = *str++)) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + hash = ((hash << 5) + hash) + c; // hash * 33 + c return hash; } @@ -1745,7 +1852,7 @@ static int keys_are_equal(void *key1, void *key2) return (strEqual((char *)key1, (char *)key2)); } -SetupFileHash *newSetupFileHash() +SetupFileHash *newSetupFileHash(void) { SetupFileHash *new_hash = create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal); @@ -1761,7 +1868,7 @@ void freeSetupFileHash(SetupFileHash *hash) if (hash == NULL) return; - hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */ + hashtable_destroy(hash, 1); // 1 == also free values stored in hash } char *getHashEntry(SetupFileHash *hash, char *token) @@ -1781,7 +1888,7 @@ void setHashEntry(SetupFileHash *hash, char *token, char *value) value_copy = getStringCopy(value); - /* change value; if it does not exist, insert it as new */ + // change value; if it does not exist, insert it as new if (!change_hash_entry(hash, token, value_copy)) if (!insert_hash_entry(hash, getStringCopy(token), value_copy)) Error(ERR_EXIT, "cannot insert into hash -- aborting"); @@ -1830,7 +1937,7 @@ static boolean getTokenValueFromSetupLineExt(char *line, static char line_copy[MAX_LINE_LEN + 1], line_raw_copy[MAX_LINE_LEN + 1]; char *token, *value, *line_ptr; - /* when externally invoked via ReadTokenValueFromLine(), copy line buffers */ + // when externally invoked via ReadTokenValueFromLine(), copy line buffers if (line_raw == NULL) { strncpy(line_copy, line, MAX_LINE_LEN); @@ -1841,7 +1948,7 @@ static boolean getTokenValueFromSetupLineExt(char *line, line_raw = line_raw_copy; } - /* cut trailing comment from input line */ + // cut trailing comment from input line for (line_ptr = line; *line_ptr; line_ptr++) { if (*line_ptr == '#') @@ -1851,33 +1958,33 @@ static boolean getTokenValueFromSetupLineExt(char *line, } } - /* cut trailing whitespaces from input line */ + // cut trailing whitespaces from input line for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--) if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0') *line_ptr = '\0'; - /* ignore empty lines */ + // ignore empty lines if (*line == '\0') return FALSE; - /* cut leading whitespaces from token */ + // cut leading whitespaces from token for (token = line; *token; token++) if (*token != ' ' && *token != '\t') break; - /* start with empty value as reliable default */ + // start with empty value as reliable default value = ""; token_value_separator_found = FALSE; - /* find end of token to determine start of value */ + // find end of token to determine start of value for (line_ptr = token; *line_ptr; line_ptr++) { - /* first look for an explicit token/value separator, like ':' or '=' */ + // first look for an explicit token/value separator, like ':' or '=' if (*line_ptr == ':' || *line_ptr == '=') { - *line_ptr = '\0'; /* terminate token string */ - value = line_ptr + 1; /* set beginning of value */ + *line_ptr = '\0'; // terminate token string + value = line_ptr + 1; // set beginning of value token_value_separator_found = TRUE; @@ -1886,15 +1993,15 @@ static boolean getTokenValueFromSetupLineExt(char *line, } #if ALLOW_TOKEN_VALUE_SEPARATOR_BEING_WHITESPACE - /* fallback: if no token/value separator found, also allow whitespaces */ + // fallback: if no token/value separator found, also allow whitespaces if (!token_value_separator_found && !separator_required) { for (line_ptr = token; *line_ptr; line_ptr++) { if (*line_ptr == ' ' || *line_ptr == '\t') { - *line_ptr = '\0'; /* terminate token string */ - value = line_ptr + 1; /* set beginning of value */ + *line_ptr = '\0'; // terminate token string + value = line_ptr + 1; // set beginning of value token_value_separator_found = TRUE; @@ -1931,12 +2038,12 @@ static boolean getTokenValueFromSetupLineExt(char *line, } #endif - /* cut trailing whitespaces from token */ + // cut trailing whitespaces from token for (line_ptr = &token[strlen(token)]; line_ptr >= token; line_ptr--) if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0') *line_ptr = '\0'; - /* cut leading whitespaces from value */ + // cut leading whitespaces from value for (; *value; value++) if (*value != ' ' && *value != '\t') break; @@ -1949,9 +2056,9 @@ static boolean getTokenValueFromSetupLineExt(char *line, boolean getTokenValueFromSetupLine(char *line, char **token, char **value) { - /* while the internal (old) interface does not require a token/value - separator (for downwards compatibility with existing files which - don't use them), it is mandatory for the external (new) interface */ + // while the internal (old) interface does not require a token/value + // separator (for downwards compatibility with existing files which + // don't use them), it is mandatory for the external (new) interface return getTokenValueFromSetupLineExt(line, token, value, NULL, NULL, 0, TRUE); } @@ -1977,56 +2084,58 @@ static boolean loadSetupFileData(void *setup_file_data, char *filename, if (!(file = openFile(filename, MODE_READ))) { +#if DEBUG_NO_CONFIG_FILE Error(ERR_DEBUG, "cannot open configuration file '%s'", filename); +#endif return FALSE; } - /* use "insert pointer" to store list end for constant insertion complexity */ + // 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) */ + // 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) */ + // 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 */ + // read next line of input file if (!getStringFromFile(file, line, MAX_LINE_LEN)) break; - /* check if line was completely read and is terminated by line break */ + // 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) */ + // 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) */ + // copy raw input line for later use (mainly debugging output) strcpy(line_raw, line); if (read_continued_line) { - /* append new line to existing line, if there is enough space */ + // 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 */ + strcpy(line, previous_line); // copy storage buffer to line read_continued_line = FALSE; } - /* if the last character is '\', continue at next line */ + // 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 */ + line[strlen(line) - 1] = '\0'; // cut off trailing backslash + strcpy(previous_line, line); // copy line to storage buffer read_continued_line = TRUE; @@ -2118,7 +2227,7 @@ static boolean loadSetupFileData(void *setup_file_data, char *filename, return TRUE; } -void saveSetupFileHash(SetupFileHash *hash, char *filename) +static void saveSetupFileHash(SetupFileHash *hash, char *filename) { FILE *file; @@ -2153,7 +2262,7 @@ SetupFileList *loadSetupFileList(char *filename) first_valid_list_entry = setup_file_list->next; - /* free empty list header */ + // free empty list header setup_file_list->next = NULL; freeSetupFileList(setup_file_list); @@ -2175,52 +2284,58 @@ SetupFileHash *loadSetupFileHash(char *filename) } -/* ========================================================================= */ -/* setup file stuff */ -/* ========================================================================= */ +// ============================================================================ +// setup file stuff +// ============================================================================ #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series" #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level" #define TOKEN_STR_HANDICAP_LEVEL "handicap_level" -/* level directory info */ +// level directory info #define LEVELINFO_TOKEN_IDENTIFIER 0 #define LEVELINFO_TOKEN_NAME 1 #define LEVELINFO_TOKEN_NAME_SORTING 2 #define LEVELINFO_TOKEN_AUTHOR 3 #define LEVELINFO_TOKEN_YEAR 4 -#define LEVELINFO_TOKEN_IMPORTED_FROM 5 -#define LEVELINFO_TOKEN_IMPORTED_BY 6 -#define LEVELINFO_TOKEN_TESTED_BY 7 -#define LEVELINFO_TOKEN_LEVELS 8 -#define LEVELINFO_TOKEN_FIRST_LEVEL 9 -#define LEVELINFO_TOKEN_SORT_PRIORITY 10 -#define LEVELINFO_TOKEN_LATEST_ENGINE 11 -#define LEVELINFO_TOKEN_LEVEL_GROUP 12 -#define LEVELINFO_TOKEN_READONLY 13 -#define LEVELINFO_TOKEN_GRAPHICS_SET_ECS 14 -#define LEVELINFO_TOKEN_GRAPHICS_SET_AGA 15 -#define LEVELINFO_TOKEN_GRAPHICS_SET 16 -#define LEVELINFO_TOKEN_SOUNDS_SET 17 -#define LEVELINFO_TOKEN_MUSIC_SET 18 -#define LEVELINFO_TOKEN_FILENAME 19 -#define LEVELINFO_TOKEN_FILETYPE 20 -#define LEVELINFO_TOKEN_SPECIAL_FLAGS 21 -#define LEVELINFO_TOKEN_HANDICAP 22 -#define LEVELINFO_TOKEN_SKIP_LEVELS 23 - -#define NUM_LEVELINFO_TOKENS 24 +#define LEVELINFO_TOKEN_PROGRAM_TITLE 5 +#define LEVELINFO_TOKEN_PROGRAM_COPYRIGHT 6 +#define LEVELINFO_TOKEN_PROGRAM_COMPANY 7 +#define LEVELINFO_TOKEN_IMPORTED_FROM 8 +#define LEVELINFO_TOKEN_IMPORTED_BY 9 +#define LEVELINFO_TOKEN_TESTED_BY 10 +#define LEVELINFO_TOKEN_LEVELS 11 +#define LEVELINFO_TOKEN_FIRST_LEVEL 12 +#define LEVELINFO_TOKEN_SORT_PRIORITY 13 +#define LEVELINFO_TOKEN_LATEST_ENGINE 14 +#define LEVELINFO_TOKEN_LEVEL_GROUP 15 +#define LEVELINFO_TOKEN_READONLY 16 +#define LEVELINFO_TOKEN_GRAPHICS_SET_ECS 17 +#define LEVELINFO_TOKEN_GRAPHICS_SET_AGA 18 +#define LEVELINFO_TOKEN_GRAPHICS_SET 19 +#define LEVELINFO_TOKEN_SOUNDS_SET 20 +#define LEVELINFO_TOKEN_MUSIC_SET 21 +#define LEVELINFO_TOKEN_FILENAME 22 +#define LEVELINFO_TOKEN_FILETYPE 23 +#define LEVELINFO_TOKEN_SPECIAL_FLAGS 24 +#define LEVELINFO_TOKEN_HANDICAP 25 +#define LEVELINFO_TOKEN_SKIP_LEVELS 26 + +#define NUM_LEVELINFO_TOKENS 27 static LevelDirTree ldi; static struct TokenInfo levelinfo_tokens[] = { - /* level directory info */ + // level directory info { TYPE_STRING, &ldi.identifier, "identifier" }, { TYPE_STRING, &ldi.name, "name" }, { TYPE_STRING, &ldi.name_sorting, "name_sorting" }, { TYPE_STRING, &ldi.author, "author" }, { TYPE_STRING, &ldi.year, "year" }, + { TYPE_STRING, &ldi.program_title, "program_title" }, + { TYPE_STRING, &ldi.program_copyright, "program_copyright" }, + { TYPE_STRING, &ldi.program_company, "program_company" }, { TYPE_STRING, &ldi.imported_from, "imported_from" }, { TYPE_STRING, &ldi.imported_by, "imported_by" }, { TYPE_STRING, &ldi.tested_by, "tested_by" }, @@ -2244,12 +2359,15 @@ static struct TokenInfo levelinfo_tokens[] = static struct TokenInfo artworkinfo_tokens[] = { - /* artwork directory info */ + // artwork directory info { TYPE_STRING, &ldi.identifier, "identifier" }, { TYPE_STRING, &ldi.subdir, "subdir" }, { TYPE_STRING, &ldi.name, "name" }, { TYPE_STRING, &ldi.name_sorting, "name_sorting" }, { TYPE_STRING, &ldi.author, "author" }, + { TYPE_STRING, &ldi.program_title, "program_title" }, + { TYPE_STRING, &ldi.program_copyright, "program_copyright" }, + { TYPE_STRING, &ldi.program_company, "program_company" }, { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" }, { TYPE_STRING, &ldi.basepath, "basepath" }, { TYPE_STRING, &ldi.fullpath, "fullpath" }, @@ -2286,8 +2404,12 @@ static void setTreeInfoToDefaults(TreeInfo *ti, int type) ti->author = getStringCopy(ANONYMOUS_NAME); ti->year = NULL; - ti->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */ - ti->latest_engine = FALSE; /* default: get from level */ + ti->program_title = NULL; + ti->program_copyright = NULL; + ti->program_company = NULL; + + ti->sort_priority = LEVELCLASS_UNDEFINED; // default: least priority + ti->latest_engine = FALSE; // default: get from level ti->parent_link = FALSE; ti->in_user_dir = FALSE; ti->user_defined = FALSE; @@ -2338,7 +2460,7 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ti, TreeInfo *parent) return; } - /* copy all values from the parent structure */ + // copy all values from the parent structure ti->type = parent->type; @@ -2359,6 +2481,10 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ti, TreeInfo *parent) ti->author = getStringCopy(parent->author); ti->year = getStringCopy(parent->year); + ti->program_title = getStringCopy(parent->program_title); + ti->program_copyright = getStringCopy(parent->program_copyright); + ti->program_company = getStringCopy(parent->program_company); + ti->sort_priority = parent->sort_priority; ti->latest_engine = parent->latest_engine; ti->parent_link = FALSE; @@ -2404,7 +2530,7 @@ static TreeInfo *getTreeInfoCopy(TreeInfo *ti) { TreeInfo *ti_copy = newTreeInfo(); - /* copy all values from the original structure */ + // copy all values from the original structure ti_copy->type = ti->type; @@ -2424,6 +2550,11 @@ static TreeInfo *getTreeInfoCopy(TreeInfo *ti) ti_copy->name_sorting = getStringCopy(ti->name_sorting); ti_copy->author = getStringCopy(ti->author); ti_copy->year = getStringCopy(ti->year); + + ti_copy->program_title = getStringCopy(ti->program_title); + ti_copy->program_copyright = getStringCopy(ti->program_copyright); + ti_copy->program_company = getStringCopy(ti->program_company); + ti_copy->imported_from = getStringCopy(ti->imported_from); ti_copy->imported_by = getStringCopy(ti->imported_by); ti_copy->tested_by = getStringCopy(ti->tested_by); @@ -2481,6 +2612,10 @@ void freeTreeInfo(TreeInfo *ti) checked_free(ti->author); checked_free(ti->year); + checked_free(ti->program_title); + checked_free(ti->program_copyright); + checked_free(ti->program_company); + checked_free(ti->class_desc); checked_free(ti->infotext); @@ -2527,7 +2662,7 @@ void setSetupInfo(struct TokenInfo *token_info, if (token_value == NULL) return; - /* set setup field to corresponding token value */ + // set setup field to corresponding token value switch (token_type) { case TYPE_BOOLEAN: @@ -2556,6 +2691,10 @@ void setSetupInfo(struct TokenInfo *token_info, *(char **)setup_value = getStringCopy(token_value); break; + case TYPE_PLAYER: + *(int *)setup_value = get_player_nr_from_string(token_value); + break; + default: break; } @@ -2649,7 +2788,7 @@ static TreeInfo *createTopTreeInfoNode(TreeInfo *node_first) setString(&ti_new->name_sorting, ti_new->name); setString(&ti_new->subdir, STRING_TOP_DIRECTORY); - setString(&ti_new->fullpath, node_first->fullpath); + setString(&ti_new->fullpath, "."); ti_new->sort_priority = node_first->sort_priority;; ti_new->latest_engine = node_first->latest_engine; @@ -2668,11 +2807,11 @@ static TreeInfo *createTopTreeInfoNode(TreeInfo *node_first) } -/* -------------------------------------------------------------------------- */ -/* functions for handling level and custom artwork info cache */ -/* -------------------------------------------------------------------------- */ +// ---------------------------------------------------------------------------- +// functions for handling level and custom artwork info cache +// ---------------------------------------------------------------------------- -static void LoadArtworkInfoCache() +static void LoadArtworkInfoCache(void) { InitCacheDirectory(); @@ -2680,10 +2819,10 @@ static void LoadArtworkInfoCache() { char *filename = getPath2(getCacheDir(), ARTWORKINFO_CACHE_FILE); - /* try to load artwork info hash from already existing cache file */ + // try to load artwork info hash from already existing cache file artworkinfo_cache_old = loadSetupFileHash(filename); - /* if no artwork info cache file was found, start with empty hash */ + // if no artwork info cache file was found, start with empty hash if (artworkinfo_cache_old == NULL) artworkinfo_cache_old = newSetupFileHash(); @@ -2694,7 +2833,7 @@ static void LoadArtworkInfoCache() artworkinfo_cache_new = newSetupFileHash(); } -static void SaveArtworkInfoCache() +static void SaveArtworkInfoCache(void) { char *filename = getPath2(getCacheDir(), ARTWORKINFO_CACHE_FILE); @@ -2716,7 +2855,7 @@ static char *getCacheTokenPrefix(char *prefix1, char *prefix2) return prefix; } -/* (identical to above function, but separate string buffer needed -- nasty) */ +// (identical to above function, but separate string buffer needed -- nasty) static char *getCacheToken(char *prefix, char *suffix) { static char *token = NULL; @@ -2740,7 +2879,7 @@ static boolean modifiedFileTimestamp(char *filename, char *timestamp_string) if (timestamp_string == NULL) return TRUE; - if (stat(filename, &file_status) != 0) /* cannot stat file */ + if (stat(filename, &file_status) != 0) // cannot stat file return TRUE; return (file_status.st_mtime != atoi(timestamp_string)); @@ -2766,42 +2905,33 @@ static TreeInfo *getArtworkInfoCacheEntry(LevelDirTree *level_node, int type) artwork_info = newTreeInfo(); setTreeInfoToDefaults(artwork_info, type); - /* set all structure fields according to the token/value pairs */ + // set all structure fields according to the token/value pairs ldi = *artwork_info; for (i = 0; artworkinfo_tokens[i].type != -1; i++) { char *token = getCacheToken(token_prefix, artworkinfo_tokens[i].text); char *value = getHashEntry(artworkinfo_cache_old, token); - setSetupInfo(artworkinfo_tokens, i, value); - - /* check if cache entry for this item is invalid or incomplete */ - if (value == NULL) - { - Error(ERR_WARN, "cache entry '%s' invalid", token); - - cached = FALSE; - } + // if defined, use value from cache, else keep default value + if (value != NULL) + setSetupInfo(artworkinfo_tokens, i, value); } *artwork_info = ldi; - } - if (cached) - { char *filename_levelinfo = getPath2(getLevelDirFromTreeInfo(level_node), LEVELINFO_FILENAME); char *filename_artworkinfo = getPath2(getSetupArtworkDir(artwork_info), ARTWORKINFO_FILENAME(type)); - /* check if corresponding "levelinfo.conf" file has changed */ + // check if corresponding "levelinfo.conf" file has changed token_main = getCacheToken(token_prefix, "TIMESTAMP_LEVELINFO"); cache_entry = getHashEntry(artworkinfo_cache_old, token_main); if (modifiedFileTimestamp(filename_levelinfo, cache_entry)) cached = FALSE; - /* check if corresponding ".conf" file has changed */ + // check if corresponding ".conf" file has changed token_main = getCacheToken(token_prefix, "TIMESTAMP_ARTWORKINFO"); cache_entry = getHashEntry(artworkinfo_cache_old, token_main); @@ -2867,11 +2997,228 @@ static void setArtworkInfoCacheEntry(TreeInfo *artwork_info, } -/* -------------------------------------------------------------------------- */ -/* functions for loading level info and custom artwork info */ -/* -------------------------------------------------------------------------- */ +// ---------------------------------------------------------------------------- +// functions for loading level info and custom artwork info +// ---------------------------------------------------------------------------- + +int GetZipFileTreeType(char *zip_filename) +{ + static char *top_dir_path = NULL; + static char *top_dir_conf_filename[NUM_BASE_TREE_TYPES] = { NULL }; + static char *conf_basename[NUM_BASE_TREE_TYPES] = + { + GRAPHICSINFO_FILENAME, + SOUNDSINFO_FILENAME, + MUSICINFO_FILENAME, + LEVELINFO_FILENAME + }; + int j; + + checked_free(top_dir_path); + top_dir_path = NULL; + + for (j = 0; j < NUM_BASE_TREE_TYPES; j++) + { + checked_free(top_dir_conf_filename[j]); + top_dir_conf_filename[j] = NULL; + } + + char **zip_entries = zip_list(zip_filename); + + // check if zip file successfully opened + if (zip_entries == NULL || zip_entries[0] == NULL) + return TREE_TYPE_UNDEFINED; + + // first zip file entry is expected to be top level directory + char *top_dir = zip_entries[0]; + + // check if valid top level directory found in zip file + if (!strSuffix(top_dir, "/")) + return TREE_TYPE_UNDEFINED; + + // get filenames of valid configuration files in top level directory + for (j = 0; j < NUM_BASE_TREE_TYPES; j++) + top_dir_conf_filename[j] = getStringCat2(top_dir, conf_basename[j]); + + int tree_type = TREE_TYPE_UNDEFINED; + int e = 0; + + while (zip_entries[e] != NULL) + { + // check if every zip file entry is below top level directory + if (!strPrefix(zip_entries[e], top_dir)) + return TREE_TYPE_UNDEFINED; + + // check if this zip file entry is a valid configuration filename + for (j = 0; j < NUM_BASE_TREE_TYPES; j++) + { + if (strEqual(zip_entries[e], top_dir_conf_filename[j])) + { + // only exactly one valid configuration file allowed + if (tree_type != TREE_TYPE_UNDEFINED) + return TREE_TYPE_UNDEFINED; + + tree_type = j; + } + } + + e++; + } + + return tree_type; +} + +static boolean CheckZipFileForDirectory(char *zip_filename, char *directory, + int tree_type) +{ + static char *top_dir_path = NULL; + static char *top_dir_conf_filename = NULL; + + checked_free(top_dir_path); + checked_free(top_dir_conf_filename); + + top_dir_path = NULL; + top_dir_conf_filename = NULL; + + char *conf_basename = (tree_type == TREE_TYPE_LEVEL_DIR ? LEVELINFO_FILENAME : + ARTWORKINFO_FILENAME(tree_type)); + + // check if valid configuration filename determined + if (conf_basename == NULL || strEqual(conf_basename, "")) + return FALSE; + + char **zip_entries = zip_list(zip_filename); + + // check if zip file successfully opened + if (zip_entries == NULL || zip_entries[0] == NULL) + return FALSE; + + // first zip file entry is expected to be top level directory + char *top_dir = zip_entries[0]; + + // check if valid top level directory found in zip file + if (!strSuffix(top_dir, "/")) + return FALSE; + + // get path of extracted top level directory + top_dir_path = getPath2(directory, top_dir); + + // remove trailing directory separator from top level directory path + // (required to be able to check for file and directory in next step) + top_dir_path[strlen(top_dir_path) - 1] = '\0'; + + // check if zip file's top level directory already exists in target directory + if (fileExists(top_dir_path)) // (checks for file and directory) + return FALSE; -/* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */ + // get filename of configuration file in top level directory + top_dir_conf_filename = getStringCat2(top_dir, conf_basename); + + boolean found_top_dir_conf_filename = FALSE; + int i = 0; + + while (zip_entries[i] != NULL) + { + // check if every zip file entry is below top level directory + if (!strPrefix(zip_entries[i], top_dir)) + return FALSE; + + // check if this zip file entry is the configuration filename + if (strEqual(zip_entries[i], top_dir_conf_filename)) + found_top_dir_conf_filename = TRUE; + + i++; + } + + // check if valid configuration filename was found in zip file + if (!found_top_dir_conf_filename) + return FALSE; + + return TRUE; +} + +char *ExtractZipFileIntoDirectory(char *zip_filename, char *directory, + int tree_type) +{ + boolean zip_file_valid = CheckZipFileForDirectory(zip_filename, directory, + tree_type); + + if (!zip_file_valid) + { + Error(ERR_WARN, "zip file '%s' rejected!", zip_filename); + + return NULL; + } + + char **zip_entries = zip_extract(zip_filename, directory); + + if (zip_entries == NULL) + { + Error(ERR_WARN, "zip file '%s' could not be extracted!", zip_filename); + + return NULL; + } + + Error(ERR_INFO, "zip file '%s' successfully extracted!", zip_filename); + + // first zip file entry contains top level directory + char *top_dir = zip_entries[0]; + + // remove trailing directory separator from top level directory + top_dir[strlen(top_dir) - 1] = '\0'; + + return top_dir; +} + +static void ProcessZipFilesInDirectory(char *directory, int tree_type) +{ + Directory *dir; + DirectoryEntry *dir_entry; + + if ((dir = openDirectory(directory)) == NULL) + { + // display error if directory is main "options.graphics_directory" etc. + if (tree_type == TREE_TYPE_LEVEL_DIR || + directory == OPTIONS_ARTWORK_DIRECTORY(tree_type)) + Error(ERR_WARN, "cannot read directory '%s'", directory); + + return; + } + + while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries + { + // skip non-zip files (and also directories with zip extension) + if (!strSuffixLower(dir_entry->basename, ".zip") || dir_entry->is_directory) + continue; + + char *zip_filename = getPath2(directory, dir_entry->basename); + char *zip_filename_extracted = getStringCat2(zip_filename, ".extracted"); + char *zip_filename_rejected = getStringCat2(zip_filename, ".rejected"); + + // check if zip file hasn't already been extracted or rejected + if (!fileExists(zip_filename_extracted) && + !fileExists(zip_filename_rejected)) + { + char *top_dir = ExtractZipFileIntoDirectory(zip_filename, directory, + tree_type); + char *marker_filename = (top_dir != NULL ? zip_filename_extracted : + zip_filename_rejected); + FILE *marker_file; + + // create empty file to mark zip file as extracted or rejected + if ((marker_file = fopen(marker_filename, MODE_WRITE))) + fclose(marker_file); + + free(zip_filename); + free(zip_filename_extracted); + free(zip_filename_rejected); + } + } + + closeDirectory(dir); +} + +// forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *); static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, @@ -2885,7 +3232,7 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, LevelDirTree *leveldir_new = NULL; int i; - /* unless debugging, silently ignore directories without "levelinfo.conf" */ + // unless debugging, silently ignore directories without "levelinfo.conf" if (!options.debug && !fileExists(filename)) { free(directory_path); @@ -2898,7 +3245,9 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, if (setup_file_hash == NULL) { +#if DEBUG_NO_CONFIG_FILE Error(ERR_WARN, "ignoring level directory '%s'", directory_path); +#endif free(directory_path); free(filename); @@ -2915,7 +3264,7 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, leveldir_new->subdir = getStringCopy(directory_name); - /* set all structure fields according to the token/value pairs */ + // set all structure fields according to the token/value pairs ldi = *leveldir_new; for (i = 0; i < NUM_LEVELINFO_TOKENS; i++) setSetupInfo(levelinfo_tokens, i, @@ -2931,12 +3280,12 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, if (leveldir_new->name_sorting == NULL) leveldir_new->name_sorting = getStringCopy(leveldir_new->name); - if (node_parent == NULL) /* top level group */ + if (node_parent == NULL) // top level group { leveldir_new->basepath = getStringCopy(level_directory); leveldir_new->fullpath = getStringCopy(leveldir_new->subdir); } - else /* sub level group */ + else // sub level group { leveldir_new->basepath = getStringCopy(node_parent->basepath); leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name); @@ -2948,7 +3297,7 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, leveldir_new->in_user_dir = (!strEqual(leveldir_new->basepath, options.level_directory)); - /* adjust some settings if user's private level directory was detected */ + // adjust some settings if user's private level directory was detected if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED && leveldir_new->in_user_dir && (strEqual(leveldir_new->subdir, getLoginName()) || @@ -2966,7 +3315,7 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new)); - leveldir_new->handicap_level = /* set handicap to default value */ + leveldir_new->handicap_level = // set handicap to default value (leveldir_new->user_defined || !leveldir_new->handicap ? leveldir_new->last_level : leveldir_new->first_level); @@ -2978,10 +3327,10 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, if (leveldir_new->level_group) { - /* create node to link back to current level directory */ + // create node to link back to current level directory createParentTreeInfoNode(leveldir_new); - /* recursively step into sub-directory and look for more level series */ + // recursively step into sub-directory and look for more level series LoadLevelInfoFromLevelDir(&leveldir_new->node_group, leveldir_new, directory_path); } @@ -2996,6 +3345,12 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, TreeInfo *node_parent, char *level_directory) { + // ---------- 1st stage: process any level set zip files ---------- + + ProcessZipFilesInDirectory(level_directory, TREE_TYPE_LEVEL_DIR); + + // ---------- 2nd stage: check for level set directories ---------- + Directory *dir; DirectoryEntry *dir_entry; boolean valid_entry_found = FALSE; @@ -3007,12 +3362,12 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, return; } - while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */ + while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries { char *directory_name = dir_entry->basename; char *directory_path = getPath2(level_directory, directory_name); - /* skip entries for current and parent directory */ + // skip entries for current and parent directory if (strEqual(directory_name, ".") || strEqual(directory_name, "..")) { @@ -3021,8 +3376,8 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, continue; } - /* find out if directory entry is itself a directory */ - if (!dir_entry->is_directory) /* not a directory */ + // find out if directory entry is itself a directory + if (!dir_entry->is_directory) // not a directory { free(directory_path); @@ -3043,10 +3398,10 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, closeDirectory(dir); - /* special case: top level directory may directly contain "levelinfo.conf" */ + // 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" */ + // check if this directory directly contains a file "levelinfo.conf" valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent, level_directory, "."); } @@ -3056,7 +3411,7 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, level_directory); } -boolean AdjustGraphicsForEMC() +boolean AdjustGraphicsForEMC(void) { boolean settings_changed = FALSE; @@ -3066,7 +3421,7 @@ boolean AdjustGraphicsForEMC() return settings_changed; } -void LoadLevelInfo() +void LoadLevelInfo(void) { InitUserLevelDirectory(getLoginName()); @@ -3086,7 +3441,7 @@ void LoadLevelInfo() AdjustGraphicsForEMC(); - /* before sorting, the first entries will be from the user directory */ + // before sorting, the first entries will be from the user directory leveldir_current = getFirstValidTreeInfoEntry(leveldir_first); if (leveldir_first == NULL) @@ -3113,7 +3468,7 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, if (fileExists(filename)) setup_file_hash = loadSetupFileHash(filename); - if (setup_file_hash == NULL) /* no config file -- look for artwork files */ + if (setup_file_hash == NULL) // no config file -- look for artwork files { Directory *dir; DirectoryEntry *dir_entry; @@ -3136,8 +3491,10 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, if (!valid_file_found) { +#if DEBUG_NO_CONFIG_FILE if (!strEqual(directory_name, ".")) Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path); +#endif free(directory_path); free(filename); @@ -3155,9 +3512,9 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, artwork_new->subdir = getStringCopy(directory_name); - if (setup_file_hash) /* (before defining ".color" and ".class_desc") */ + if (setup_file_hash) // (before defining ".color" and ".class_desc") { - /* set all structure fields according to the token/value pairs */ + // 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, @@ -3174,12 +3531,12 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, artwork_new->name_sorting = getStringCopy(artwork_new->name); } - if (node_parent == NULL) /* top level group */ + if (node_parent == NULL) // top level group { artwork_new->basepath = getStringCopy(base_directory); artwork_new->fullpath = getStringCopy(artwork_new->subdir); } - else /* sub level group */ + else // sub level group { artwork_new->basepath = getStringCopy(node_parent->basepath); artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name); @@ -3188,12 +3545,12 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, artwork_new->in_user_dir = (!strEqual(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type))); - /* (may use ".sort_priority" from "setup_file_hash" above) */ + // (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 (setup_file_hash == NULL) // (after determining ".user_defined") { if (strEqual(artwork_new->subdir, ".")) { @@ -3208,7 +3565,7 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, artwork_new->sort_priority = ARTWORKCLASS_CLASSICS; } - /* set to new values after changing ".sort_priority" */ + // set to new values after changing ".sort_priority" artwork_new->color = ARTWORKCOLOR(artwork_new); setString(&artwork_new->class_desc, @@ -3237,25 +3594,31 @@ static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first, TreeInfo *node_parent, char *base_directory, int type) { + // ---------- 1st stage: process any artwork set zip files ---------- + + ProcessZipFilesInDirectory(base_directory, type); + + // ---------- 2nd stage: check for artwork set directories ---------- + 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. */ + // 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 */ + 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 */ + // skip directory entries for current and parent directory if (strEqual(directory_name, ".") || strEqual(directory_name, "..")) { @@ -3264,8 +3627,8 @@ static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first, continue; } - /* skip directory entries which are not a directory */ - if (!dir_entry->is_directory) /* not a directory */ + // skip directory entries which are not a directory + if (!dir_entry->is_directory) // not a directory { free(directory_path); @@ -3274,7 +3637,7 @@ static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first, free(directory_path); - /* check if this directory contains artwork with or without config file */ + // check if this directory contains artwork with or without config file valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent, base_directory, directory_name, type); @@ -3282,7 +3645,7 @@ static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first, closeDirectory(dir); - /* check if this directory directly contains artwork itself */ + // check if this directory directly contains artwork itself valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent, base_directory, ".", type); @@ -3293,7 +3656,7 @@ static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first, static TreeInfo *getDummyArtworkInfo(int type) { - /* this is only needed when there is completely no artwork available */ + // this is only needed when there is completely no artwork available TreeInfo *artwork_new = newTreeInfo(); setTreeInfoToDefaults(artwork_new, type); @@ -3309,7 +3672,7 @@ static TreeInfo *getDummyArtworkInfo(int type) return artwork_new; } -void LoadArtworkInfo() +void LoadArtworkInfo(void) { LoadArtworkInfoCache(); @@ -3343,7 +3706,7 @@ void LoadArtworkInfo() if (artwork.mus_first == NULL) artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR); - /* before sorting, the first entries will be from the user directory */ + // before sorting, the first entries will be from the user directory artwork.gfx_current = getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set); if (artwork.gfx_current == NULL) @@ -3389,16 +3752,16 @@ void LoadArtworkInfo() #endif } -void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node, - LevelDirTree *level_node) +static void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node, + LevelDirTree *level_node) { int type = (*artwork_node)->type; - /* recursively check all level directories for artwork sub-directories */ + // recursively check all level directories for artwork sub-directories while (level_node) { - /* check all tree entries for artwork, but skip parent link entries */ + // check all tree entries for artwork, but skip parent link entries if (!level_node->parent_link) { TreeInfo *artwork_new = getArtworkInfoCacheEntry(level_node, type); @@ -3416,7 +3779,7 @@ void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node, LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path, type); - if (topnode_last != *artwork_node) /* check for newly added node */ + if (topnode_last != *artwork_node) // check for newly added node { artwork_new = *artwork_node; @@ -3431,7 +3794,7 @@ void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node, free(path); } - /* insert artwork info (from old cache or filesystem) into new cache */ + // insert artwork info (from old cache or filesystem) into new cache if (artwork_new != NULL) setArtworkInfoCacheEntry(artwork_new, level_node, type); } @@ -3445,7 +3808,7 @@ void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node, } } -void LoadLevelArtworkInfo() +void LoadLevelArtworkInfo(void) { print_timestamp_init("LoadLevelArtworkInfo"); @@ -3464,7 +3827,7 @@ void LoadLevelArtworkInfo() print_timestamp_time("SaveArtworkInfoCache"); - /* needed for reloading level artwork not known at ealier stage */ + // needed for reloading level artwork not known at ealier stage if (!strEqual(artwork.gfx_current_identifier, setup.graphics_set)) { @@ -3516,34 +3879,182 @@ void LoadLevelArtworkInfo() print_timestamp_done("LoadLevelArtworkInfo"); } -boolean UpdateUserLevelSet(char *level_subdir, - char *level_name, char *level_author, - int num_levels, int first_level_nr) +static boolean AddTreeSetToTreeInfoExt(TreeInfo *tree_node_old, char *tree_dir, + char *tree_subdir_new, int type) +{ + if (tree_node_old == NULL) + { + if (type == TREE_TYPE_LEVEL_DIR) + { + // get level info tree node of personal user level set + tree_node_old = getTreeInfoFromIdentifier(leveldir_first, getLoginName()); + } + else + { + // get artwork info tree node of first artwork set + tree_node_old = ARTWORK_FIRST_NODE(artwork, type); + } + } + + if (tree_dir == NULL) + tree_dir = TREE_USERDIR(type); + + if (tree_node_old == NULL || + tree_dir == NULL || + tree_subdir_new == NULL) // should not happen + return FALSE; + + int draw_deactivation_mask = GetDrawDeactivationMask(); + + // override draw deactivation mask (temporarily disable drawing) + SetDrawDeactivationMask(REDRAW_ALL); + + if (type == TREE_TYPE_LEVEL_DIR) + { + // load new level set config and add it next to first user level set + LoadLevelInfoFromLevelConf(&tree_node_old->next, + tree_node_old->node_parent, + tree_dir, tree_subdir_new); + } + else + { + // load new artwork set config and add it next to first artwork set + LoadArtworkInfoFromArtworkConf(&tree_node_old->next, + tree_node_old->node_parent, + tree_dir, tree_subdir_new, type); + } + + // set draw deactivation mask to previous value + SetDrawDeactivationMask(draw_deactivation_mask); + + // get first node of level or artwork info tree + TreeInfo **tree_node_first = TREE_FIRST_NODE_PTR(type); + + // get tree info node of newly added level or artwork set + TreeInfo *tree_node_new = getTreeInfoFromIdentifier(*tree_node_first, + tree_subdir_new); + + if (tree_node_new == NULL) // should not happen + return FALSE; + + // correct top link and parent node link of newly created tree node + tree_node_new->node_top = tree_node_old->node_top; + tree_node_new->node_parent = tree_node_old->node_parent; + + // sort tree info to adjust position of newly added tree set + sortTreeInfo(tree_node_first); + + return TRUE; +} + +void AddTreeSetToTreeInfo(TreeInfo *tree_node, char *tree_dir, + char *tree_subdir_new, int type) +{ + if (!AddTreeSetToTreeInfoExt(tree_node, tree_dir, tree_subdir_new, type)) + Error(ERR_EXIT, "internal tree info structure corrupted -- aborting"); +} + +void AddUserLevelSetToLevelInfo(char *level_subdir_new) +{ + AddTreeSetToTreeInfo(NULL, NULL, level_subdir_new, TREE_TYPE_LEVEL_DIR); +} + +char *getArtworkIdentifierForUserLevelSet(int type) +{ + char *classic_artwork_set = getClassicArtworkSet(type); + + // check for custom artwork configured in "levelinfo.conf" + char *leveldir_artwork_set = + *LEVELDIR_ARTWORK_SET_PTR(leveldir_current, type); + boolean has_leveldir_artwork_set = + (leveldir_artwork_set != NULL && !strEqual(leveldir_artwork_set, + classic_artwork_set)); + + // check for custom artwork in sub-directory "graphics" etc. + TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type); + char *leveldir_identifier = leveldir_current->identifier; + boolean has_artwork_subdir = + (getTreeInfoFromIdentifier(artwork_first_node, + leveldir_identifier) != NULL); + + return (has_leveldir_artwork_set ? leveldir_artwork_set : + has_artwork_subdir ? leveldir_identifier : + classic_artwork_set); +} + +TreeInfo *getArtworkTreeInfoForUserLevelSet(int type) +{ + char *artwork_set = getArtworkIdentifierForUserLevelSet(type); + TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type); + TreeInfo *ti = getTreeInfoFromIdentifier(artwork_first_node, artwork_set); + + if (ti == NULL) + { + ti = getTreeInfoFromIdentifier(artwork_first_node, + ARTWORK_DEFAULT_SUBDIR(type)); + if (ti == NULL) + Error(ERR_EXIT, "cannot find default graphics -- should not happen"); + } + + return ti; +} + +boolean checkIfCustomArtworkExistsForCurrentLevelSet(void) +{ + char *graphics_set = + getArtworkIdentifierForUserLevelSet(ARTWORK_TYPE_GRAPHICS); + char *sounds_set = + getArtworkIdentifierForUserLevelSet(ARTWORK_TYPE_SOUNDS); + char *music_set = + getArtworkIdentifierForUserLevelSet(ARTWORK_TYPE_MUSIC); + + return (!strEqual(graphics_set, GFX_CLASSIC_SUBDIR) || + !strEqual(sounds_set, SND_CLASSIC_SUBDIR) || + !strEqual(music_set, MUS_CLASSIC_SUBDIR)); +} + +boolean UpdateUserLevelSet(char *level_subdir, char *level_name, + char *level_author, int num_levels) { char *filename = getPath2(getUserLevelDir(level_subdir), LEVELINFO_FILENAME); char *filename_tmp = getStringCat2(filename, ".tmp"); FILE *file = NULL; FILE *file_tmp = NULL; char line[MAX_LINE_LEN]; - boolean update_num_levels_only = FALSE; boolean success = FALSE; + LevelDirTree *leveldir = getTreeInfoFromIdentifier(leveldir_first, + level_subdir); + // update values in level directory tree + + if (level_name != NULL) + setString(&leveldir->name, level_name); + + if (level_author != NULL) + setString(&leveldir->author, level_author); - if (level_name == NULL || level_author == NULL || first_level_nr == -1) - update_num_levels_only = TRUE; + if (num_levels != -1) + leveldir->levels = num_levels; + + // update values that depend on other values + + setString(&leveldir->name_sorting, leveldir->name); + + leveldir->last_level = leveldir->first_level + leveldir->levels - 1; + + // sort order of level sets may have changed + sortTreeInfo(&leveldir_first); if ((file = fopen(filename, MODE_READ)) && (file_tmp = fopen(filename_tmp, MODE_WRITE))) { while (fgets(line, MAX_LINE_LEN, file)) { - if (strPrefix(line, "name:") && !update_num_levels_only) + if (strPrefix(line, "name:") && level_name != NULL) fprintf(file_tmp, "%-32s%s\n", "name:", level_name); - else if (strPrefix(line, "author:") && !update_num_levels_only) + else if (strPrefix(line, "author:") && level_author != NULL) fprintf(file_tmp, "%-32s%s\n", "author:", level_author); - else if (strPrefix(line, "levels:")) + else if (strPrefix(line, "levels:") && num_levels != -1) fprintf(file_tmp, "%-32s%d\n", "levels:", num_levels); - else if (strPrefix(line, "first_level:") && !update_num_levels_only) - fprintf(file_tmp, "%-32s%d\n", "first_level:", first_level_nr); else fputs(line, file_tmp); } @@ -3566,9 +4077,9 @@ boolean UpdateUserLevelSet(char *level_subdir, return success; } -boolean CreateUserLevelSet(char *level_subdir, - char *level_name, char *level_author, - int num_levels, int first_level_nr) +boolean CreateUserLevelSet(char *level_subdir, char *level_name, + char *level_author, int num_levels, + boolean use_artwork_set) { LevelDirTree *level_info; char *filename; @@ -3590,16 +4101,26 @@ boolean CreateUserLevelSet(char *level_subdir, level_info = newTreeInfo(); - /* always start with reliable default values */ + // always start with reliable default values setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR); setString(&level_info->name, level_name); setString(&level_info->author, level_author); level_info->levels = num_levels; - level_info->first_level = first_level_nr; + level_info->first_level = 1; level_info->sort_priority = LEVELCLASS_PRIVATE_START; level_info->readonly = FALSE; + if (use_artwork_set) + { + level_info->graphics_set = + getStringCopy(getArtworkIdentifierForUserLevelSet(ARTWORK_TYPE_GRAPHICS)); + level_info->sounds_set = + getStringCopy(getArtworkIdentifierForUserLevelSet(ARTWORK_TYPE_SOUNDS)); + level_info->music_set = + getStringCopy(getArtworkIdentifierForUserLevelSet(ARTWORK_TYPE_MUSIC)); + } + token_value_position = TOKEN_VALUE_POSITION_SHORT; fprintFileHeader(file, LEVELINFO_FILENAME); @@ -3612,11 +4133,16 @@ boolean CreateUserLevelSet(char *level_subdir, i == LEVELINFO_TOKEN_LEVELS || i == LEVELINFO_TOKEN_FIRST_LEVEL || i == LEVELINFO_TOKEN_SORT_PRIORITY || - i == LEVELINFO_TOKEN_READONLY) + i == LEVELINFO_TOKEN_READONLY || + (use_artwork_set && (i == LEVELINFO_TOKEN_GRAPHICS_SET || + i == LEVELINFO_TOKEN_SOUNDS_SET || + i == LEVELINFO_TOKEN_MUSIC_SET))) fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i)); - /* just to make things nicer :) */ - if (i == LEVELINFO_TOKEN_AUTHOR) + // just to make things nicer :) + if (i == LEVELINFO_TOKEN_AUTHOR || + i == LEVELINFO_TOKEN_FIRST_LEVEL || + (use_artwork_set && i == LEVELINFO_TOKEN_READONLY)) fprintf(file, "\n"); } @@ -3632,9 +4158,9 @@ boolean CreateUserLevelSet(char *level_subdir, return TRUE; } -static void SaveUserLevelInfo() +static void SaveUserLevelInfo(void) { - CreateUserLevelSet(getLoginName(), getLoginName(), getRealName(), 100, 1); + CreateUserLevelSet(getLoginName(), getLoginName(), getRealName(), 100, FALSE); } char *getSetupValue(int type, void *value) @@ -3691,6 +4217,10 @@ char *getSetupValue(int type, void *value) strcpy(value_string, *(char **)value); break; + case TYPE_PLAYER: + sprintf(value_string, "player_%d", *(int *)value + 1); + break; + default: value_string[0] = '\0'; break; @@ -3712,10 +4242,10 @@ char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr) char *token_text = token_info[token_nr].text; char *value_string = getSetupValue(token_type, setup_value); - /* build complete token string */ + // build complete token string sprintf(token_string, "%s%s", prefix, token_text); - /* build setup entry line */ + // build setup entry line line = getFormattedSetupEntry(token_string, value_string); if (token_type == TYPE_KEY_X11) @@ -3723,11 +4253,11 @@ char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr) Key key = *(Key *)setup_value; char *keyname = getKeyNameFromKey(key); - /* add comment, if useful */ + // add comment, if useful if (!strEqual(keyname, "(undefined)") && !strEqual(keyname, "(unknown)")) { - /* add at least one whitespace */ + // add at least one whitespace strcat(line, " "); for (i = strlen(line); i < token_comment_position; i++) strcat(line, " "); @@ -3740,16 +4270,16 @@ char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr) return line; } -void LoadLevelSetup_LastSeries() +void LoadLevelSetup_LastSeries(void) { - /* ----------------------------------------------------------------------- */ - /* ~/./levelsetup.conf */ - /* ----------------------------------------------------------------------- */ + // -------------------------------------------------------------------------- + // ~/./levelsetup.conf + // -------------------------------------------------------------------------- char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME); SetupFileHash *level_setup_hash = NULL; - /* always start with reliable default values */ + // always start with reliable default values leveldir_current = getFirstValidTreeInfoEntry(leveldir_first); if (!strEqual(DEFAULT_LEVELSET, UNDEFINED_LEVELSET)) @@ -3782,9 +4312,9 @@ void LoadLevelSetup_LastSeries() static void SaveLevelSetup_LastSeries_Ext(boolean deactivate_last_level_series) { - /* ----------------------------------------------------------------------- */ - /* ~/./levelsetup.conf */ - /* ----------------------------------------------------------------------- */ + // -------------------------------------------------------------------------- + // ~/./levelsetup.conf + // -------------------------------------------------------------------------- // check if the current level directory structure is available at this point if (leveldir_current == NULL) @@ -3820,22 +4350,27 @@ static void SaveLevelSetup_LastSeries_Ext(boolean deactivate_last_level_series) free(filename); } -void SaveLevelSetup_LastSeries() +void SaveLevelSetup_LastSeries(void) { SaveLevelSetup_LastSeries_Ext(FALSE); } -void SaveLevelSetup_LastSeries_Deactivate() +void SaveLevelSetup_LastSeries_Deactivate(void) { SaveLevelSetup_LastSeries_Ext(TRUE); } -static void checkSeriesInfo() +static void checkSeriesInfo(void) { static char *level_directory = NULL; Directory *dir; +#if 0 + DirectoryEntry *dir_entry; +#endif + + checked_free(level_directory); - /* check for more levels besides the 'levels' field of 'levelinfo.conf' */ + // check for more levels besides the 'levels' field of 'levelinfo.conf' level_directory = getPath2((leveldir_current->in_user_dir ? getUserLevelDir(NULL) : @@ -3849,17 +4384,46 @@ static void checkSeriesInfo() return; } +#if 0 + while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries + { + 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); } -void LoadLevelSetup_SeriesInfo() +void LoadLevelSetup_SeriesInfo(void) { char *filename; SetupFileHash *level_setup_hash = NULL; char *level_subdir = leveldir_current->subdir; int i; - /* always start with reliable default values */ + // always start with reliable default values level_nr = leveldir_current->first_level; for (i = 0; i < MAX_LEVELS; i++) @@ -3870,9 +4434,9 @@ void LoadLevelSetup_SeriesInfo() checkSeriesInfo(); - /* ----------------------------------------------------------------------- */ - /* ~/./levelsetup//levelsetup.conf */ - /* ----------------------------------------------------------------------- */ + // -------------------------------------------------------------------------- + // ~/./levelsetup//levelsetup.conf + // -------------------------------------------------------------------------- level_subdir = leveldir_current->subdir; @@ -3882,7 +4446,7 @@ void LoadLevelSetup_SeriesInfo() { char *token_value; - /* get last played level in this level set */ + // get last played level in this level set token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL); @@ -3896,7 +4460,7 @@ void LoadLevelSetup_SeriesInfo() level_nr = leveldir_current->last_level; } - /* get handicap level in this level set */ + // get handicap level in this level set token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL); @@ -3915,7 +4479,7 @@ void LoadLevelSetup_SeriesInfo() leveldir_current->handicap_level = level_nr; } - /* get number of played and solved levels in this level set */ + // get number of played and solved levels in this level set BEGIN_HASH_ITERATION(level_setup_hash, itr) { @@ -3930,12 +4494,12 @@ void LoadLevelSetup_SeriesInfo() int level_nr = atoi(token); if (value != NULL) - LevelStats_setPlayed(level_nr, atoi(value)); /* read 1st column */ + LevelStats_setPlayed(level_nr, atoi(value)); // read 1st column value = strchr(value, ' '); if (value != NULL) - LevelStats_setSolved(level_nr, atoi(value)); /* read 2nd column */ + LevelStats_setSolved(level_nr, atoi(value)); // read 2nd column } } END_HASH_ITERATION(hash, itr) @@ -3950,7 +4514,7 @@ void LoadLevelSetup_SeriesInfo() free(filename); } -void SaveLevelSetup_SeriesInfo() +void SaveLevelSetup_SeriesInfo(void) { char *filename; char *level_subdir = leveldir_current->subdir; @@ -3959,9 +4523,9 @@ void SaveLevelSetup_SeriesInfo() FILE *file; int i; - /* ----------------------------------------------------------------------- */ - /* ~/./levelsetup//levelsetup.conf */ - /* ----------------------------------------------------------------------- */ + // -------------------------------------------------------------------------- + // ~/./levelsetup//levelsetup.conf + // -------------------------------------------------------------------------- InitLevelSetupDirectory(level_subdir);