X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Flibgame%2Fsetup.c;h=1ae27900c05b0e6e183268632491e9e0f7788c17;hp=5dbbdada82fdd8309178c03eefd7bd04deeaad96;hb=c3e0f340f3ba490911b5e1dce7d9902b01afc207;hpb=d6a1a6cb31174ac804f9ad54a919d65478da588f diff --git a/src/libgame/setup.c b/src/libgame/setup.c index 5dbbdada..1ae27900 100644 --- a/src/libgame/setup.c +++ b/src/libgame/setup.c @@ -1,15 +1,13 @@ -/*********************************************************** -* Artsoft Retro-Game Library * -*----------------------------------------------------------* -* (c) 1994-2006 Artsoft Entertainment * -* Holger Schemel * -* Detmolder Strasse 189 * -* 33604 Bielefeld * -* Germany * -* e-mail: info@artsoft.org * -*----------------------------------------------------------* -* setup.c * -***********************************************************/ +// ============================================================================ +// Artsoft Retro-Game Library +// ---------------------------------------------------------------------------- +// (c) 1995-2014 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// setup.c +// ============================================================================ #include #include @@ -30,8 +28,12 @@ #include "text.h" #include "misc.h" #include "hash.h" +#include "zip/miniunz.h" +#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 static char *levelclass_desc[NUM_LEVELCLASS_DESC] = @@ -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,34 +116,28 @@ 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; - char *data_dir = getCommonDataDir(); + static char *score_level_dir = NULL; char *score_subdir = SCORES_DIRECTORY; - checked_free(score_dir); + if (score_dir == NULL) + { + if (program.global_scores) + score_dir = getPath2(getCommonDataDir(), score_subdir); + else + score_dir = getPath2(getUserGameDataDir(), score_subdir); + } if (level_subdir != NULL) - score_dir = getPath3(data_dir, score_subdir, level_subdir); - else - score_dir = getPath2(data_dir, score_subdir); + { + checked_free(score_level_dir); + + score_level_dir = getPath2(score_dir, level_subdir); + + return score_level_dir; + } return score_dir; } @@ -162,7 +158,7 @@ static char *getLevelSetupDir(char *level_subdir) return levelsetup_dir; } -static char *getCacheDir() +static char *getCacheDir(void) { static char *cache_dir = NULL; @@ -172,6 +168,16 @@ static char *getCacheDir() return cache_dir; } +static char *getNetworkDir(void) +{ + static char *network_dir = NULL; + + if (network_dir == NULL) + network_dir = getPath2(getUserGameDataDir(), NETWORK_DIRECTORY); + + return network_dir; +} + static char *getLevelDirFromTreeInfo(TreeInfo *node) { static char *level_dir = NULL; @@ -187,11 +193,65 @@ 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(void) +{ + static char *new_level_subdir = NULL; + char *subdir_prefix = getLoginName(); + char subdir_suffix[10]; + int max_suffix_number = 1000; + int i = 0; + + while (++i < max_suffix_number) + { + sprintf(subdir_suffix, "_%d", i); + + checked_free(new_level_subdir); + new_level_subdir = getStringCat2(subdir_prefix, subdir_suffix); + + if (!directoryExists(getUserLevelDir(new_level_subdir))) + break; + } + + return new_level_subdir; +} + static char *getTapeDir(char *level_subdir) { static char *tape_dir = NULL; @@ -208,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(); @@ -280,7 +340,7 @@ static char *getClassicArtworkDir(int type) getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : ""); } -static char *getUserGraphicsDir() +static char *getUserGraphicsDir(void) { static char *usergraphics_dir = NULL; @@ -290,7 +350,7 @@ static char *getUserGraphicsDir() return usergraphics_dir; } -static char *getUserSoundsDir() +static char *getUserSoundsDir(void) { static char *usersounds_dir = NULL; @@ -300,7 +360,7 @@ static char *getUserSoundsDir() return usersounds_dir; } -static char *getUserMusicDir() +static char *getUserMusicDir(void) { static char *usermusic_dir = NULL; @@ -314,6 +374,9 @@ static char *getSetupArtworkDir(TreeInfo *ti) { static char *artwork_dir = NULL; + if (ti == NULL) + return NULL; + checked_free(artwork_dir); artwork_dir = getPath2(ti->basepath, ti->fullpath); @@ -374,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; @@ -382,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; @@ -390,6 +453,76 @@ inline static char *getLevelArtworkDir(int type) return LEVELDIR_ARTWORK_PATH(leveldir_current, type); } +char *getProgramMainDataPath(char *command_filename, char *base_path) +{ + // check if the program's main data base directory is configured + if (!strEqual(base_path, ".")) + return 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); + + // 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); + + char *command_basepath = getBasePath(command_filename); + char *command_basename = getBaseNameNoSuffix(command_filename); + char *command_filename_2 = getPath2(command_basepath, command_basename); + + char *config_filename_1 = getStringCat2(command_filename_1, ".conf"); + char *config_filename_2 = getStringCat2(command_filename_2, ".conf"); + char *config_filename_3 = getPath2(conf_directory, SETUP_FILENAME); + + // 1st try: look for config file that exactly matches the binary filename + if (fileExists(config_filename_1)) + return config_filename_1; + + // 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) { static char *filename = NULL; @@ -437,12 +570,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; @@ -453,7 +588,12 @@ char *getSetupFilename() return filename; } -char *getEditorSetupFilename() +char *getDefaultSetupFilename(void) +{ + return program.config_filename; +} + +char *getEditorSetupFilename(void) { static char *filename = NULL; @@ -469,7 +609,7 @@ char *getEditorSetupFilename() return filename; } -char *getHelpAnimFilename() +char *getHelpAnimFilename(void) { static char *filename = NULL; @@ -480,7 +620,7 @@ char *getHelpAnimFilename() return filename; } -char *getHelpTextFilename() +char *getHelpTextFilename(void) { static char *filename = NULL; @@ -491,7 +631,7 @@ char *getHelpTextFilename() return filename; } -char *getLevelSetInfoFilename() +char *getLevelSetInfoFilename(void) { static char *filename = NULL; char *basenames[] = @@ -520,7 +660,7 @@ char *getLevelSetInfoFilename() return NULL; } -char *getLevelSetTitleMessageBasename(int nr, boolean initial) +static char *getLevelSetTitleMessageBasename(int nr, boolean initial) { static char basename[32]; @@ -542,38 +682,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; @@ -581,51 +721,24 @@ 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) { - 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) @@ -639,64 +752,66 @@ char *getCustomImageFilename(char *basename) if (!gfx.override_level_graphics) { - /* 1st try: look for special artwork in current level series directory */ - filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename); + // 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 */ - filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename); + // 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 */ - filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename); + // 3rd try: look for special artwork in configured artwork directory + filename = getImg2(getSetupArtworkDir(artwork.gfx_current), basename); if (fileExists(filename)) return filename; free(filename); } - /* 4th try: look for default artwork in new default artwork directory */ - filename = getPath2(getDefaultGraphicsDir(GFX_DEFAULT_SUBDIR), basename); + // 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 */ - filename = getPath2(options.graphics_directory, basename); + // 5th try: look for default artwork in old default artwork directory + filename = getImg2(options.graphics_directory, basename); if (fileExists(filename)) return filename; -#if defined(CREATE_SPECIAL_EDITION) - free(filename); + if (!strEqual(GFX_FALLBACK_FILENAME, UNDEFINED_FILENAME)) + { + free(filename); - if (options.debug) - Error(ERR_WARN, "cannot find artwork file '%s' (using fallback)", basename); + if (options.debug) + 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) */ - filename = getPath2(options.graphics_directory, GFX_FALLBACK_FILENAME); - if (fileExists(filename)) - return filename; -#endif + // 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) @@ -710,31 +825,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; @@ -742,32 +857,34 @@ 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; -#if defined(CREATE_SPECIAL_EDITION) - free(filename); + if (!strEqual(SND_FALLBACK_FILENAME, UNDEFINED_FILENAME)) + { + free(filename); - if (options.debug) - Error(ERR_WARN, "cannot find artwork file '%s' (using fallback)", basename); + if (options.debug) + 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) */ - filename = getPath2(options.sounds_directory, SND_FALLBACK_FILENAME); - if (fileExists(filename)) - return filename; -#endif + // 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) @@ -781,31 +898,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; @@ -813,32 +930,34 @@ 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; -#if defined(CREATE_SPECIAL_EDITION) - free(filename); + if (!strEqual(MUS_FALLBACK_FILENAME, UNDEFINED_FILENAME)) + { + free(filename); - if (options.debug) - Error(ERR_WARN, "cannot find artwork file '%s' (using fallback)", basename); + if (options.debug) + 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) */ - filename = getPath2(options.music_directory, MUS_FALLBACK_FILENAME); - if (fileExists(filename)) - return filename; -#endif + // 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) @@ -878,31 +997,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; @@ -910,19 +1029,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) @@ -934,12 +1053,18 @@ void InitTapeDirectory(char *level_subdir) void InitScoreDirectory(char *level_subdir) { - createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC); - createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC); - createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC); + int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE); + + if (program.global_scores) + createDirectory(getCommonDataDir(), "common data", permissions); + else + createDirectory(getUserGameDataDir(), "user data", permissions); + + createDirectory(getScoreDir(NULL), "main score", permissions); + createDirectory(getScoreDir(level_subdir), "level score", permissions); } -static void SaveUserLevelInfo(); +static void SaveUserLevelInfo(void); void InitUserLevelDirectory(char *level_subdir) { @@ -953,6 +1078,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); @@ -960,18 +1096,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)); } @@ -1014,16 +1150,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; } @@ -1032,9 +1168,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; } @@ -1105,8 +1241,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; @@ -1118,16 +1254,10 @@ TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent, return cloneTreeNode(node_top, node_parent, node->next, skip_sets_without_levels); -#if 1 - node_new = getTreeInfoCopy(node); /* copy complete node */ -#else - node_new = newTreeInfo(); - - *node_new = *node; /* copy complete node */ -#endif + 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, @@ -1139,7 +1269,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); @@ -1212,11 +1342,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; @@ -1224,21 +1354,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) { @@ -1255,9 +1385,9 @@ void sortTreeInfo(TreeInfo **node_first) } -/* ========================================================================= */ -/* some stuff from "files.c" */ -/* ========================================================================= */ +// ============================================================================ +// some stuff from "files.c" +// ============================================================================ #if defined(PLATFORM_WIN32) #ifndef S_IRGRP @@ -1284,24 +1414,27 @@ 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) #define MODE_W_PRIVATE (S_IWUSR) -#define MODE_W_PUBLIC (S_IWUSR | S_IWGRP) +#define MODE_W_PUBLIC_FILE (S_IWUSR | S_IWGRP) #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID) #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE) #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR) +#define DIR_PERMS_PUBLIC_ALL (MODE_R_ALL | MODE_X_ALL | MODE_W_ALL) #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE) -#define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC) +#define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC_FILE) +#define FILE_PERMS_PUBLIC_ALL (MODE_R_ALL | MODE_W_ALL) -char *getHomeDir() + +char *getHomeDir(void) { static char *dir = NULL; @@ -1343,7 +1476,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; @@ -1377,7 +1510,10 @@ char *getUserGameDataDir(void) #if defined(PLATFORM_ANDROID) if (user_game_data_dir == NULL) - user_game_data_dir = SDL_AndroidGetInternalStoragePath(); + user_game_data_dir = (char *)(SDL_AndroidGetExternalStorageState() & + SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? + SDL_AndroidGetExternalStoragePath() : + SDL_AndroidGetInternalStoragePath()); #else if (user_game_data_dir == NULL) user_game_data_dir = getPath2(getPersonalDataDir(), @@ -1387,31 +1523,7 @@ char *getUserGameDataDir(void) return user_game_data_dir; } -void updateUserGameDataDir() -{ -#if defined(PLATFORM_MACOSX) - char *userdata_dir_old = getPath2(getHomeDir(), program.userdata_subdir_unix); - char *userdata_dir_new = getUserGameDataDir(); /* do not free() this */ - - /* convert old Unix style game data directory to Mac OS X style, if needed */ - if (directoryExists(userdata_dir_old) && !directoryExists(userdata_dir_new)) - { - if (rename(userdata_dir_old, userdata_dir_new) != 0) - { - Error(ERR_WARN, "cannot move game data directory '%s' to '%s'", - userdata_dir_old, userdata_dir_new); - - /* continue using Unix style data directory -- this should not happen */ - program.userdata_path = getPath2(getPersonalDataDir(), - program.userdata_subdir_unix); - } - } - - free(userdata_dir_old); -#endif -} - -char *getSetupDir() +char *getSetupDir(void) { return getUserGameDataDir(); } @@ -1434,7 +1546,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()); @@ -1445,34 +1557,39 @@ static boolean posix_process_running_setgid() 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 */ + if (directoryExists(dir)) + return; + + // 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); mode_t group_umask = ~(dir_mode & S_IRWXG); 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 (permission_class == PERMS_PUBLIC) + { + // 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); - else - dir_mode |= MODE_W_ALL; + if (running_setgid) + posix_umask(last_umask & group_umask); + else + dir_mode = DIR_PERMS_PUBLIC_ALL; + } - if (!directoryExists(dir)) - if (posix_mkdir(dir, dir_mode) != 0) - Error(ERR_WARN, "cannot create %s directory '%s': %s", - text, dir, strerror(errno)); + if (posix_mkdir(dir, dir_mode) != 0) + Error(ERR_WARN, "cannot create %s directory '%s': %s", + text, dir, strerror(errno)); 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); } @@ -1484,7 +1601,7 @@ void SetFilePermissions(char *filename, int permission_class) FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC); if (permission_class == PERMS_PUBLIC && !running_setgid) - perms |= MODE_W_ALL; + perms = FILE_PERMS_PUBLIC_ALL; chmod(filename, perms); } @@ -1495,15 +1612,26 @@ 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; } +void fprintFileHeader(FILE *file, char *basename) +{ + char *prefix = "# "; + char *sep1 = "="; + + fprintf_line_with_prefix(file, prefix, sep1, 77); + fprintf(file, "%s%s\n", prefix, basename); + fprintf_line_with_prefix(file, prefix, sep1, 77); + fprintf(file, "\n"); +} + int getFileVersionFromCookieString(const char *cookie) { const char *ptr_cookie1, *ptr_cookie2; @@ -1513,7 +1641,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; @@ -1529,10 +1657,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) @@ -1551,25 +1679,26 @@ boolean checkCookieString(const char *cookie, const char *template) return TRUE; } -/* ------------------------------------------------------------------------- */ -/* 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; @@ -1642,7 +1771,7 @@ SetupFileList *addListEntry(SetupFileList *list, char *token, char *value) return addListEntry(list->next, token, value); } -#if 0 +#if ENABLE_UNUSED_CODE #ifdef DEBUG static void printSetupFileList(SetupFileList *list) { @@ -1694,7 +1823,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; } @@ -1704,7 +1833,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); @@ -1720,7 +1849,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) @@ -1740,7 +1869,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"); @@ -1754,7 +1883,8 @@ char *removeHashEntry(SetupFileHash *hash, char *token) return remove_hash_entry(hash, token); } -#if 0 +#if ENABLE_UNUSED_CODE +#if DEBUG static void printSetupFileHash(SetupFileHash *hash) { BEGIN_HASH_ITERATION(hash, itr) @@ -1765,6 +1895,7 @@ static void printSetupFileHash(SetupFileHash *hash) END_HASH_ITERATION(hash, itr) } #endif +#endif #define ALLOW_TOKEN_VALUE_SEPARATOR_BEING_WHITESPACE 1 #define CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING 0 @@ -1787,7 +1918,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); @@ -1798,7 +1929,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 == '#') @@ -1808,37 +1939,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++) { -#if 1 - /* 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 == '=') -#else - if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':') -#endif { - *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; @@ -1847,15 +1974,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; @@ -1892,21 +2019,16 @@ 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; -#if 0 - if (*value == '\0') - value = "true"; /* treat tokens without value as "true" */ -#endif - *token_ptr = token; *value_ptr = value; @@ -1915,16 +2037,13 @@ 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); } -#if 1 - -#if 1 static boolean loadSetupFileData(void *setup_file_data, char *filename, boolean top_recursion_level, boolean is_hash) { @@ -1944,78 +2063,60 @@ static boolean loadSetupFileData(void *setup_file_data, char *filename, 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); +#if DEBUG_NO_CONFIG_FILE + Error(ERR_DEBUG, "cannot open configuration file '%s'", filename); +#endif return FALSE; } -#if 0 - Error(ERR_INFO, "===== reading file: '%s'", filename); -#endif - - /* 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; -#if 0 - Error(ERR_INFO, "got line: '%s'", line); -#endif - - /* 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) { -#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 */ + // 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; @@ -2036,10 +2137,6 @@ static boolean loadSetupFileData(void *setup_file_data, char *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); @@ -2111,458 +2208,47 @@ static boolean loadSetupFileData(void *setup_file_data, char *filename, return TRUE; } -#else - -static boolean loadSetupFileData(void *setup_file_data, char *filename, - boolean top_recursion_level, boolean is_hash) +static void saveSetupFileHash(SetupFileHash *hash, char *filename) { - 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 (!(file = fopen(filename, MODE_READ))) + if (!(file = fopen(filename, MODE_WRITE))) { - Error(ERR_WARN, "cannot open configuration file '%s'", filename); + Error(ERR_WARN, "cannot write configuration file '%s'", filename); - return FALSE; + return; } - /* use "insert pointer" to store list end for constant insertion complexity */ - if (!is_hash) - insert_ptr = setup_file_data; + BEGIN_HASH_ITERATION(hash, itr) + { + fprintf(file, "%s\n", getFormattedSetupEntry(HASH_ITERATION_TOKEN(itr), + HASH_ITERATION_VALUE(itr))); + } + END_HASH_ITERATION(hash, itr) - /* on top invocation, create hash to mark included files (to prevent loops) */ - if (top_recursion_level) - include_filename_hash = newSetupFileHash(); + fclose(file); +} - /* mark this file as already included (to prevent including it again) */ - setHashEntry(include_filename_hash, getBaseNamePtr(filename), "true"); +SetupFileList *loadSetupFileList(char *filename) +{ + SetupFileList *setup_file_list = newSetupFileList("", ""); + SetupFileList *first_valid_list_entry; - while (!feof(file)) + if (!loadSetupFileData(setup_file_list, filename, TRUE, FALSE)) { - /* read next line of input file */ - if (!fgets(line, MAX_LINE_LEN, file)) - break; + freeSetupFileList(setup_file_list); - /* check if line was completely read and is terminated by line break */ - if (strlen(line) > 0 && line[strlen(line) - 1] == '\n') - line_nr++; + return NULL; + } - /* 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'; + first_valid_list_entry = setup_file_list->next; - /* copy raw input line for later use (mainly debugging output) */ - strcpy(line_raw, line); + // free empty list header + setup_file_list->next = NULL; + freeSetupFileList(setup_file_list); - 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++; - } - } - } - - fclose(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; -} - -#endif - -#else - -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; - int token_count = 0; - -#if CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING - token_value_separator_warning = FALSE; -#endif - - if (!(file = fopen(filename, MODE_READ))) - { - Error(ERR_WARN, "cannot open configuration file '%s'", filename); - - return FALSE; - } - - /* 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 (!feof(file)) - { - /* read next line of input file */ - if (!fgets(line, MAX_LINE_LEN, file)) - 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) */ - 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) - { - /* cut leading whitespaces from input line */ - for (line_ptr = line; *line_ptr; line_ptr++) - if (*line_ptr != ' ' && *line_ptr != '\t') - break; - - /* 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; - } - - /* cut trailing comment from input line */ - for (line_ptr = line; *line_ptr; line_ptr++) - { - if (*line_ptr == '#') - { - *line_ptr = '\0'; - break; - } - } - - /* 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 */ - if (*line == '\0') - continue; - - /* cut leading whitespaces from token */ - for (token = line; *token; token++) - if (*token != ' ' && *token != '\t') - break; - - /* start with empty value as reliable default */ - value = ""; - - token_value_separator_found = FALSE; - - /* find end of token to determine start of value */ - for (line_ptr = token; *line_ptr; line_ptr++) - { -#if 1 - /* first look for an explicit token/value separator, like ':' or '=' */ - if (*line_ptr == ':' || *line_ptr == '=') -#else - if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':') -#endif - { - *line_ptr = '\0'; /* terminate token string */ - value = line_ptr + 1; /* set beginning of value */ - - token_value_separator_found = TRUE; - - break; - } - } - -#if ALLOW_TOKEN_VALUE_SEPARATOR_BEING_WHITESPACE - /* fallback: if no token/value separator found, also allow whitespaces */ - if (!token_value_separator_found) - { - 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 */ - - token_value_separator_found = TRUE; - - break; - } - } - -#if CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING - if (token_value_separator_found) - { - if (!token_value_separator_warning) - { - Error(ERR_INFO_LINE, "-"); - Error(ERR_WARN, "missing token/value separator(s) in config file:"); - Error(ERR_INFO, "- config file: '%s'", filename); - - token_value_separator_warning = TRUE; - } - - Error(ERR_INFO, "- line %d: '%s'", line_nr, line_raw); - } -#endif - } -#endif - - /* 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 */ - for (; *value; value++) - if (*value != ' ' && *value != '\t') - break; - -#if 0 - if (*value == '\0') - value = "true"; /* treat tokens without value as "true" */ -#endif - - 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); - } - else - { - Error(ERR_WARN, "ignoring already processed file '%s'", value); - } - } - else - { - if (is_hash) - setHashEntry((SetupFileHash *)setup_file_data, token, value); - else - insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value); - - token_count++; - } - } - } - - fclose(file); - -#if CHECK_TOKEN_VALUE_SEPARATOR__WARN_IF_MISSING - if (token_value_separator_warning) - Error(ERR_INFO_LINE, "-"); -#endif - - if (token_count == 0) - Error(ERR_WARN, "configuration file '%s' is empty", filename); - - if (top_recursion_level) - freeSetupFileHash(include_filename_hash); - - return TRUE; -} -#endif - -void saveSetupFileHash(SetupFileHash *hash, char *filename) -{ - FILE *file; - - if (!(file = fopen(filename, MODE_WRITE))) - { - Error(ERR_WARN, "cannot write configuration file '%s'", filename); - - return; - } - - BEGIN_HASH_ITERATION(hash, itr) - { - fprintf(file, "%s\n", getFormattedSetupEntry(HASH_ITERATION_TOKEN(itr), - HASH_ITERATION_VALUE(itr))); - } - END_HASH_ITERATION(hash, itr) - - fclose(file); -} - -SetupFileList *loadSetupFileList(char *filename) -{ - SetupFileList *setup_file_list = newSetupFileList("", ""); - SetupFileList *first_valid_list_entry; - - if (!loadSetupFileData(setup_file_list, filename, TRUE, FALSE)) - { - freeSetupFileList(setup_file_list); - - return NULL; - } - - first_valid_list_entry = setup_file_list->next; - - /* free empty list header */ - setup_file_list->next = NULL; - freeSetupFileList(setup_file_list); - - return first_valid_list_entry; -} + return first_valid_list_entry; +} SetupFileHash *loadSetupFileHash(char *filename) { @@ -2578,64 +2264,59 @@ SetupFileHash *loadSetupFileHash(char *filename) return setup_file_hash; } -void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash, - char *filename, char *identifier) -{ - char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER); - if (value == NULL) - Error(ERR_WARN, "config file '%s' has no file identifier", filename); - else if (!checkCookieString(value, identifier)) - Error(ERR_WARN, "config file '%s' has wrong file identifier", 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" }, @@ -2659,12 +2340,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" }, @@ -2701,8 +2385,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; @@ -2753,7 +2441,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; @@ -2774,6 +2462,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; @@ -2790,32 +2482,28 @@ static void setTreeInfoToDefaultsFromParent(TreeInfo *ti, TreeInfo *parent) ti->imported_by = getStringCopy(parent->imported_by); ti->tested_by = getStringCopy(parent->tested_by); - ti->graphics_set_ecs = NULL; - ti->graphics_set_aga = NULL; - ti->graphics_set = NULL; - ti->sounds_set = NULL; - ti->music_set = NULL; + ti->graphics_set_ecs = getStringCopy(parent->graphics_set_ecs); + ti->graphics_set_aga = getStringCopy(parent->graphics_set_aga); + ti->graphics_set = getStringCopy(parent->graphics_set); + ti->sounds_set = getStringCopy(parent->sounds_set); + ti->music_set = getStringCopy(parent->music_set); ti->graphics_path = getStringCopy(UNDEFINED_FILENAME); ti->sounds_path = getStringCopy(UNDEFINED_FILENAME); ti->music_path = getStringCopy(UNDEFINED_FILENAME); - ti->level_filename = NULL; - ti->level_filetype = NULL; + ti->level_filename = getStringCopy(parent->level_filename); + ti->level_filetype = getStringCopy(parent->level_filetype); ti->special_flags = getStringCopy(parent->special_flags); - ti->levels = 0; - ti->first_level = 0; - ti->last_level = 0; + ti->levels = parent->levels; + ti->first_level = parent->first_level; + ti->last_level = parent->last_level; ti->level_group = FALSE; - ti->handicap_level = 0; -#if 1 + ti->handicap_level = parent->handicap_level; ti->readonly = parent->readonly; -#else - ti->readonly = TRUE; -#endif - ti->handicap = TRUE; - ti->skip_levels = FALSE; + ti->handicap = parent->handicap; + ti->skip_levels = parent->skip_levels; } } @@ -2823,7 +2511,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; @@ -2843,6 +2531,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); @@ -2900,6 +2593,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); @@ -2926,6 +2623,14 @@ void freeTreeInfo(TreeInfo *ti) 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); } @@ -2938,7 +2643,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: @@ -2967,6 +2672,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; } @@ -3012,12 +2721,12 @@ static int compareTreeInfoEntries(const void *object1, const void *object2) return compare_result; } -static void createParentTreeInfoNode(TreeInfo *node_parent) +static TreeInfo *createParentTreeInfoNode(TreeInfo *node_parent) { TreeInfo *ti_new; if (node_parent == NULL) - return; + return NULL; ti_new = newTreeInfo(); setTreeInfoToDefaults(ti_new, node_parent->type); @@ -3029,7 +2738,7 @@ static void createParentTreeInfoNode(TreeInfo *node_parent) setString(&ti_new->name, ".. (parent directory)"); setString(&ti_new->name_sorting, ti_new->name); - setString(&ti_new->subdir, ".."); + setString(&ti_new->subdir, STRING_PARENT_DIRECTORY); setString(&ti_new->fullpath, node_parent->fullpath); ti_new->sort_priority = node_parent->sort_priority; @@ -3038,14 +2747,52 @@ static void createParentTreeInfoNode(TreeInfo *node_parent) setString(&ti_new->class_desc, getLevelClassDescription(ti_new)); pushTreeInfo(&node_parent->node_group, ti_new); + + return ti_new; } +static TreeInfo *createTopTreeInfoNode(TreeInfo *node_first) +{ + TreeInfo *ti_new, *ti_new2; + + if (node_first == NULL) + return NULL; + + ti_new = newTreeInfo(); + setTreeInfoToDefaults(ti_new, TREE_TYPE_LEVEL_DIR); + + ti_new->node_parent = NULL; + ti_new->parent_link = FALSE; + + setString(&ti_new->identifier, node_first->identifier); + setString(&ti_new->name, "level sets"); + setString(&ti_new->name_sorting, ti_new->name); + + setString(&ti_new->subdir, STRING_TOP_DIRECTORY); + setString(&ti_new->fullpath, "."); + + ti_new->sort_priority = node_first->sort_priority;; + ti_new->latest_engine = node_first->latest_engine; + + setString(&ti_new->class_desc, "level sets"); -/* -------------------------------------------------------------------------- */ -/* functions for handling level and custom artwork info cache */ -/* -------------------------------------------------------------------------- */ + ti_new->node_group = node_first; + ti_new->level_group = TRUE; -static void LoadArtworkInfoCache() + ti_new2 = createParentTreeInfoNode(ti_new); + + setString(&ti_new2->name, ".. (main menu)"); + setString(&ti_new2->name_sorting, ti_new2->name); + + return ti_new; +} + + +// ---------------------------------------------------------------------------- +// functions for handling level and custom artwork info cache +// ---------------------------------------------------------------------------- + +static void LoadArtworkInfoCache(void) { InitCacheDirectory(); @@ -3053,10 +2800,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(); @@ -3067,7 +2814,7 @@ static void LoadArtworkInfoCache() artworkinfo_cache_new = newSetupFileHash(); } -static void SaveArtworkInfoCache() +static void SaveArtworkInfoCache(void) { char *filename = getPath2(getCacheDir(), ARTWORKINFO_CACHE_FILE); @@ -3089,7 +2836,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; @@ -3103,16 +2850,7 @@ static char *getCacheToken(char *prefix, char *suffix) static char *getFileTimestampString(char *filename) { -#if 1 return getStringCopy(i_to_a(getFileTimestampEpochSeconds(filename))); -#else - struct stat file_status; - - if (stat(filename, &file_status) != 0) /* cannot stat file */ - return getStringCopy(i_to_a(0)); - - return getStringCopy(i_to_a(file_status.st_mtime)); -#endif } static boolean modifiedFileTimestamp(char *filename, char *timestamp_string) @@ -3122,7 +2860,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)); @@ -3148,55 +2886,39 @@ 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) - { -#if 1 - Error(ERR_WARN, "cache entry '%s' invalid", token); -#endif - - 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); if (modifiedFileTimestamp(filename_artworkinfo, cache_entry)) cached = FALSE; -#if 0 - if (!cached) - printf("::: '%s': INVALIDATED FROM CACHE BY TIMESTAMP\n", identifier); -#endif - checked_free(filename_levelinfo); checked_free(filename_artworkinfo); } @@ -3244,23 +2966,165 @@ static void setArtworkInfoCacheEntry(TreeInfo *artwork_info, checked_free(timestamp_artworkinfo); } - ldi = *artwork_info; - for (i = 0; artworkinfo_tokens[i].type != -1; i++) + ldi = *artwork_info; + for (i = 0; artworkinfo_tokens[i].type != -1; i++) + { + char *token = getCacheToken(token_prefix, artworkinfo_tokens[i].text); + char *value = getSetupValue(artworkinfo_tokens[i].type, + artworkinfo_tokens[i].value); + if (value != NULL) + setHashEntry(artworkinfo_cache_new, token, value); + } +} + + +// ---------------------------------------------------------------------------- +// functions for loading level info and custom artwork info +// ---------------------------------------------------------------------------- + +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; + + // 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; +} + +static boolean ExtractZipFileIntoDirectory(char *zip_filename, char *directory, + int tree_type) +{ + boolean zip_file_valid = CheckZipFileForDirectory(zip_filename, directory, + tree_type); + + Error(ERR_DEBUG, "zip file '%s': %s", zip_filename, + (zip_file_valid ? "EXTRACT" : "REJECT")); + + if (!zip_file_valid) + return FALSE; + + char **zip_entries = zip_extract(zip_filename, directory); + + boolean zip_file_extracted = (zip_entries != NULL); + + if (zip_file_extracted) + Error(ERR_DEBUG, "zip file successfully extracted!"); + else + Error(ERR_DEBUG, "zip file could not be extracted!"); + + return zip_file_extracted; +} + +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 { - char *token = getCacheToken(token_prefix, artworkinfo_tokens[i].text); - char *value = getSetupValue(artworkinfo_tokens[i].type, - artworkinfo_tokens[i].value); - if (value != NULL) - setHashEntry(artworkinfo_cache_new, token, value); - } -} + // 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)) + { + boolean zip_file_extracted = ExtractZipFileIntoDirectory(zip_filename, + directory, + tree_type); + char *marker_filename = (zip_file_extracted ? 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); + } + } -/* -------------------------------------------------------------------------- */ -/* functions for loading level info and custom artwork info */ -/* -------------------------------------------------------------------------- */ + closeDirectory(dir); +} -/* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */ +// forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *); static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, @@ -3268,17 +3132,13 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, char *level_directory, char *directory_name) { -#if 0 - 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); SetupFileHash *setup_file_hash; 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); @@ -3291,7 +3151,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); @@ -3308,10 +3170,7 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, leveldir_new->subdir = getStringCopy(directory_name); - checkSetupFileHashIdentifier(setup_file_hash, filename, - getCookie("LEVELINFO")); - - /* 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, @@ -3327,35 +3186,24 @@ 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); } -#if 0 - if (leveldir_new->levels < 1) - leveldir_new->levels = 1; -#endif - leveldir_new->last_level = leveldir_new->first_level + leveldir_new->levels - 1; 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 */ + // 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()) || @@ -3373,38 +3221,11 @@ 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); -#if 1 -#if 1 - DrawInitTextExt(leveldir_new->name, 150, FC_YELLOW, - leveldir_new->level_group); -#else - if (leveldir_new->level_group || - DelayReached(&progress_delay, progress_delay_value)) - DrawInitText(leveldir_new->name, 150, FC_YELLOW); -#endif -#else DrawInitText(leveldir_new->name, 150, FC_YELLOW); -#endif - -#if 0 - /* !!! don't skip sets without levels (else artwork base sets are missing) */ -#if 1 - if (leveldir_new->levels < 1 && !leveldir_new->level_group) - { - /* skip level sets without levels (which are probably artwork base sets) */ - - freeSetupFileHash(setup_file_hash); - free(directory_path); - free(filename); - - return FALSE; - } -#endif -#endif pushTreeInfo(node_first, leveldir_new); @@ -3412,10 +3233,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); } @@ -3426,19 +3247,20 @@ static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first, return TRUE; } -#if 1 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; -#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); @@ -3446,20 +3268,12 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, return; } -#if 0 - Error(ERR_INFO, "opening '%s' succeeded ...", level_directory); -#endif - - 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); -#if 0 - Error(ERR_INFO, "checking entry '%s' ...", directory_name); -#endif - - /* skip entries for current and parent directory */ + // skip entries for current and parent directory if (strEqual(directory_name, ".") || strEqual(directory_name, "..")) { @@ -3468,29 +3282,13 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, continue; } -#if 1 - /* find out if directory entry is itself a directory */ - if (!dir_entry->is_directory) /* not a directory */ - { - 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 */ + // find out if directory entry is itself a directory + if (!dir_entry->is_directory) // not a directory { free(directory_path); continue; } -#endif free(directory_path); @@ -3506,88 +3304,10 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, 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) || - strEqual(directory_name, MUSIC_DIRECTORY)) - continue; - - valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent, - level_directory, - directory_name); - } - - closedir(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, "."); } @@ -3596,196 +3316,49 @@ static void LoadLevelInfoFromLevelDir(TreeInfo **node_first, Error(ERR_WARN, "cannot find any valid level series in directory '%s'", level_directory); } -#endif -boolean AdjustGraphicsForEMC() +boolean AdjustGraphicsForEMC(void) { boolean settings_changed = FALSE; - settings_changed |= adjustTreeGraphicsForEMC(leveldir_first_all); - settings_changed |= adjustTreeGraphicsForEMC(leveldir_first); - - return settings_changed; -} - -void LoadLevelInfo() -{ - InitUserLevelDirectory(getLoginName()); - - DrawInitText("Loading level series", 120, FC_GREEN); - - LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory); - LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL)); - - /* after loading all level set information, clone the level directory tree - and remove all level sets without levels (these may still contain artwork - to be offered in the setup menu as "custom artwork", and are therefore - checked for existing artwork in the function "LoadLevelArtworkInfo()") */ - leveldir_first_all = leveldir_first; - cloneTree(&leveldir_first, leveldir_first_all, TRUE); - - AdjustGraphicsForEMC(); - - /* before sorting, the first entries will be from the user directory */ - leveldir_current = getFirstValidTreeInfoEntry(leveldir_first); - - if (leveldir_first == NULL) - Error(ERR_EXIT, "cannot find any valid level series in any directory"); - - sortTreeInfo(&leveldir_first); - -#if 0 - dumpTreeInfo(leveldir_first, 0); -#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); + settings_changed |= adjustTreeGraphicsForEMC(leveldir_first_all); + settings_changed |= adjustTreeGraphicsForEMC(leveldir_first); - setString(&artwork_new->class_desc, - getLevelClassDescription(artwork_new)); - } - else - { - setString(&artwork_new->identifier, artwork_new->subdir); - } + return settings_changed; +} - setString(&artwork_new->name, artwork_new->identifier); - setString(&artwork_new->name_sorting, artwork_new->name); - } +void LoadLevelInfo(void) +{ + InitUserLevelDirectory(getLoginName()); -#if 0 - DrawInitText(artwork_new->name, 150, FC_YELLOW); -#endif + DrawInitText("Loading level series", 120, FC_GREEN); - pushTreeInfo(node_first, artwork_new); + LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory); + LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL)); - freeSetupFileHash(setup_file_hash); + leveldir_first = createTopTreeInfoNode(leveldir_first); - free(directory_path); - free(filename); + /* after loading all level set information, clone the level directory tree + and remove all level sets without levels (these may still contain artwork + to be offered in the setup menu as "custom artwork", and are therefore + checked for existing artwork in the function "LoadLevelArtworkInfo()") */ + leveldir_first_all = leveldir_first; + cloneTree(&leveldir_first, leveldir_first_all, TRUE); - return TRUE; -} + AdjustGraphicsForEMC(); -#else + // before sorting, the first entries will be from the user directory + leveldir_current = getFirstValidTreeInfoEntry(leveldir_first); + + if (leveldir_first == NULL) + Error(ERR_EXIT, "cannot find any valid level series in any directory"); + + sortTreeInfo(&leveldir_first); + +#if ENABLE_UNUSED_CODE + dumpTreeInfo(leveldir_first, 0); +#endif +} static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, TreeInfo *node_parent, @@ -3801,32 +3374,33 @@ 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 { - DIR *dir; - struct dirent *dir_entry; + Directory *dir; + DirectoryEntry *dir_entry; boolean valid_file_found = FALSE; - if ((dir = opendir(directory_path)) != NULL) + if ((dir = openDirectory(directory_path)) != NULL) { - while ((dir_entry = readdir(dir)) != NULL) + while ((dir_entry = readDirectory(dir)) != NULL) { - char *entry_name = dir_entry->d_name; - - if (FileIsArtworkType(entry_name, type)) + if (FileIsArtworkType(dir_entry->filename, type)) { valid_file_found = TRUE; + break; } } - closedir(dir); + closeDirectory(dir); } 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); @@ -3844,13 +3418,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") { -#if 0 - checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("...")); -#endif - - /* 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, @@ -3867,12 +3437,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); @@ -3881,12 +3451,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, ".")) { @@ -3901,7 +3471,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, @@ -3916,10 +3486,6 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, 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); @@ -3930,33 +3496,35 @@ static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first, return TRUE; } -#endif - -#if 1 - 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, "..")) { @@ -3965,29 +3533,17 @@ static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first, 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 */ + // skip directory entries which are not a directory + if (!dir_entry->is_directory) // not a directory { free(directory_path); continue; } -#endif 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); @@ -3995,67 +3551,7 @@ static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first, 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) -{ - DIR *dir; - struct dirent *dir_entry; - boolean valid_entry_found = FALSE; - - if ((dir = opendir(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 = readdir(dir)) != NULL) /* loop until last dir entry */ - { - struct stat file_status; - char *directory_name = dir_entry->d_name; - 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; - } - - /* skip directory entries which are not a directory or are not accessible */ - 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); - - /* check if this directory contains artwork with or without config file */ - valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent, - base_directory, - directory_name, type); - } - - closedir(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); @@ -4064,11 +3560,9 @@ static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first, base_directory); } -#endif - 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); @@ -4084,7 +3578,7 @@ static TreeInfo *getDummyArtworkInfo(int type) return artwork_new; } -void LoadArtworkInfo() +void LoadArtworkInfo(void) { LoadArtworkInfoCache(); @@ -4118,7 +3612,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) @@ -4147,7 +3641,7 @@ void LoadArtworkInfo() artwork.snd_current_identifier = artwork.snd_current->identifier; artwork.mus_current_identifier = artwork.mus_current->identifier; -#if 0 +#if ENABLE_UNUSED_CODE printf("graphics set == %s\n\n", artwork.gfx_current_identifier); printf("sounds set == %s\n\n", artwork.snd_current_identifier); printf("music set == %s\n\n", artwork.mus_current_identifier); @@ -4157,27 +3651,23 @@ void LoadArtworkInfo() sortTreeInfo(&artwork.snd_first); sortTreeInfo(&artwork.mus_first); -#if 0 +#if ENABLE_UNUSED_CODE dumpTreeInfo(artwork.gfx_first, 0); dumpTreeInfo(artwork.snd_first, 0); dumpTreeInfo(artwork.mus_first, 0); #endif } -void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node, - LevelDirTree *level_node) +static void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node, + LevelDirTree *level_node) { -#if 0 - static unsigned int progress_delay = 0; - unsigned int progress_delay_value = 100; /* (in milliseconds) */ -#endif 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); @@ -4195,7 +3685,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; @@ -4210,19 +3700,12 @@ 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); } -#if 1 - DrawInitTextExt(level_node->name, 150, FC_YELLOW, - level_node->level_group); -#else - if (level_node->level_group || - DelayReached(&progress_delay, progress_delay_value)) - DrawInitText(level_node->name, 150, FC_YELLOW); -#endif + DrawInitText(level_node->name, 150, FC_YELLOW); if (level_node->node_group != NULL) LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group); @@ -4231,7 +3714,7 @@ void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node, } } -void LoadLevelArtworkInfo() +void LoadLevelArtworkInfo(void) { print_timestamp_init("LoadLevelArtworkInfo"); @@ -4250,7 +3733,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)) { @@ -4293,7 +3776,7 @@ void LoadLevelArtworkInfo() print_timestamp_time("sortTreeInfo"); -#if 0 +#if ENABLE_UNUSED_CODE dumpTreeInfo(artwork.gfx_first, 0); dumpTreeInfo(artwork.snd_first, 0); dumpTreeInfo(artwork.mus_first, 0); @@ -4302,36 +3785,205 @@ void LoadLevelArtworkInfo() print_timestamp_done("LoadLevelArtworkInfo"); } -static void SaveUserLevelInfo() +static boolean AddUserLevelSetToLevelInfoExt(char *level_subdir_new) +{ + // get level info tree node of first (original) user level set + char *level_subdir_old = getLoginName(); + LevelDirTree *leveldir_old = getTreeInfoFromIdentifier(leveldir_first, + level_subdir_old); + if (leveldir_old == NULL) // should not happen + return FALSE; + + int draw_deactivation_mask = GetDrawDeactivationMask(); + + // override draw deactivation mask (temporarily disable drawing) + SetDrawDeactivationMask(REDRAW_ALL); + + // load new level set config and add it next to first user level set + LoadLevelInfoFromLevelConf(&leveldir_old->next, NULL, + leveldir_old->basepath, level_subdir_new); + + // set draw deactivation mask to previous value + SetDrawDeactivationMask(draw_deactivation_mask); + + // get level info tree node of newly added user level set + LevelDirTree *leveldir_new = getTreeInfoFromIdentifier(leveldir_first, + level_subdir_new); + if (leveldir_new == NULL) // should not happen + return FALSE; + + // correct top link and parent node link of newly created tree node + leveldir_new->node_top = leveldir_old->node_top; + leveldir_new->node_parent = leveldir_old->node_parent; + + // sort level info tree to adjust position of newly added level set + sortTreeInfo(&leveldir_first); + + return TRUE; +} + +void AddUserLevelSetToLevelInfo(char *level_subdir_new) +{ + if (!AddUserLevelSetToLevelInfoExt(level_subdir_new)) + Error(ERR_EXIT, "internal level set structure corrupted -- aborting"); +} + +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); + + return getTreeInfoFromIdentifier(artwork_first_node, artwork_set); +} + +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 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 (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:") && level_name != NULL) + fprintf(file_tmp, "%-32s%s\n", "name:", level_name); + else if (strPrefix(line, "author:") && level_author != NULL) + fprintf(file_tmp, "%-32s%s\n", "author:", level_author); + else if (strPrefix(line, "levels:") && num_levels != -1) + fprintf(file_tmp, "%-32s%d\n", "levels:", num_levels); + else + fputs(line, file_tmp); + } + + success = TRUE; + } + + if (file) + fclose(file); + + if (file_tmp) + fclose(file_tmp); + + if (success) + success = (rename(filename_tmp, filename) == 0); + + free(filename); + free(filename_tmp); + + return success; +} + +boolean CreateUserLevelSet(char *level_subdir, char *level_name, + char *level_author, int num_levels, + boolean use_artwork_set) { LevelDirTree *level_info; char *filename; FILE *file; int i; - filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME); + // create user level sub-directory, if needed + createDirectory(getUserLevelDir(level_subdir), "user level", PERMS_PRIVATE); + + filename = getPath2(getUserLevelDir(level_subdir), LEVELINFO_FILENAME); if (!(file = fopen(filename, MODE_WRITE))) { Error(ERR_WARN, "cannot write level info file '%s'", filename); free(filename); - return; + + return FALSE; } 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, getLoginName()); - setString(&level_info->author, getRealName()); - level_info->levels = 100; + setString(&level_info->name, level_name); + setString(&level_info->author, level_author); + level_info->levels = num_levels; 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; - fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, - getCookie("LEVELINFO"))); + fprintFileHeader(file, LEVELINFO_FILENAME); ldi = *level_info; for (i = 0; i < NUM_LEVELINFO_TOKENS; i++) @@ -4339,11 +3991,18 @@ static void SaveUserLevelInfo() if (i == LEVELINFO_TOKEN_NAME || i == LEVELINFO_TOKEN_AUTHOR || i == LEVELINFO_TOKEN_LEVELS || - i == LEVELINFO_TOKEN_FIRST_LEVEL) + i == LEVELINFO_TOKEN_FIRST_LEVEL || + i == LEVELINFO_TOKEN_SORT_PRIORITY || + 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"); } @@ -4355,6 +4014,13 @@ static void SaveUserLevelInfo() freeTreeInfo(level_info); free(filename); + + return TRUE; +} + +static void SaveUserLevelInfo(void) +{ + CreateUserLevelSet(getLoginName(), getLoginName(), getRealName(), 100, FALSE); } char *getSetupValue(int type, void *value) @@ -4411,6 +4077,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; @@ -4432,10 +4102,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) @@ -4443,11 +4113,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, " "); @@ -4460,24 +4130,25 @@ 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 defined(CREATE_SPECIAL_EDITION_RND_JUE) - leveldir_current = getTreeInfoFromIdentifier(leveldir_first, - "jue_start"); - if (leveldir_current == NULL) - leveldir_current = getFirstValidTreeInfoEntry(leveldir_first); -#endif + if (!strEqual(DEFAULT_LEVELSET, UNDEFINED_LEVELSET)) + { + leveldir_current = getTreeInfoFromIdentifier(leveldir_first, + DEFAULT_LEVELSET); + if (leveldir_current == NULL) + leveldir_current = getFirstValidTreeInfoEntry(leveldir_first); + } if ((level_setup_hash = loadSetupFileHash(filename))) { @@ -4489,22 +4160,21 @@ void LoadLevelSetup_LastSeries() if (leveldir_current == NULL) leveldir_current = getFirstValidTreeInfoEntry(leveldir_first); - checkSetupFileHashIdentifier(level_setup_hash, filename, - getCookie("LEVELSETUP")); - freeSetupFileHash(level_setup_hash); } else - Error(ERR_WARN, "using default setup values"); + { + Error(ERR_DEBUG, "using default setup values"); + } free(filename); } 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) @@ -4525,8 +4195,7 @@ static void SaveLevelSetup_LastSeries_Ext(boolean deactivate_last_level_series) return; } - fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, - getCookie("LEVELSETUP"))); + fprintFileHeader(file, LEVELSETUP_FILENAME); if (deactivate_last_level_series) fprintf(file, "# %s\n# ", "the following level set may have caused a problem and was deactivated"); @@ -4541,27 +4210,22 @@ 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); } -#if 1 - -static void checkSeriesInfo() +static void checkSeriesInfo(void) { static char *level_directory = NULL; Directory *dir; -#if 0 - DirectoryEntry *dir_entry; -#endif - /* 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) : @@ -4575,104 +4239,17 @@ static void checkSeriesInfo() 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' */ - - level_directory = getPath2((leveldir_current->in_user_dir ? - getUserLevelDir(NULL) : - options.level_directory), - leveldir_current->fullpath); - - 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 && - dir_entry->d_name[3] == '.' && - strEqual(&dir_entry->d_name[4], LEVELFILE_EXTENSION)) - { - char levelnum_str[4]; - int levelnum_value; - - strncpy(levelnum_str, dir_entry->d_name, 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 - - closedir(dir); -} - -#endif - -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++) @@ -4681,11 +4258,11 @@ void LoadLevelSetup_SeriesInfo() LevelStats_setSolved(i, 0); } - checkSeriesInfo(leveldir_current); + checkSeriesInfo(); - /* ----------------------------------------------------------------------- */ - /* ~/./levelsetup//levelsetup.conf */ - /* ----------------------------------------------------------------------- */ + // -------------------------------------------------------------------------- + // ~/./levelsetup//levelsetup.conf + // -------------------------------------------------------------------------- level_subdir = leveldir_current->subdir; @@ -4695,7 +4272,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); @@ -4709,7 +4286,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); @@ -4728,7 +4305,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) { @@ -4743,28 +4320,27 @@ 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) - checkSetupFileHashIdentifier(level_setup_hash, filename, - getCookie("LEVELSETUP")); - freeSetupFileHash(level_setup_hash); } else - Error(ERR_WARN, "using default setup values"); + { + Error(ERR_DEBUG, "using default setup values"); + } free(filename); } -void SaveLevelSetup_SeriesInfo() +void SaveLevelSetup_SeriesInfo(void) { char *filename; char *level_subdir = leveldir_current->subdir; @@ -4773,9 +4349,9 @@ void SaveLevelSetup_SeriesInfo() FILE *file; int i; - /* ----------------------------------------------------------------------- */ - /* ~/./levelsetup//levelsetup.conf */ - /* ----------------------------------------------------------------------- */ + // -------------------------------------------------------------------------- + // ~/./levelsetup//levelsetup.conf + // -------------------------------------------------------------------------- InitLevelSetupDirectory(level_subdir); @@ -4788,8 +4364,8 @@ void SaveLevelSetup_SeriesInfo() return; } - fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER, - getCookie("LEVELSETUP"))); + fprintFileHeader(file, LEVELSETUP_FILENAME); + fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL, level_nr_str)); fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,