#include "text.h"
#include "misc.h"
#include "hash.h"
+#include "zip/miniunz.h"
#define ENABLE_UNUSED_CODE FALSE // for currently unused functions
return network_dir;
}
-static char *getLevelDirFromTreeInfo(TreeInfo *node)
+char *getLevelDirFromTreeInfo(TreeInfo *node)
{
static char *level_dir = NULL;
getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
}
-static char *getUserGraphicsDir(void)
+char *getUserGraphicsDir(void)
{
static char *usergraphics_dir = NULL;
return usergraphics_dir;
}
-static char *getUserSoundsDir(void)
+char *getUserSoundsDir(void)
{
static char *usersounds_dir = NULL;
return usersounds_dir;
}
-static char *getUserMusicDir(void)
+char *getUserMusicDir(void)
{
static char *usermusic_dir = NULL;
// functions for loading level info and custom artwork info
// ----------------------------------------------------------------------------
+int GetZipFileTreeType(char *zip_filename)
+{
+ static char *top_dir_path = NULL;
+ static char *top_dir_conf_filename[NUM_BASE_TREE_TYPES] = { NULL };
+ static char *conf_basename[NUM_BASE_TREE_TYPES] =
+ {
+ GRAPHICSINFO_FILENAME,
+ SOUNDSINFO_FILENAME,
+ MUSICINFO_FILENAME,
+ LEVELINFO_FILENAME
+ };
+ int j;
+
+ checked_free(top_dir_path);
+ top_dir_path = NULL;
+
+ for (j = 0; j < NUM_BASE_TREE_TYPES; j++)
+ {
+ checked_free(top_dir_conf_filename[j]);
+ top_dir_conf_filename[j] = NULL;
+ }
+
+ char **zip_entries = zip_list(zip_filename);
+
+ // check if zip file successfully opened
+ if (zip_entries == NULL || zip_entries[0] == NULL)
+ return TREE_TYPE_UNDEFINED;
+
+ // first zip file entry is expected to be top level directory
+ char *top_dir = zip_entries[0];
+
+ // check if valid top level directory found in zip file
+ if (!strSuffix(top_dir, "/"))
+ return TREE_TYPE_UNDEFINED;
+
+ // get filenames of valid configuration files in top level directory
+ for (j = 0; j < NUM_BASE_TREE_TYPES; j++)
+ top_dir_conf_filename[j] = getStringCat2(top_dir, conf_basename[j]);
+
+ int tree_type = TREE_TYPE_UNDEFINED;
+ int e = 0;
+
+ while (zip_entries[e] != NULL)
+ {
+ // check if every zip file entry is below top level directory
+ if (!strPrefix(zip_entries[e], top_dir))
+ return TREE_TYPE_UNDEFINED;
+
+ // check if this zip file entry is a valid configuration filename
+ for (j = 0; j < NUM_BASE_TREE_TYPES; j++)
+ {
+ if (strEqual(zip_entries[e], top_dir_conf_filename[j]))
+ {
+ // only exactly one valid configuration file allowed
+ if (tree_type != TREE_TYPE_UNDEFINED)
+ return TREE_TYPE_UNDEFINED;
+
+ tree_type = j;
+ }
+ }
+
+ e++;
+ }
+
+ return tree_type;
+}
+
+static boolean CheckZipFileForDirectory(char *zip_filename, char *directory,
+ int tree_type)
+{
+ static char *top_dir_path = NULL;
+ static char *top_dir_conf_filename = NULL;
+
+ checked_free(top_dir_path);
+ checked_free(top_dir_conf_filename);
+
+ top_dir_path = NULL;
+ top_dir_conf_filename = NULL;
+
+ char *conf_basename = (tree_type == TREE_TYPE_LEVEL_DIR ? LEVELINFO_FILENAME :
+ ARTWORKINFO_FILENAME(tree_type));
+
+ // check if valid configuration filename determined
+ if (conf_basename == NULL || strEqual(conf_basename, ""))
+ return FALSE;
+
+ char **zip_entries = zip_list(zip_filename);
+
+ // check if zip file successfully opened
+ if (zip_entries == NULL || zip_entries[0] == NULL)
+ return FALSE;
+
+ // first zip file entry is expected to be top level directory
+ char *top_dir = zip_entries[0];
+
+ // check if valid top level directory found in zip file
+ if (!strSuffix(top_dir, "/"))
+ return FALSE;
+
+ // get path of extracted top level directory
+ top_dir_path = getPath2(directory, top_dir);
+
+ // remove trailing directory separator from top level directory path
+ // (required to be able to check for file and directory in next step)
+ top_dir_path[strlen(top_dir_path) - 1] = '\0';
+
+ // check if zip file's top level directory already exists in target directory
+ if (fileExists(top_dir_path)) // (checks for file and directory)
+ return FALSE;
+
+ // get filename of configuration file in top level directory
+ top_dir_conf_filename = getStringCat2(top_dir, conf_basename);
+
+ boolean found_top_dir_conf_filename = FALSE;
+ int i = 0;
+
+ while (zip_entries[i] != NULL)
+ {
+ // check if every zip file entry is below top level directory
+ if (!strPrefix(zip_entries[i], top_dir))
+ return FALSE;
+
+ // check if this zip file entry is the configuration filename
+ if (strEqual(zip_entries[i], top_dir_conf_filename))
+ found_top_dir_conf_filename = TRUE;
+
+ i++;
+ }
+
+ // check if valid configuration filename was found in zip file
+ if (!found_top_dir_conf_filename)
+ return FALSE;
+
+ return TRUE;
+}
+
+char *ExtractZipFileIntoDirectory(char *zip_filename, char *directory,
+ int tree_type)
+{
+ boolean zip_file_valid = CheckZipFileForDirectory(zip_filename, directory,
+ tree_type);
+
+ if (!zip_file_valid)
+ {
+ Error(ERR_WARN, "zip file '%s' rejected!", zip_filename);
+
+ return NULL;
+ }
+
+ char **zip_entries = zip_extract(zip_filename, directory);
+
+ if (zip_entries == NULL)
+ {
+ Error(ERR_WARN, "zip file '%s' could not be extracted!", zip_filename);
+
+ return NULL;
+ }
+
+ Error(ERR_INFO, "zip file '%s' successfully extracted!", zip_filename);
+
+ // first zip file entry contains top level directory
+ char *top_dir = zip_entries[0];
+
+ // remove trailing directory separator from top level directory
+ top_dir[strlen(top_dir) - 1] = '\0';
+
+ return top_dir;
+}
+
+static void ProcessZipFilesInDirectory(char *directory, int tree_type)
+{
+ Directory *dir;
+ DirectoryEntry *dir_entry;
+
+ if ((dir = openDirectory(directory)) == NULL)
+ {
+ // display error if directory is main "options.graphics_directory" etc.
+ if (tree_type == TREE_TYPE_LEVEL_DIR ||
+ directory == OPTIONS_ARTWORK_DIRECTORY(tree_type))
+ Error(ERR_WARN, "cannot read directory '%s'", directory);
+
+ return;
+ }
+
+ while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
+ {
+ // skip non-zip files (and also directories with zip extension)
+ if (!strSuffixLower(dir_entry->basename, ".zip") || dir_entry->is_directory)
+ continue;
+
+ char *zip_filename = getPath2(directory, dir_entry->basename);
+ char *zip_filename_extracted = getStringCat2(zip_filename, ".extracted");
+ char *zip_filename_rejected = getStringCat2(zip_filename, ".rejected");
+
+ // check if zip file hasn't already been extracted or rejected
+ if (!fileExists(zip_filename_extracted) &&
+ !fileExists(zip_filename_rejected))
+ {
+ char *top_dir = ExtractZipFileIntoDirectory(zip_filename, directory,
+ tree_type);
+ char *marker_filename = (top_dir != NULL ? zip_filename_extracted :
+ zip_filename_rejected);
+ FILE *marker_file;
+
+ // create empty file to mark zip file as extracted or rejected
+ if ((marker_file = fopen(marker_filename, MODE_WRITE)))
+ fclose(marker_file);
+
+ free(zip_filename);
+ free(zip_filename_extracted);
+ free(zip_filename_rejected);
+ }
+ }
+
+ closeDirectory(dir);
+}
+
// forward declaration for recursive call by "LoadLevelInfoFromLevelDir()"
static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
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;
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;
print_timestamp_done("LoadLevelArtworkInfo");
}
-static boolean AddUserLevelSetToLevelInfoExt(char *level_subdir_new)
+static boolean AddTreeSetToTreeInfoExt(TreeInfo *tree_node_old, char *tree_dir,
+ char *tree_subdir_new, int type)
{
- // 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
+ if (tree_node_old == NULL)
+ {
+ if (type == TREE_TYPE_LEVEL_DIR)
+ {
+ // get level info tree node of personal user level set
+ tree_node_old = getTreeInfoFromIdentifier(leveldir_first, getLoginName());
+ }
+ else
+ {
+ // get artwork info tree node of first artwork set
+ tree_node_old = ARTWORK_FIRST_NODE(artwork, type);
+ }
+ }
+
+ if (tree_dir == NULL)
+ tree_dir = TREE_USERDIR(type);
+
+ if (tree_node_old == NULL ||
+ tree_dir == NULL ||
+ tree_subdir_new == NULL) // should not happen
return FALSE;
int draw_deactivation_mask = GetDrawDeactivationMask();
// override draw deactivation mask (temporarily disable drawing)
SetDrawDeactivationMask(REDRAW_ALL);
- // 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);
+ if (type == TREE_TYPE_LEVEL_DIR)
+ {
+ // load new level set config and add it next to first user level set
+ LoadLevelInfoFromLevelConf(&tree_node_old->next,
+ tree_node_old->node_parent,
+ tree_dir, tree_subdir_new);
+ }
+ else
+ {
+ // load new artwork set config and add it next to first artwork set
+ LoadArtworkInfoFromArtworkConf(&tree_node_old->next,
+ tree_node_old->node_parent,
+ tree_dir, tree_subdir_new, type);
+ }
// set draw deactivation mask to previous value
SetDrawDeactivationMask(draw_deactivation_mask);
- // get 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
+ // get first node of level or artwork info tree
+ TreeInfo **tree_node_first = TREE_FIRST_NODE_PTR(type);
+
+ // get tree info node of newly added level or artwork set
+ TreeInfo *tree_node_new = getTreeInfoFromIdentifier(*tree_node_first,
+ tree_subdir_new);
+
+ if (tree_node_new == NULL) // should not happen
return FALSE;
// correct top link and parent node link of newly created tree node
- leveldir_new->node_top = leveldir_old->node_top;
- leveldir_new->node_parent = leveldir_old->node_parent;
+ tree_node_new->node_top = tree_node_old->node_top;
+ tree_node_new->node_parent = tree_node_old->node_parent;
- // sort level info tree to adjust position of newly added level set
- sortTreeInfo(&leveldir_first);
+ // sort tree info to adjust position of newly added tree set
+ sortTreeInfo(tree_node_first);
return TRUE;
}
+void AddTreeSetToTreeInfo(TreeInfo *tree_node, char *tree_dir,
+ char *tree_subdir_new, int type)
+{
+ if (!AddTreeSetToTreeInfoExt(tree_node, tree_dir, tree_subdir_new, type))
+ Error(ERR_EXIT, "internal tree info structure corrupted -- aborting");
+}
+
void AddUserLevelSetToLevelInfo(char *level_subdir_new)
{
- if (!AddUserLevelSetToLevelInfoExt(level_subdir_new))
- Error(ERR_EXIT, "internal level set structure corrupted -- aborting");
+ AddTreeSetToTreeInfo(NULL, NULL, level_subdir_new, TREE_TYPE_LEVEL_DIR);
}
char *getArtworkIdentifierForUserLevelSet(int type)
{
char *artwork_set = getArtworkIdentifierForUserLevelSet(type);
TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
+ TreeInfo *ti = getTreeInfoFromIdentifier(artwork_first_node, artwork_set);
+
+ if (ti == NULL)
+ {
+ ti = getTreeInfoFromIdentifier(artwork_first_node,
+ ARTWORK_DEFAULT_SUBDIR(type));
+ if (ti == NULL)
+ Error(ERR_EXIT, "cannot find default graphics -- should not happen");
+ }
- return getTreeInfoFromIdentifier(artwork_first_node, artwork_set);
+ return ti;
}
boolean checkIfCustomArtworkExistsForCurrentLevelSet(void)
{
static char *level_directory = NULL;
Directory *dir;
+#if 0
+ DirectoryEntry *dir_entry;
+#endif
+
+ checked_free(level_directory);
// check for more levels besides the 'levels' field of 'levelinfo.conf'
return;
}
+#if 0
+ while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
+ {
+ if (strlen(dir_entry->basename) > 4 &&
+ dir_entry->basename[3] == '.' &&
+ strEqual(&dir_entry->basename[4], LEVELFILE_EXTENSION))
+ {
+ char levelnum_str[4];
+ int levelnum_value;
+
+ strncpy(levelnum_str, dir_entry->basename, 3);
+ levelnum_str[3] = '\0';
+
+ levelnum_value = atoi(levelnum_str);
+
+ if (levelnum_value < leveldir_current->first_level)
+ {
+ Error(ERR_WARN, "additional level %d found", levelnum_value);
+ leveldir_current->first_level = levelnum_value;
+ }
+ else if (levelnum_value > leveldir_current->last_level)
+ {
+ Error(ERR_WARN, "additional level %d found", levelnum_value);
+ leveldir_current->last_level = levelnum_value;
+ }
+ }
+ }
+#endif
+
closeDirectory(dir);
}