#include "platform.h"
#include "setup.h"
+#include "sound.h"
#include "joystick.h"
#include "text.h"
#include "misc.h"
#define TOKEN_VALUE_POSITION_DEFAULT 40
#define TOKEN_COMMENT_POSITION_DEFAULT 60
-#define MAX_COOKIE_LEN 256
-
#define TREE_NODE_TYPE_DEFAULT 0
#define TREE_NODE_TYPE_PARENT 1
#define TREE_NODE_TYPE_GROUP 2
static SetupFileHash *artworkinfo_cache_old = NULL;
static SetupFileHash *artworkinfo_cache_new = NULL;
static SetupFileHash *optional_tokens_hash = NULL;
+static SetupFileHash *missing_file_hash = NULL;
static boolean use_artworkinfo_cache = TRUE;
static boolean update_artworkinfo_cache = FALSE;
// file functions
// ----------------------------------------------------------------------------
+static void WarnUsingFallback(char *filename)
+{
+ if (getHashEntry(missing_file_hash, filename) == NULL)
+ {
+ setHashEntry(missing_file_hash, filename, "");
+
+ Debug("setup", "cannot find artwork file '%s' (using fallback)", filename);
+ }
+}
+
static char *getLevelClassDescription(TreeInfo *ti)
{
int position = ti->sort_priority / 100;
return score_tape_dir;
}
+static char *getScoreCacheTapeDir(char *level_subdir, int nr)
+{
+ static char *score_cache_tape_dir = NULL;
+ char tape_subdir[MAX_FILENAME_LEN];
+
+ checked_free(score_cache_tape_dir);
+
+ sprintf(tape_subdir, "%03d", nr);
+ score_cache_tape_dir = getPath2(getScoreCacheDir(level_subdir), tape_subdir);
+
+ return score_cache_tape_dir;
+}
+
static char *getUserSubdir(int nr)
{
static char user_subdir[16] = { 0 };
set the current working directory to the program package directory) */
char *main_data_path = getBasePath(command_filename);
-#if defined(PLATFORM_MACOSX)
+#if defined(PLATFORM_MAC)
if (strSuffix(main_data_path, MAC_APP_BINARY_SUBDIR))
{
char *main_data_path_old = main_data_path;
return config_filename_3;
}
+static char *getPlatformConfigFilename(char *config_filename)
+{
+ static char *platform_config_filename = NULL;
+ static boolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ char *config_basepath = getBasePath(config_filename);
+ char *config_basename = getBaseNameNoSuffix(config_filename);
+ char *config_filename_prefix = getPath2(config_basepath, config_basename);
+ char *platform_string_lower = getStringToLower(PLATFORM_STRING);
+ char *platform_suffix = getStringCat2("-", platform_string_lower);
+
+ platform_config_filename = getStringCat3(config_filename_prefix,
+ platform_suffix, ".conf");
+
+ checked_free(config_basepath);
+ checked_free(config_basename);
+ checked_free(config_filename_prefix);
+ checked_free(platform_string_lower);
+ checked_free(platform_suffix);
+
+ initialized = TRUE;
+ }
+
+ return platform_config_filename;
+}
+
char *getTapeFilename(int nr)
{
static char *filename = NULL;
return filename;
}
+char *getScoreCacheTapeFilename(char *basename_no_ext, int nr)
+{
+ static char *filename = NULL;
+ char basename[MAX_FILENAME_LEN];
+
+ checked_free(filename);
+
+ sprintf(basename, "%s.%s", basename_no_ext, TAPEFILE_EXTENSION);
+
+ // used instead of "leveldir_current->subdir" (for network games)
+ filename = getPath2(getScoreCacheTapeDir(levelset.identifier, nr), basename);
+
+ return filename;
+}
+
char *getSetupFilename(void)
{
static char *filename = NULL;
return program.config_filename;
}
+char *getPlatformSetupFilename(void)
+{
+ return getPlatformConfigFilename(program.config_filename);
+}
+
char *getEditorSetupFilename(void)
{
static char *filename = NULL;
return filename;
}
-char *getLevelSetInfoFilename(void)
+static char *getLevelSetInfoBasename(int nr)
{
+ static char basename[32];
+
+ sprintf(basename, "levelset_%d.txt", nr + 1);
+
+ return basename;
+}
+
+char *getLevelSetInfoFilename(int nr)
+{
+ char *basename = getLevelSetInfoBasename(nr);
+ static char *info_subdir = NULL;
static char *filename = NULL;
+
+ if (info_subdir == NULL)
+ info_subdir = getPath2(DOCS_DIRECTORY, LEVELSET_INFO_DIRECTORY);
+
+ checked_free(filename);
+
+ // look for level set info file the current level set directory
+ filename = getPath3(getCurrentLevelDir(), info_subdir, basename);
+ if (fileExists(filename))
+ return filename;
+
+ if (nr > 0)
+ return NULL;
+
char *basenames[] =
{
"README",
return NULL; // cannot find specified artwork file anywhere
}
+static char *getCreditsBasename(int nr)
+{
+ static char basename[32];
+
+ sprintf(basename, "credits_%d.txt", nr + 1);
+
+ return basename;
+}
+
+char *getCreditsFilename(int nr, boolean global)
+{
+ char *basename = getCreditsBasename(nr);
+ char *basepath = NULL;
+ static char *credits_subdir = NULL;
+ static char *filename = NULL;
+
+ if (credits_subdir == NULL)
+ credits_subdir = getPath2(DOCS_DIRECTORY, CREDITS_DIRECTORY);
+
+ checked_free(filename);
+
+ // look for credits file in the game's base or current level set directory
+ basepath = (global ? options.base_directory : getCurrentLevelDir());
+
+ filename = getPath3(basepath, credits_subdir, basename);
+ if (fileExists(filename))
+ return filename;
+
+ return NULL; // cannot find credits file
+}
+
+static char *getProgramInfoBasename(int nr)
+{
+ static char basename[32];
+
+ sprintf(basename, "program_%d.txt", nr + 1);
+
+ return basename;
+}
+
+char *getProgramInfoFilename(int nr)
+{
+ char *basename = getProgramInfoBasename(nr);
+ static char *info_subdir = NULL;
+ static char *filename = NULL;
+
+ if (info_subdir == NULL)
+ info_subdir = getPath2(DOCS_DIRECTORY, PROGRAM_INFO_DIRECTORY);
+
+ checked_free(filename);
+
+ // look for program info file in the game's base directory
+ filename = getPath3(options.base_directory, info_subdir, basename);
+ if (fileExists(filename))
+ return filename;
+
+ return NULL; // cannot find program info file
+}
+
static char *getCorrectedArtworkBasename(char *basename)
{
return basename;
{
free(filename);
- Warn("cannot find artwork file '%s' (using fallback)", basename);
+ WarnUsingFallback(basename);
// 6th try: look for fallback artwork in old default artwork directory
// (needed to prevent errors when trying to access unused artwork files)
{
free(filename);
- Warn("cannot find artwork file '%s' (using fallback)", basename);
+ WarnUsingFallback(basename);
// 6th try: look for fallback artwork in old default artwork directory
// (needed to prevent errors when trying to access unused artwork files)
{
free(filename);
- Warn("cannot find artwork file '%s' (using fallback)", basename);
+ WarnUsingFallback(basename);
// 6th try: look for fallback artwork in old default artwork directory
// (needed to prevent errors when trying to access unused artwork files)
return filename;
}
-char *getCustomMusicDirectory(void)
+static boolean directoryExists_CheckMusic(char *directory, boolean check_music)
+{
+ if (!directoryExists(directory))
+ return FALSE;
+
+ if (!check_music)
+ return TRUE;
+
+ Directory *dir;
+ DirectoryEntry *dir_entry;
+ int num_music = getMusicListSize();
+ boolean music_found = FALSE;
+
+ if ((dir = openDirectory(directory)) == NULL)
+ return FALSE;
+
+ while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
+ {
+ char *basename = dir_entry->basename;
+ boolean music_already_used = FALSE;
+ int i;
+
+ // skip all music files that are configured in music config file
+ for (i = 0; i < num_music; i++)
+ {
+ struct FileInfo *music = getMusicListEntry(i);
+
+ if (strEqual(basename, music->filename))
+ {
+ music_already_used = TRUE;
+
+ break;
+ }
+ }
+
+ if (music_already_used)
+ continue;
+
+ if (FileIsMusic(dir_entry->filename))
+ {
+ music_found = TRUE;
+
+ break;
+ }
+ }
+
+ closeDirectory(dir);
+
+ return music_found;
+}
+
+static char *getCustomMusicDirectoryExt(boolean check_music)
{
static char *directory = NULL;
boolean skip_setup_artwork = FALSE;
{
// 1st try: look for special artwork in current level series directory
directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
- if (directoryExists(directory))
+ if (directoryExists_CheckMusic(directory, check_music))
return directory;
free(directory);
{
// 2nd try: look for special artwork configured in level series config
directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
- if (directoryExists(directory))
+
+ // directory also valid if no unconfigured music found (no game music)
+ if (directoryExists_CheckMusic(directory, FALSE))
return directory;
free(directory);
{
// 3rd try: look for special artwork in configured artwork directory
directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
- if (directoryExists(directory))
+
+ // directory also valid if no unconfigured music found (no game music)
+ if (directoryExists_CheckMusic(directory, FALSE))
return directory;
free(directory);
// 4th try: look for default artwork in new default artwork directory
directory = getStringCopy(getDefaultMusicDir(MUS_DEFAULT_SUBDIR));
- if (directoryExists(directory))
+ if (directoryExists_CheckMusic(directory, check_music))
return directory;
free(directory);
// 5th try: look for default artwork in old default artwork directory
directory = getStringCopy(options.music_directory);
- if (directoryExists(directory))
+ if (directoryExists_CheckMusic(directory, check_music))
return directory;
return NULL; // cannot find specified artwork file anywhere
}
+char *getCustomMusicDirectory(void)
+{
+ return getCustomMusicDirectoryExt(FALSE);
+}
+
+char *getCustomMusicDirectory_NoConf(void)
+{
+ return getCustomMusicDirectoryExt(TRUE);
+}
+
+void MarkTapeDirectoryUploadsAsComplete(char *level_subdir)
+{
+ char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+
+ touchFile(filename);
+
+ checked_free(filename);
+}
+
+void MarkTapeDirectoryUploadsAsIncomplete(char *level_subdir)
+{
+ char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+
+ unlink(filename);
+
+ checked_free(filename);
+}
+
+boolean CheckTapeDirectoryUploadsComplete(char *level_subdir)
+{
+ char *filename = getPath2(getTapeDir(level_subdir), UPLOADED_FILENAME);
+ boolean success = fileExists(filename);
+
+ checked_free(filename);
+
+ return success;
+}
+
+void InitMissingFileHash(void)
+{
+ if (missing_file_hash == NULL)
+ freeSetupFileHash(missing_file_hash);
+
+ missing_file_hash = newSetupFileHash();
+}
+
void InitTapeDirectory(char *level_subdir)
{
- createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
- createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
- createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
+ boolean new_tape_dir = !directoryExists(getTapeDir(level_subdir));
+
+ createDirectory(getUserGameDataDir(), "user data");
+ createDirectory(getTapeDir(NULL), "main tape");
+ createDirectory(getTapeDir(level_subdir), "level tape");
+
+ if (new_tape_dir)
+ MarkTapeDirectoryUploadsAsComplete(level_subdir);
}
void InitScoreDirectory(char *level_subdir)
{
- createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
- createDirectory(getScoreDir(NULL), "main score", PERMS_PRIVATE);
- createDirectory(getScoreDir(level_subdir), "level score", PERMS_PRIVATE);
+ createDirectory(getMainUserGameDataDir(), "main user data");
+ createDirectory(getScoreDir(NULL), "main score");
+ createDirectory(getScoreDir(level_subdir), "level score");
}
void InitScoreCacheDirectory(char *level_subdir)
{
- createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
- createDirectory(getCacheDir(), "cache data", PERMS_PRIVATE);
- createDirectory(getScoreCacheDir(NULL), "main score", PERMS_PRIVATE);
- createDirectory(getScoreCacheDir(level_subdir), "level score", PERMS_PRIVATE);
+ createDirectory(getMainUserGameDataDir(), "main user data");
+ createDirectory(getCacheDir(), "cache data");
+ createDirectory(getScoreCacheDir(NULL), "main score");
+ createDirectory(getScoreCacheDir(level_subdir), "level score");
}
void InitScoreTapeDirectory(char *level_subdir, int nr)
{
InitScoreDirectory(level_subdir);
- createDirectory(getScoreTapeDir(level_subdir, nr), "score tape", PERMS_PRIVATE);
+ createDirectory(getScoreTapeDir(level_subdir, nr), "score tape");
+}
+
+void InitScoreCacheTapeDirectory(char *level_subdir, int nr)
+{
+ InitScoreCacheDirectory(level_subdir);
+
+ createDirectory(getScoreCacheTapeDir(level_subdir, nr), "score tape");
}
static void SaveUserLevelInfo(void);
{
if (!directoryExists(getUserLevelDir(level_subdir)))
{
- createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
- createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
- createDirectory(getUserLevelDir(level_subdir), "user level", PERMS_PRIVATE);
+ createDirectory(getMainUserGameDataDir(), "main user data");
+ createDirectory(getUserLevelDir(NULL), "main user level");
if (setup.internal.create_user_levelset)
+ {
+ createDirectory(getUserLevelDir(level_subdir), "user level");
+
SaveUserLevelInfo();
+ }
}
}
{
if (!directoryExists(getNetworkLevelDir(level_subdir)))
{
- createDirectory(getMainUserGameDataDir(), "main 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);
+ createDirectory(getMainUserGameDataDir(), "main user data");
+ createDirectory(getNetworkDir(), "network data");
+ createDirectory(getNetworkLevelDir(NULL), "main network level");
+ createDirectory(getNetworkLevelDir(level_subdir), "network level");
}
}
void InitLevelSetupDirectory(char *level_subdir)
{
- createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
- createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
- createDirectory(getLevelSetupDir(level_subdir), "level setup", PERMS_PRIVATE);
+ createDirectory(getUserGameDataDir(), "user data");
+ createDirectory(getLevelSetupDir(NULL), "main level setup");
+ createDirectory(getLevelSetupDir(level_subdir), "level setup");
}
static void InitCacheDirectory(void)
{
- createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
- createDirectory(getCacheDir(), "cache data", PERMS_PRIVATE);
+ createDirectory(getMainUserGameDataDir(), "main user data");
+ createDirectory(getCacheDir(), "cache data");
}
*ti_new = ti_cloned;
}
-static boolean adjustTreeGraphicsForEMC(TreeInfo *node)
+static boolean adjustTreeArtworkForEMC(char **artwork_set_1,
+ char **artwork_set_2,
+ char **artwork_set, boolean prefer_2)
{
- boolean settings_changed = FALSE;
+ // do nothing if neither special artwork set 1 nor 2 are defined
+ if (!*artwork_set_1 && !*artwork_set_2)
+ return FALSE;
- while (node)
+ boolean want_1 = (prefer_2 == FALSE);
+ boolean want_2 = (prefer_2 == TRUE);
+ boolean has_only_1 = (!*artwork_set && !*artwork_set_2);
+ boolean has_only_2 = (!*artwork_set && !*artwork_set_1);
+ char *artwork_set_new = NULL;
+
+ // replace missing special artwork 1 or 2 with (optional) standard artwork
+
+ if (!*artwork_set_1)
+ setString(artwork_set_1, *artwork_set);
+
+ if (!*artwork_set_2)
+ setString(artwork_set_2, *artwork_set);
+
+ // set standard artwork to either special artwork 1 or 2, as requested
+
+ if (*artwork_set_1 && (want_1 || has_only_1))
+ artwork_set_new = *artwork_set_1;
+
+ if (*artwork_set_2 && (want_2 || has_only_2))
+ artwork_set_new = *artwork_set_2;
+
+ if (artwork_set_new && !strEqual(*artwork_set, artwork_set_new))
{
- boolean want_ecs = (setup.prefer_aga_graphics == FALSE);
- boolean want_aga = (setup.prefer_aga_graphics == TRUE);
- boolean has_only_ecs = (!node->graphics_set && !node->graphics_set_aga);
- boolean has_only_aga = (!node->graphics_set && !node->graphics_set_ecs);
- char *graphics_set = NULL;
+ setString(artwork_set, artwork_set_new);
- if (node->graphics_set_ecs && (want_ecs || has_only_ecs))
- graphics_set = node->graphics_set_ecs;
+ return TRUE;
+ }
- if (node->graphics_set_aga && (want_aga || has_only_aga))
- graphics_set = node->graphics_set_aga;
+ return FALSE;
+}
- if (graphics_set && !strEqual(node->graphics_set, graphics_set))
- {
- setString(&node->graphics_set, graphics_set);
- settings_changed = TRUE;
- }
+static boolean adjustTreeGraphicsForEMC(TreeInfo *node)
+{
+ boolean settings_changed = FALSE;
+ while (node)
+ {
+ settings_changed |= adjustTreeArtworkForEMC(&node->graphics_set_ecs,
+ &node->graphics_set_aga,
+ &node->graphics_set,
+ setup.prefer_aga_graphics);
if (node->node_group != NULL)
settings_changed |= adjustTreeGraphicsForEMC(node->node_group);
while (node)
{
- boolean want_default = (setup.prefer_lowpass_sounds == FALSE);
- boolean want_lowpass = (setup.prefer_lowpass_sounds == TRUE);
- boolean has_only_default = (!node->sounds_set && !node->sounds_set_lowpass);
- boolean has_only_lowpass = (!node->sounds_set && !node->sounds_set_default);
- char *sounds_set = NULL;
-
- if (node->sounds_set_default && (want_default || has_only_default))
- sounds_set = node->sounds_set_default;
-
- if (node->sounds_set_lowpass && (want_lowpass || has_only_lowpass))
- sounds_set = node->sounds_set_lowpass;
-
- if (sounds_set && !strEqual(node->sounds_set, sounds_set))
- {
- setString(&node->sounds_set, sounds_set);
- settings_changed = TRUE;
- }
-
+ settings_changed |= adjustTreeArtworkForEMC(&node->sounds_set_default,
+ &node->sounds_set_lowpass,
+ &node->sounds_set,
+ setup.prefer_lowpass_sounds);
if (node->node_group != NULL)
settings_changed |= adjustTreeSoundsForEMC(node->node_group);
// some stuff from "files.c"
// ============================================================================
-#if defined(PLATFORM_WIN32)
+#if defined(PLATFORM_WINDOWS)
#ifndef S_IRGRP
#define S_IRGRP S_IRUSR
#endif
#ifndef S_ISGID
#define S_ISGID 0
#endif
-#endif // PLATFORM_WIN32
+#endif // PLATFORM_WINDOWS
// file permissions for newly written files
#define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
{
static char *dir = NULL;
-#if defined(PLATFORM_WIN32)
+#if defined(PLATFORM_WINDOWS)
if (dir == NULL)
{
dir = checked_malloc(MAX_PATH + 1);
strcpy(dir, ".");
}
#elif defined(PLATFORM_EMSCRIPTEN)
- dir = "/persistent";
+ dir = PERSISTENT_DIRECTORY;
#elif defined(PLATFORM_UNIX)
if (dir == NULL)
{
{
static char *personal_data_dir = NULL;
-#if defined(PLATFORM_MACOSX)
+#if defined(PLATFORM_MAC)
if (personal_data_dir == NULL)
personal_data_dir = getPath2(getHomeDir(), "Documents");
#else
static int posix_mkdir(const char *pathname, mode_t mode)
{
-#if defined(PLATFORM_WIN32)
+#if defined(PLATFORM_WINDOWS)
return mkdir(pathname);
#else
return mkdir(pathname, mode);
#endif
}
-void createDirectory(char *dir, char *text, int permission_class)
+void createDirectory(char *dir, char *text)
{
if (directoryExists(dir))
return;
// leave "other" permissions in umask untouched, but ensure group parts
// of USERDATA_DIR_MODE are not masked
+ int permission_class = PERMS_PRIVATE;
mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
mode_t last_umask = posix_umask(0);
void InitMainUserDataDirectory(void)
{
- createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
+ createDirectory(getMainUserGameDataDir(), "main user data");
}
void InitUserDataDirectory(void)
{
- createDirectory(getMainUserGameDataDir(), "main user data", PERMS_PRIVATE);
+ createDirectory(getMainUserGameDataDir(), "main user data");
if (user.nr != 0)
{
- createDirectory(getUserDir(-1), "users", PERMS_PRIVATE);
- createDirectory(getUserDir(user.nr), "user data", PERMS_PRIVATE);
+ createDirectory(getUserDir(-1), "users");
+ createDirectory(getUserDir(user.nr), "user data");
}
}
chmod(filename, perms);
}
-char *getCookie(char *file_type)
-{
- static char cookie[MAX_COOKIE_LEN + 1];
-
- if (strlen(program.cookie_prefix) + 1 +
- strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
- return "[COOKIE ERROR]"; // should never happen
-
- sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
- program.cookie_prefix, file_type,
- program.version_super, program.version_major);
-
- return cookie;
-}
-
void fprintFileHeader(FILE *file, char *basename)
{
char *prefix = "# ";
return hash;
}
-static int keys_are_equal(void *key1, void *key2)
+int hash_keys_are_equal(void *key1, void *key2)
{
return (strEqual((char *)key1, (char *)key2));
}
SetupFileHash *newSetupFileHash(void)
{
SetupFileHash *new_hash =
- create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
+ create_hashtable(16, 0.75, get_hash_from_key, hash_keys_are_equal);
if (new_hash == NULL)
Fail("create_hashtable() failed -- out of memory");
// ============================================================================
#define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
+#define TOKEN_STR_LAST_PLAYED_MENU_USED "last_played_menu_used"
#define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
#define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
#define TOKEN_STR_LAST_USER "last_user"
#define LEVELINFO_TOKEN_FILENAME 24
#define LEVELINFO_TOKEN_FILETYPE 25
#define LEVELINFO_TOKEN_SPECIAL_FLAGS 26
-#define LEVELINFO_TOKEN_HANDICAP 27
-#define LEVELINFO_TOKEN_SKIP_LEVELS 28
-#define LEVELINFO_TOKEN_USE_EMC_TILES 29
+#define LEVELINFO_TOKEN_EMPTY_LEVEL_NAME 27
+#define LEVELINFO_TOKEN_FORCE_LEVEL_NAME 28
+#define LEVELINFO_TOKEN_HANDICAP 29
+#define LEVELINFO_TOKEN_TIME_LIMIT 30
+#define LEVELINFO_TOKEN_SKIP_LEVELS 31
+#define LEVELINFO_TOKEN_USE_EMC_TILES 32
+#define LEVELINFO_TOKEN_INFO_SCREENS_FROM_MAIN 33
-#define NUM_LEVELINFO_TOKENS 30
+#define NUM_LEVELINFO_TOKENS 34
static LevelDirTree ldi;
{ TYPE_STRING, &ldi.level_filename, "filename" },
{ TYPE_STRING, &ldi.level_filetype, "filetype" },
{ TYPE_STRING, &ldi.special_flags, "special_flags" },
+ { TYPE_STRING, &ldi.empty_level_name, "empty_level_name" },
+ { TYPE_BOOLEAN, &ldi.force_level_name, "force_level_name" },
{ TYPE_BOOLEAN, &ldi.handicap, "handicap" },
+ { TYPE_BOOLEAN, &ldi.time_limit, "time_limit" },
{ TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" },
- { TYPE_BOOLEAN, &ldi.use_emc_tiles, "use_emc_tiles" }
+ { TYPE_BOOLEAN, &ldi.use_emc_tiles, "use_emc_tiles" },
+ { TYPE_BOOLEAN, &ldi.info_screens_from_main, "info_screens_from_main" }
};
static struct TokenInfo artworkinfo_tokens[] =
ti->special_flags = NULL;
+ ti->empty_level_name = NULL;
+ ti->force_level_name = FALSE;
+
ti->levels = 0;
ti->first_level = 0;
ti->last_level = 0;
ti->handicap_level = 0;
ti->readonly = TRUE;
ti->handicap = TRUE;
+ ti->time_limit = TRUE;
ti->skip_levels = FALSE;
ti->use_emc_tiles = FALSE;
+ ti->info_screens_from_main = FALSE;
}
}
ti->special_flags = getStringCopy(parent->special_flags);
+ ti->empty_level_name = getStringCopy(parent->empty_level_name);
+ ti->force_level_name = parent->force_level_name;
+
ti->levels = parent->levels;
ti->first_level = parent->first_level;
ti->last_level = parent->last_level;
ti->handicap_level = parent->handicap_level;
ti->readonly = parent->readonly;
ti->handicap = parent->handicap;
+ ti->time_limit = parent->time_limit;
ti->skip_levels = parent->skip_levels;
ti->use_emc_tiles = parent->use_emc_tiles;
+ ti->info_screens_from_main = parent->info_screens_from_main;
}
}
ti_copy->special_flags = getStringCopy(ti->special_flags);
+ ti_copy->empty_level_name = getStringCopy(ti->empty_level_name);
+ ti_copy->force_level_name = ti->force_level_name;
+
ti_copy->levels = ti->levels;
ti_copy->first_level = ti->first_level;
ti_copy->last_level = ti->last_level;
ti_copy->user_defined = ti->user_defined;
ti_copy->readonly = ti->readonly;
ti_copy->handicap = ti->handicap;
+ ti_copy->time_limit = ti->time_limit;
ti_copy->skip_levels = ti->skip_levels;
ti_copy->use_emc_tiles = ti->use_emc_tiles;
+ ti_copy->info_screens_from_main = ti->info_screens_from_main;
ti_copy->color = ti->color;
ti_copy->class_desc = getStringCopy(ti->class_desc);
}
}
+TreeInfo *addTopTreeInfoNode(TreeInfo *node_first)
+{
+ // add top tree node with back link node in previous tree
+ node_first = createTopTreeInfoNode(node_first);
+
+ // set all parent links (back links) in complete tree
+ setTreeInfoParentNodes(node_first, NULL);
+
+ return node_first;
+}
+
// ----------------------------------------------------------------------------
// functions for handling level and custom artwork info cache
level_directory, ".");
}
- if (!valid_entry_found)
+ boolean valid_entry_expected =
+ (strEqual(level_directory, options.level_directory) ||
+ setup.internal.create_user_levelset);
+
+ if (valid_entry_expected && !valid_entry_found)
Warn("cannot find any valid level series in directory '%s'",
- level_directory);
+ level_directory);
}
boolean AdjustGraphicsForEMC(void)
LoadArtworkInfoFromLevelInfoExt(artwork_node, NULL, leveldir_first_all, TRUE);
LoadArtworkInfoFromLevelInfoExt(artwork_node, NULL, leveldir_first_all, FALSE);
- // add top tree node over all three separate sub-trees
- *artwork_node = createTopTreeInfoNode(*artwork_node);
-
- // set all parent links (back links) in complete artwork tree
- setTreeInfoParentNodes(*artwork_node, NULL);
+ // add top tree node over all sub-trees and set parent links
+ *artwork_node = addTopTreeInfoNode(*artwork_node);
}
void LoadLevelArtworkInfo(void)
TreeInfo *tree_node_new = getTreeInfoFromIdentifier(*tree_node_first,
tree_subdir_new);
+ // if not found, check if added node is level group or artwork group
+ if (tree_node_new == NULL)
+ tree_node_new = getTreeInfoFromIdentifierExt(*tree_node_first,
+ tree_subdir_new,
+ TREE_NODE_TYPE_GROUP);
+
if (tree_node_new == NULL) // should not happen
return FALSE;
int i;
// create user level sub-directory, if needed
- createDirectory(getUserLevelDir(level_subdir), "user level", PERMS_PRIVATE);
+ createDirectory(getUserLevelDir(level_subdir), "user level");
filename = getPath2(getUserLevelDir(level_subdir), LEVELINFO_FILENAME);
setString(&last_level_series[0], leveldir_current->identifier);
}
-static TreeInfo *StoreOrRestoreLastPlayedLevels(TreeInfo *node, boolean store)
+#define LAST_PLAYED_MODE_SET 1
+#define LAST_PLAYED_MODE_SET_FORCED 2
+#define LAST_PLAYED_MODE_GET 3
+
+static TreeInfo *StoreOrRestoreLastPlayedLevels(TreeInfo *node, int mode)
{
static char *identifier = NULL;
- if (store)
+ if (mode == LAST_PLAYED_MODE_SET)
{
setString(&identifier, (node && node->is_copy ? node->identifier : NULL));
-
- return NULL; // not used
}
- else
+ else if (mode == LAST_PLAYED_MODE_SET_FORCED)
+ {
+ setString(&identifier, (node ? node->identifier : NULL));
+ }
+ else if (mode == LAST_PLAYED_MODE_GET)
{
TreeInfo *node_new = getTreeInfoFromIdentifierExt(leveldir_first,
identifier,
TREE_NODE_TYPE_COPY);
return (node_new != NULL ? node_new : node);
}
+
+ return NULL; // not used
}
void StoreLastPlayedLevels(TreeInfo *node)
{
- StoreOrRestoreLastPlayedLevels(node, TRUE);
+ StoreOrRestoreLastPlayedLevels(node, LAST_PLAYED_MODE_SET);
+}
+
+void ForcedStoreLastPlayedLevels(TreeInfo *node)
+{
+ StoreOrRestoreLastPlayedLevels(node, LAST_PLAYED_MODE_SET_FORCED);
}
void RestoreLastPlayedLevels(TreeInfo **node)
{
- *node = StoreOrRestoreLastPlayedLevels(*node, FALSE);
+ *node = StoreOrRestoreLastPlayedLevels(*node, LAST_PLAYED_MODE_GET);
+}
+
+boolean CheckLastPlayedLevels(void)
+{
+ return (StoreOrRestoreLastPlayedLevels(NULL, LAST_PLAYED_MODE_GET) != NULL);
}
void LoadLevelSetup_LastSeries(void)
if (leveldir_current == NULL)
leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
+ char *last_played_menu_used =
+ getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_MENU_USED);
+
+ // store if last level set was selected from "last played" menu
+ if (strEqual(last_played_menu_used, "true"))
+ ForcedStoreLastPlayedLevels(leveldir_current);
+
for (i = 0; i < MAX_LEVELDIR_HISTORY; i++)
{
char token[strlen(TOKEN_STR_LAST_LEVEL_SERIES) + 10];
fprintf(file, "# %s\n# ", "the following level set may have caused a problem and was deactivated");
fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
- leveldir_current->identifier));
+ leveldir_current->identifier));
+
+ // store if last level set was selected from "last played" menu
+ boolean last_played_menu_used = CheckLastPlayedLevels();
+ char *setup_value = getSetupValue(TYPE_BOOLEAN, &last_played_menu_used);
+
+ fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_MENU_USED,
+ setup_value));
for (i = 0; last_level_series[i] != NULL; i++)
{