1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
27 #define NUM_LEVELCLASS_DESC 8
29 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
42 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
43 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
44 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
49 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
52 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
53 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
54 IS_LEVELCLASS_BD(n) ? 2 : \
55 IS_LEVELCLASS_EM(n) ? 3 : \
56 IS_LEVELCLASS_SP(n) ? 4 : \
57 IS_LEVELCLASS_DX(n) ? 5 : \
58 IS_LEVELCLASS_CONTRIB(n) ? 6 : \
59 IS_LEVELCLASS_PRIVATE(n) ? 7 : \
62 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
63 IS_ARTWORKCLASS_CONTRIB(n) ? FC_YELLOW : \
64 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
65 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
68 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
69 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
70 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
71 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
74 #define TOKEN_VALUE_POSITION 40
75 #define TOKEN_COMMENT_POSITION 60
77 #define MAX_COOKIE_LEN 256
80 /* ------------------------------------------------------------------------- */
82 /* ------------------------------------------------------------------------- */
84 static char *getLevelClassDescription(TreeInfo *ldi)
86 int position = ldi->sort_priority / 100;
88 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
89 return levelclass_desc[position];
91 return "Unknown Level Class";
94 static char *getUserLevelDir(char *level_subdir)
96 static char *userlevel_dir = NULL;
97 char *data_dir = getUserDataDir();
98 char *userlevel_subdir = LEVELS_DIRECTORY;
103 if (level_subdir != NULL)
104 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
106 userlevel_dir = getPath2(data_dir, userlevel_subdir);
108 return userlevel_dir;
111 static char *getTapeDir(char *level_subdir)
113 static char *tape_dir = NULL;
114 char *data_dir = getUserDataDir();
115 char *tape_subdir = TAPES_DIRECTORY;
120 if (level_subdir != NULL)
121 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
123 tape_dir = getPath2(data_dir, tape_subdir);
128 static char *getScoreDir(char *level_subdir)
130 static char *score_dir = NULL;
131 char *data_dir = getCommonDataDir();
132 char *score_subdir = SCORES_DIRECTORY;
137 if (level_subdir != NULL)
138 score_dir = getPath3(data_dir, score_subdir, level_subdir);
140 score_dir = getPath2(data_dir, score_subdir);
145 static char *getLevelSetupDir(char *level_subdir)
147 static char *levelsetup_dir = NULL;
148 char *data_dir = getUserDataDir();
149 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
152 free(levelsetup_dir);
154 if (level_subdir != NULL)
155 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
157 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
159 return levelsetup_dir;
162 static char *getLevelDirFromTreeInfo(TreeInfo *node)
164 static char *level_dir = NULL;
167 return options.level_directory;
172 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
173 options.level_directory), node->fullpath);
178 static char *getCurrentLevelDir()
180 return getLevelDirFromTreeInfo(leveldir_current);
183 static char *getDefaultGraphicsDir(char *graphics_subdir)
185 static char *graphics_dir = NULL;
187 if (graphics_subdir == NULL)
188 return options.graphics_directory;
193 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
198 static char *getDefaultSoundsDir(char *sounds_subdir)
200 static char *sounds_dir = NULL;
202 if (sounds_subdir == NULL)
203 return options.sounds_directory;
208 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
213 static char *getDefaultMusicDir(char *music_subdir)
215 static char *music_dir = NULL;
217 if (music_subdir == NULL)
218 return options.music_directory;
223 music_dir = getPath2(options.music_directory, music_subdir);
228 static char *getDefaultArtworkSet(int type)
230 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
231 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
232 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
235 static char *getDefaultArtworkDir(int type)
237 return (type == TREE_TYPE_GRAPHICS_DIR ?
238 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
239 type == TREE_TYPE_SOUNDS_DIR ?
240 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
241 type == TREE_TYPE_MUSIC_DIR ?
242 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
245 static char *getUserGraphicsDir()
247 static char *usergraphics_dir = NULL;
249 if (usergraphics_dir == NULL)
250 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
252 return usergraphics_dir;
255 static char *getUserSoundsDir()
257 static char *usersounds_dir = NULL;
259 if (usersounds_dir == NULL)
260 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
262 return usersounds_dir;
265 static char *getUserMusicDir()
267 static char *usermusic_dir = NULL;
269 if (usermusic_dir == NULL)
270 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
272 return usermusic_dir;
275 static char *getSetupArtworkDir(TreeInfo *ti)
277 static char *artwork_dir = NULL;
279 if (artwork_dir != NULL)
282 artwork_dir = getPath2(ti->basepath, ti->fullpath);
287 char *setLevelArtworkDir(TreeInfo *ti)
289 char **artwork_path_ptr, **artwork_set_ptr;
290 TreeInfo *level_artwork;
292 if (ti == NULL || leveldir_current == NULL)
295 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
296 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
298 if (*artwork_path_ptr != NULL)
299 free(*artwork_path_ptr);
301 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
302 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
305 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
306 normally result in using the artwork configured in the setup menu. But
307 if an artwork subdirectory exists (which might contain custom artwork
308 or an artwork configuration file), this level artwork must be treated
309 as relative to the default "classic" artwork, not to the artwork that
310 is currently configured in the setup menu. */
312 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
314 if (*artwork_set_ptr != NULL)
315 free(*artwork_set_ptr);
319 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
320 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
324 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
325 *artwork_set_ptr = NULL;
331 return *artwork_set_ptr;
334 inline static char *getLevelArtworkSet(int type)
336 if (leveldir_current == NULL)
339 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
342 inline static char *getLevelArtworkDir(int type)
344 if (leveldir_current == NULL)
345 return UNDEFINED_FILENAME;
347 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
350 char *getLevelFilename(int nr)
352 static char *filename = NULL;
353 char basename[MAX_FILENAME_LEN];
355 if (filename != NULL)
359 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
361 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
363 filename = getPath2(getCurrentLevelDir(), basename);
368 char *getTapeFilename(int nr)
370 static char *filename = NULL;
371 char basename[MAX_FILENAME_LEN];
373 if (filename != NULL)
376 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
377 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
382 char *getScoreFilename(int nr)
384 static char *filename = NULL;
385 char basename[MAX_FILENAME_LEN];
387 if (filename != NULL)
390 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
391 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
396 char *getSetupFilename()
398 static char *filename = NULL;
400 if (filename != NULL)
403 filename = getPath2(getSetupDir(), SETUP_FILENAME);
408 static char *getCorrectedArtworkBasename(char *basename)
410 char *basename_corrected = basename;
412 #if defined(PLATFORM_MSDOS)
413 if (program.filename_prefix != NULL)
415 int prefix_len = strlen(program.filename_prefix);
417 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
418 basename_corrected = &basename[prefix_len];
420 /* if corrected filename is still longer than standard MS-DOS filename
421 size (8 characters + 1 dot + 3 characters file extension), shorten
422 filename by writing file extension after 8th basename character */
423 if (strlen(basename_corrected) > 8 + 1 + 3)
425 static char *msdos_filename = NULL;
427 if (msdos_filename != NULL)
428 free(msdos_filename);
430 msdos_filename = getStringCopy(basename_corrected);
431 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
433 basename_corrected = msdos_filename;
438 return basename_corrected;
441 char *getCustomImageFilename(char *basename)
443 static char *filename = NULL;
444 boolean skip_setup_artwork = FALSE;
446 if (filename != NULL)
449 basename = getCorrectedArtworkBasename(basename);
451 if (!setup.override_level_graphics)
453 /* 1st try: look for special artwork in current level series directory */
454 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
455 if (fileExists(filename))
460 /* check if there is special artwork configured in level series config */
461 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
463 /* 2nd try: look for special artwork configured in level series config */
464 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
465 if (fileExists(filename))
470 /* take missing artwork configured in level set config from default */
471 skip_setup_artwork = TRUE;
475 if (!skip_setup_artwork)
477 /* 3rd try: look for special artwork in configured artwork directory */
478 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
479 if (fileExists(filename))
485 /* 4th try: look for default artwork in new default artwork directory */
486 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
487 if (fileExists(filename))
492 /* 5th try: look for default artwork in old default artwork directory */
493 filename = getPath2(options.graphics_directory, basename);
494 if (fileExists(filename))
497 return NULL; /* cannot find specified artwork file anywhere */
500 char *getCustomSoundFilename(char *basename)
502 static char *filename = NULL;
503 boolean skip_setup_artwork = FALSE;
505 if (filename != NULL)
508 basename = getCorrectedArtworkBasename(basename);
510 if (!setup.override_level_sounds)
512 /* 1st try: look for special artwork in current level series directory */
513 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
514 if (fileExists(filename))
519 /* check if there is special artwork configured in level series config */
520 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
522 /* 2nd try: look for special artwork configured in level series config */
523 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
524 if (fileExists(filename))
529 /* take missing artwork configured in level set config from default */
530 skip_setup_artwork = TRUE;
534 if (!skip_setup_artwork)
536 /* 3rd try: look for special artwork in configured artwork directory */
537 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
538 if (fileExists(filename))
544 /* 4th try: look for default artwork in new default artwork directory */
545 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
546 if (fileExists(filename))
551 /* 5th try: look for default artwork in old default artwork directory */
552 filename = getPath2(options.sounds_directory, basename);
553 if (fileExists(filename))
556 return NULL; /* cannot find specified artwork file anywhere */
559 char *getCustomArtworkFilename(char *basename, int type)
561 if (type == ARTWORK_TYPE_GRAPHICS)
562 return getCustomImageFilename(basename);
563 else if (type == ARTWORK_TYPE_SOUNDS)
564 return getCustomSoundFilename(basename);
566 return UNDEFINED_FILENAME;
569 char *getCustomArtworkConfigFilename(int type)
571 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
574 char *getCustomArtworkLevelConfigFilename(int type)
576 static char *filename = NULL;
578 if (filename != NULL)
581 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
586 char *getCustomMusicDirectory(void)
588 static char *directory = NULL;
589 boolean skip_setup_artwork = FALSE;
591 if (directory != NULL)
594 if (!setup.override_level_music)
596 /* 1st try: look for special artwork in current level series directory */
597 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
598 if (fileExists(directory))
603 /* check if there is special artwork configured in level series config */
604 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
606 /* 2nd try: look for special artwork configured in level series config */
607 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
608 if (fileExists(directory))
613 /* take missing artwork configured in level set config from default */
614 skip_setup_artwork = TRUE;
618 if (!skip_setup_artwork)
620 /* 3rd try: look for special artwork in configured artwork directory */
621 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
622 if (fileExists(directory))
628 /* 4th try: look for default artwork in new default artwork directory */
629 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
630 if (fileExists(directory))
635 /* 5th try: look for default artwork in old default artwork directory */
636 directory = getStringCopy(options.music_directory);
637 if (fileExists(directory))
640 return NULL; /* cannot find specified artwork file anywhere */
643 void InitTapeDirectory(char *level_subdir)
645 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
646 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
647 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
650 void InitScoreDirectory(char *level_subdir)
652 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
653 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
654 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
657 static void SaveUserLevelInfo();
659 void InitUserLevelDirectory(char *level_subdir)
661 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
663 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
664 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
665 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
671 void InitLevelSetupDirectory(char *level_subdir)
673 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
674 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
675 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
679 /* ------------------------------------------------------------------------- */
680 /* some functions to handle lists of level directories */
681 /* ------------------------------------------------------------------------- */
683 TreeInfo *newTreeInfo()
685 return checked_calloc(sizeof(TreeInfo));
688 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
690 node_new->next = *node_first;
691 *node_first = node_new;
694 int numTreeInfo(TreeInfo *node)
707 boolean validLevelSeries(TreeInfo *node)
709 return (node != NULL && !node->node_group && !node->parent_link);
712 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
717 if (node->node_group) /* enter level group (step down into tree) */
718 return getFirstValidTreeInfoEntry(node->node_group);
719 else if (node->parent_link) /* skip start entry of level group */
721 if (node->next) /* get first real level series entry */
722 return getFirstValidTreeInfoEntry(node->next);
723 else /* leave empty level group and go on */
724 return getFirstValidTreeInfoEntry(node->node_parent->next);
726 else /* this seems to be a regular level series */
730 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
735 if (node->node_parent == NULL) /* top level group */
736 return *node->node_top;
737 else /* sub level group */
738 return node->node_parent->node_group;
741 int numTreeInfoInGroup(TreeInfo *node)
743 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
746 int posTreeInfo(TreeInfo *node)
748 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
753 if (node_cmp == node)
757 node_cmp = node_cmp->next;
763 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
765 TreeInfo *node_default = node;
780 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
782 if (identifier == NULL)
787 if (node->node_group)
789 TreeInfo *node_group;
791 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
796 else if (!node->parent_link)
798 if (strcmp(identifier, node->identifier) == 0)
808 void dumpTreeInfo(TreeInfo *node, int depth)
812 printf("Dumping TreeInfo:\n");
816 for (i=0; i<(depth + 1) * 3; i++)
820 printf("filename == '%s' ['%s', '%s'] [%d])\n",
821 node->filename, node->fullpath, node->basepath, node->user_defined);
823 printf("filename == '%s' (%s) [%s] (%d)\n",
824 node->filename, node->name, node->identifier, node->sort_priority);
827 if (node->node_group != NULL)
828 dumpTreeInfo(node->node_group, depth + 1);
834 void sortTreeInfo(TreeInfo **node_first,
835 int (*compare_function)(const void *, const void *))
837 int num_nodes = numTreeInfo(*node_first);
838 TreeInfo **sort_array;
839 TreeInfo *node = *node_first;
845 /* allocate array for sorting structure pointers */
846 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
848 /* writing structure pointers to sorting array */
849 while (i < num_nodes && node) /* double boundary check... */
851 sort_array[i] = node;
857 /* sorting the structure pointers in the sorting array */
858 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
861 /* update the linkage of list elements with the sorted node array */
862 for (i=0; i<num_nodes - 1; i++)
863 sort_array[i]->next = sort_array[i + 1];
864 sort_array[num_nodes - 1]->next = NULL;
866 /* update the linkage of the main list anchor pointer */
867 *node_first = sort_array[0];
871 /* now recursively sort the level group structures */
875 if (node->node_group != NULL)
876 sortTreeInfo(&node->node_group, compare_function);
883 /* ========================================================================= */
884 /* some stuff from "files.c" */
885 /* ========================================================================= */
887 #if defined(PLATFORM_WIN32)
889 #define S_IRGRP S_IRUSR
892 #define S_IROTH S_IRUSR
895 #define S_IWGRP S_IWUSR
898 #define S_IWOTH S_IWUSR
901 #define S_IXGRP S_IXUSR
904 #define S_IXOTH S_IXUSR
907 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
912 #endif /* PLATFORM_WIN32 */
914 /* file permissions for newly written files */
915 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
916 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
917 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
919 #define MODE_W_PRIVATE (S_IWUSR)
920 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
921 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
923 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
924 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
926 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
927 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
929 char *getUserDataDir(void)
931 static char *userdata_dir = NULL;
933 if (userdata_dir == NULL)
934 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
939 char *getCommonDataDir(void)
941 static char *common_data_dir = NULL;
943 #if defined(PLATFORM_WIN32)
944 if (common_data_dir == NULL)
946 char *dir = checked_malloc(MAX_PATH + 1);
948 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
949 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
950 common_data_dir = getPath2(dir, program.userdata_directory);
952 common_data_dir = options.rw_base_directory;
955 if (common_data_dir == NULL)
956 common_data_dir = options.rw_base_directory;
959 return common_data_dir;
964 return getUserDataDir();
967 static mode_t posix_umask(mode_t mask)
969 #if defined(PLATFORM_UNIX)
976 static int posix_mkdir(const char *pathname, mode_t mode)
978 #if defined(PLATFORM_WIN32)
979 return mkdir(pathname);
981 return mkdir(pathname, mode);
985 void createDirectory(char *dir, char *text, int permission_class)
987 /* leave "other" permissions in umask untouched, but ensure group parts
988 of USERDATA_DIR_MODE are not masked */
989 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
990 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
991 mode_t normal_umask = posix_umask(0);
992 mode_t group_umask = ~(dir_mode & S_IRWXG);
993 posix_umask(normal_umask & group_umask);
995 if (access(dir, F_OK) != 0)
996 if (posix_mkdir(dir, dir_mode) != 0)
997 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
999 posix_umask(normal_umask); /* reset normal umask */
1002 void InitUserDataDirectory()
1004 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1007 void SetFilePermissions(char *filename, int permission_class)
1009 chmod(filename, (permission_class == PERMS_PRIVATE ?
1010 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1013 char *getCookie(char *file_type)
1015 static char cookie[MAX_COOKIE_LEN + 1];
1017 if (strlen(program.cookie_prefix) + 1 +
1018 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1019 return "[COOKIE ERROR]"; /* should never happen */
1021 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1022 program.cookie_prefix, file_type,
1023 program.version_major, program.version_minor);
1028 int getFileVersionFromCookieString(const char *cookie)
1030 const char *ptr_cookie1, *ptr_cookie2;
1031 const char *pattern1 = "_FILE_VERSION_";
1032 const char *pattern2 = "?.?";
1033 const int len_cookie = strlen(cookie);
1034 const int len_pattern1 = strlen(pattern1);
1035 const int len_pattern2 = strlen(pattern2);
1036 const int len_pattern = len_pattern1 + len_pattern2;
1037 int version_major, version_minor;
1039 if (len_cookie <= len_pattern)
1042 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1043 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1045 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1048 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1049 ptr_cookie2[1] != '.' ||
1050 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1053 version_major = ptr_cookie2[0] - '0';
1054 version_minor = ptr_cookie2[2] - '0';
1056 return VERSION_IDENT(version_major, version_minor, 0, 0);
1059 boolean checkCookieString(const char *cookie, const char *template)
1061 const char *pattern = "_FILE_VERSION_?.?";
1062 const int len_cookie = strlen(cookie);
1063 const int len_template = strlen(template);
1064 const int len_pattern = strlen(pattern);
1066 if (len_cookie != len_template)
1069 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1075 /* ------------------------------------------------------------------------- */
1076 /* setup file list and hash handling functions */
1077 /* ------------------------------------------------------------------------- */
1079 char *getFormattedSetupEntry(char *token, char *value)
1082 static char entry[MAX_LINE_LEN];
1084 /* start with the token and some spaces to format output line */
1085 sprintf(entry, "%s:", token);
1086 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1089 /* continue with the token's value */
1090 strcat(entry, value);
1095 SetupFileList *newSetupFileList(char *token, char *value)
1097 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1099 new->token = getStringCopy(token);
1100 new->value = getStringCopy(value);
1107 void freeSetupFileList(SetupFileList *list)
1117 freeSetupFileList(list->next);
1121 char *getListEntry(SetupFileList *list, char *token)
1126 if (strcmp(list->token, token) == 0)
1129 return getListEntry(list->next, token);
1132 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1137 if (strcmp(list->token, token) == 0)
1142 list->value = getStringCopy(value);
1146 else if (list->next == NULL)
1147 return (list->next = newSetupFileList(token, value));
1149 return setListEntry(list->next, token, value);
1153 static void printSetupFileList(SetupFileList *list)
1158 printf("token: '%s'\n", list->token);
1159 printf("value: '%s'\n", list->value);
1161 printSetupFileList(list->next);
1166 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1167 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1168 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1169 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1171 #define insert_hash_entry hashtable_insert
1172 #define search_hash_entry hashtable_search
1173 #define change_hash_entry hashtable_change
1174 #define remove_hash_entry hashtable_remove
1177 static unsigned int get_hash_from_key(void *key)
1182 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1183 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1184 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1185 it works better than many other constants, prime or not) has never been
1186 adequately explained.
1188 If you just want to have a good hash function, and cannot wait, djb2
1189 is one of the best string hash functions i know. It has excellent
1190 distribution and speed on many different sets of keys and table sizes.
1191 You are not likely to do better with one of the "well known" functions
1192 such as PJW, K&R, etc.
1194 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1197 char *str = (char *)key;
1198 unsigned int hash = 5381;
1201 while ((c = *str++))
1202 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1207 static int keys_are_equal(void *key1, void *key2)
1209 return (strcmp((char *)key1, (char *)key2) == 0);
1212 SetupFileHash *newSetupFileHash()
1214 SetupFileHash *new_hash =
1215 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1220 void freeSetupFileHash(SetupFileHash *hash)
1225 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1228 char *getHashEntry(SetupFileHash *hash, char *token)
1233 return search_hash_entry(hash, token);
1236 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1243 value_copy = getStringCopy(value);
1245 /* change value; if it does not exist, insert it as new */
1246 if (!change_hash_entry(hash, token, value_copy))
1247 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1248 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1253 static void printSetupFileHash(SetupFileHash *hash)
1255 BEGIN_HASH_ITERATION(hash, itr)
1257 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1258 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1260 END_HASH_ITERATION(hash, itr)
1265 static void *loadSetupFileData(char *filename, boolean use_hash)
1268 char line[MAX_LINE_LEN];
1269 char *token, *value, *line_ptr;
1270 void *setup_file_data, *insert_ptr = NULL;
1274 setup_file_data = newSetupFileHash();
1276 insert_ptr = setup_file_data = newSetupFileList("", "");
1278 if (!(file = fopen(filename, MODE_READ)))
1280 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1286 /* read next line of input file */
1287 if (!fgets(line, MAX_LINE_LEN, file))
1290 /* cut trailing comment or whitespace from input line */
1291 for (line_ptr = line; *line_ptr; line_ptr++)
1293 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1300 /* cut trailing whitespaces from input line */
1301 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1302 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1305 /* ignore empty lines */
1309 line_len = strlen(line);
1311 /* cut leading whitespaces from token */
1312 for (token = line; *token; token++)
1313 if (*token != ' ' && *token != '\t')
1316 /* find end of token */
1317 for (line_ptr = token; *line_ptr; line_ptr++)
1319 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1326 if (line_ptr < line + line_len)
1327 value = line_ptr + 1;
1331 /* cut leading whitespaces from value */
1332 for (; *value; value++)
1333 if (*value != ' ' && *value != '\t')
1336 if (*token && *value)
1339 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1341 insert_ptr = setListEntry((SetupFileList *)insert_ptr, token, value);
1349 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1350 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1354 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1355 SetupFileList *first_valid_list_entry = setup_file_list->next;
1357 /* free empty list header */
1358 setup_file_list->next = NULL;
1359 freeSetupFileList(setup_file_list);
1360 setup_file_data = first_valid_list_entry;
1362 if (first_valid_list_entry == NULL)
1363 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1366 return setup_file_data;
1369 SetupFileList *loadSetupFileList(char *filename)
1371 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1374 SetupFileHash *loadSetupFileHash(char *filename)
1376 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1379 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1382 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1385 Error(ERR_WARN, "configuration file has no file identifier");
1386 else if (!checkCookieString(value, identifier))
1387 Error(ERR_WARN, "configuration file has wrong file identifier");
1391 /* ========================================================================= */
1392 /* setup file stuff */
1393 /* ========================================================================= */
1395 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1396 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1397 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1399 /* level directory info */
1400 #define LEVELINFO_TOKEN_IDENTIFIER 0
1401 #define LEVELINFO_TOKEN_NAME 1
1402 #define LEVELINFO_TOKEN_NAME_SORTING 2
1403 #define LEVELINFO_TOKEN_AUTHOR 3
1404 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1405 #define LEVELINFO_TOKEN_LEVELS 5
1406 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1407 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1408 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1409 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1410 #define LEVELINFO_TOKEN_READONLY 10
1411 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1412 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1413 #define LEVELINFO_TOKEN_MUSIC_SET 13
1415 #define NUM_LEVELINFO_TOKENS 14
1417 static LevelDirTree ldi;
1419 static struct TokenInfo levelinfo_tokens[] =
1421 /* level directory info */
1422 { TYPE_STRING, &ldi.identifier, "identifier" },
1423 { TYPE_STRING, &ldi.name, "name" },
1424 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1425 { TYPE_STRING, &ldi.author, "author" },
1426 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1427 { TYPE_INTEGER, &ldi.levels, "levels" },
1428 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1429 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1430 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1431 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1432 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1433 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1434 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1435 { TYPE_STRING, &ldi.music_set, "music_set" }
1438 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1442 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1443 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1444 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1445 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1448 ldi->node_parent = NULL;
1449 ldi->node_group = NULL;
1453 ldi->cl_cursor = -1;
1455 ldi->filename = NULL;
1456 ldi->fullpath = NULL;
1457 ldi->basepath = NULL;
1458 ldi->identifier = NULL;
1459 ldi->name = getStringCopy(ANONYMOUS_NAME);
1460 ldi->name_sorting = NULL;
1461 ldi->author = getStringCopy(ANONYMOUS_NAME);
1463 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1464 ldi->latest_engine = FALSE; /* default: get from level */
1465 ldi->parent_link = FALSE;
1466 ldi->user_defined = FALSE;
1468 ldi->class_desc = NULL;
1470 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1472 ldi->imported_from = NULL;
1474 ldi->graphics_set = NULL;
1475 ldi->sounds_set = NULL;
1476 ldi->music_set = NULL;
1477 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1478 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1479 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1482 ldi->first_level = 0;
1483 ldi->last_level = 0;
1484 ldi->level_group = FALSE;
1485 ldi->handicap_level = 0;
1486 ldi->readonly = TRUE;
1490 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1494 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1496 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1502 /* copy all values from the parent structure */
1504 ldi->type = parent->type;
1506 ldi->node_top = parent->node_top;
1507 ldi->node_parent = parent;
1508 ldi->node_group = NULL;
1512 ldi->cl_cursor = -1;
1514 ldi->filename = NULL;
1515 ldi->fullpath = NULL;
1516 ldi->basepath = NULL;
1517 ldi->identifier = NULL;
1518 ldi->name = getStringCopy(ANONYMOUS_NAME);
1519 ldi->name_sorting = NULL;
1520 ldi->author = getStringCopy(parent->author);
1522 ldi->sort_priority = parent->sort_priority;
1523 ldi->latest_engine = parent->latest_engine;
1524 ldi->parent_link = FALSE;
1525 ldi->user_defined = parent->user_defined;
1526 ldi->color = parent->color;
1527 ldi->class_desc = getStringCopy(parent->class_desc);
1529 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1531 ldi->imported_from = getStringCopy(parent->imported_from);
1533 ldi->graphics_set = NULL;
1534 ldi->sounds_set = NULL;
1535 ldi->music_set = NULL;
1536 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1537 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1538 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1541 ldi->first_level = 0;
1542 ldi->last_level = 0;
1543 ldi->level_group = FALSE;
1544 ldi->handicap_level = 0;
1545 ldi->readonly = TRUE;
1551 /* first copy all values from the parent structure ... */
1554 /* ... then set all fields to default that cannot be inherited from parent.
1555 This is especially important for all those fields that can be set from
1556 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1557 calls 'free()' for all already set token values which requires that no
1558 other structure's pointer may point to them!
1561 ldi->filename = NULL;
1562 ldi->fullpath = NULL;
1563 ldi->basepath = NULL;
1564 ldi->identifier = NULL;
1565 ldi->name = getStringCopy(ANONYMOUS_NAME);
1566 ldi->name_sorting = NULL;
1567 ldi->author = getStringCopy(parent->author);
1569 ldi->imported_from = getStringCopy(parent->imported_from);
1570 ldi->class_desc = getStringCopy(parent->class_desc);
1572 ldi->graphics_set = NULL;
1573 ldi->sounds_set = NULL;
1574 ldi->music_set = NULL;
1575 ldi->graphics_path = NULL;
1576 ldi->sounds_path = NULL;
1577 ldi->music_path = NULL;
1579 ldi->level_group = FALSE;
1580 ldi->parent_link = FALSE;
1582 ldi->node_top = parent->node_top;
1583 ldi->node_parent = parent;
1584 ldi->node_group = NULL;
1590 static void freeTreeInfo(TreeInfo *ldi)
1593 free(ldi->filename);
1595 free(ldi->fullpath);
1597 free(ldi->basepath);
1598 if (ldi->identifier)
1599 free(ldi->identifier);
1603 if (ldi->name_sorting)
1604 free(ldi->name_sorting);
1608 if (ldi->class_desc)
1609 free(ldi->class_desc);
1611 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1613 if (ldi->graphics_set)
1614 free(ldi->graphics_set);
1615 if (ldi->sounds_set)
1616 free(ldi->sounds_set);
1618 free(ldi->music_set);
1620 if (ldi->graphics_path)
1621 free(ldi->graphics_path);
1622 if (ldi->sounds_path)
1623 free(ldi->sounds_path);
1624 if (ldi->music_path)
1625 free(ldi->music_path);
1629 void setSetupInfo(struct TokenInfo *token_info,
1630 int token_nr, char *token_value)
1632 int token_type = token_info[token_nr].type;
1633 void *setup_value = token_info[token_nr].value;
1635 if (token_value == NULL)
1638 /* set setup field to corresponding token value */
1643 *(boolean *)setup_value = get_boolean_from_string(token_value);
1647 *(Key *)setup_value = getKeyFromKeyName(token_value);
1651 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1655 *(int *)setup_value = get_integer_from_string(token_value);
1659 if (*(char **)setup_value != NULL)
1660 free(*(char **)setup_value);
1661 *(char **)setup_value = getStringCopy(token_value);
1669 static int compareTreeInfoEntries(const void *object1, const void *object2)
1671 const TreeInfo *entry1 = *((TreeInfo **)object1);
1672 const TreeInfo *entry2 = *((TreeInfo **)object2);
1673 int class_sorting1, class_sorting2;
1676 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1678 class_sorting1 = LEVELSORTING(entry1);
1679 class_sorting2 = LEVELSORTING(entry2);
1683 class_sorting1 = ARTWORKSORTING(entry1);
1684 class_sorting2 = ARTWORKSORTING(entry2);
1687 if (entry1->parent_link || entry2->parent_link)
1688 compare_result = (entry1->parent_link ? -1 : +1);
1689 else if (entry1->sort_priority == entry2->sort_priority)
1691 char *name1 = getStringToLower(entry1->name_sorting);
1692 char *name2 = getStringToLower(entry2->name_sorting);
1694 compare_result = strcmp(name1, name2);
1699 else if (class_sorting1 == class_sorting2)
1700 compare_result = entry1->sort_priority - entry2->sort_priority;
1702 compare_result = class_sorting1 - class_sorting2;
1704 return compare_result;
1707 static void createParentTreeInfoNode(TreeInfo *node_parent)
1711 if (node_parent == NULL)
1714 ti_new = newTreeInfo();
1715 setTreeInfoToDefaults(ti_new, node_parent->type);
1717 ti_new->node_parent = node_parent;
1718 ti_new->parent_link = TRUE;
1721 setString(&ti_new->identifier, node_parent->identifier);
1722 setString(&ti_new->name, ".. (parent directory)");
1723 setString(&ti_new->name_sorting, ti_new->name);
1725 setString(&ti_new->filename, "..");
1726 setString(&ti_new->fullpath, node_parent->fullpath);
1728 ti_new->sort_priority = node_parent->sort_priority;
1729 ti_new->latest_engine = node_parent->latest_engine;
1731 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1733 ti_new->identifier = getStringCopy(node_parent->identifier);
1734 ti_new->name = ".. (parent directory)";
1735 ti_new->name_sorting = getStringCopy(ti_new->name);
1737 ti_new->filename = "..";
1738 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1740 ti_new->sort_priority = node_parent->sort_priority;
1741 ti_new->latest_engine = node_parent->latest_engine;
1743 ti_new->class_desc = getLevelClassDescription(ti_new);
1746 pushTreeInfo(&node_parent->node_group, ti_new);
1749 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1750 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1752 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1753 TreeInfo *node_parent,
1754 char *level_directory,
1755 char *directory_name)
1757 char *directory_path = getPath2(level_directory, directory_name);
1758 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1759 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1760 LevelDirTree *leveldir_new = NULL;
1763 if (setup_file_hash == NULL)
1765 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1767 free(directory_path);
1773 leveldir_new = newTreeInfo();
1776 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1778 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1780 leveldir_new->filename = getStringCopy(directory_name);
1782 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1784 /* set all structure fields according to the token/value pairs */
1785 ldi = *leveldir_new;
1786 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1787 setSetupInfo(levelinfo_tokens, i,
1788 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1789 *leveldir_new = ldi;
1792 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1793 setString(&leveldir_new->name, leveldir_new->filename);
1795 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1797 free(leveldir_new->name);
1798 leveldir_new->name = getStringCopy(leveldir_new->filename);
1802 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1804 if (leveldir_new->identifier == NULL)
1805 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1807 if (leveldir_new->name_sorting == NULL)
1808 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1810 if (node_parent == NULL) /* top level group */
1812 leveldir_new->basepath = getStringCopy(level_directory);
1813 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1815 else /* sub level group */
1817 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1818 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1821 if (leveldir_new->levels < 1)
1822 leveldir_new->levels = 1;
1824 leveldir_new->last_level =
1825 leveldir_new->first_level + leveldir_new->levels - 1;
1828 leveldir_new->user_defined =
1829 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1831 leveldir_new->user_defined =
1832 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1835 leveldir_new->color = LEVELCOLOR(leveldir_new);
1837 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1839 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1842 leveldir_new->handicap_level = /* set handicap to default value */
1843 (leveldir_new->user_defined ?
1844 leveldir_new->last_level :
1845 leveldir_new->first_level);
1847 pushTreeInfo(node_first, leveldir_new);
1849 freeSetupFileHash(setup_file_hash);
1851 if (leveldir_new->level_group)
1853 /* create node to link back to current level directory */
1854 createParentTreeInfoNode(leveldir_new);
1856 /* step into sub-directory and look for more level series */
1857 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1858 leveldir_new, directory_path);
1861 free(directory_path);
1867 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1868 TreeInfo *node_parent,
1869 char *level_directory)
1872 struct dirent *dir_entry;
1873 boolean valid_entry_found = FALSE;
1875 if ((dir = opendir(level_directory)) == NULL)
1877 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1881 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1883 struct stat file_status;
1884 char *directory_name = dir_entry->d_name;
1885 char *directory_path = getPath2(level_directory, directory_name);
1887 /* skip entries for current and parent directory */
1888 if (strcmp(directory_name, ".") == 0 ||
1889 strcmp(directory_name, "..") == 0)
1891 free(directory_path);
1895 /* find out if directory entry is itself a directory */
1896 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1897 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1899 free(directory_path);
1903 free(directory_path);
1905 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1906 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1907 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1910 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1917 if (!valid_entry_found)
1919 /* check if this directory directly contains a file "levelinfo.conf" */
1920 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1921 level_directory, ".");
1924 if (!valid_entry_found)
1925 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1929 void LoadLevelInfo()
1931 InitUserLevelDirectory(getLoginName());
1933 DrawInitText("Loading level series:", 120, FC_GREEN);
1935 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1936 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1938 /* before sorting, the first entries will be from the user directory */
1939 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1941 if (leveldir_first == NULL)
1942 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1944 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1947 dumpTreeInfo(leveldir_first, 0);
1951 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1952 TreeInfo *node_parent,
1953 char *base_directory,
1954 char *directory_name, int type)
1956 char *directory_path = getPath2(base_directory, directory_name);
1957 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1958 SetupFileHash *setup_file_hash = NULL;
1959 TreeInfo *artwork_new = NULL;
1962 if (access(filename, F_OK) == 0) /* file exists */
1963 setup_file_hash = loadSetupFileHash(filename);
1965 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1968 struct dirent *dir_entry;
1969 boolean valid_file_found = FALSE;
1971 if ((dir = opendir(directory_path)) != NULL)
1973 while ((dir_entry = readdir(dir)) != NULL)
1975 char *entry_name = dir_entry->d_name;
1977 if (FileIsArtworkType(entry_name, type))
1979 valid_file_found = TRUE;
1987 if (!valid_file_found)
1989 if (strcmp(directory_name, ".") != 0)
1990 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1992 free(directory_path);
1999 artwork_new = newTreeInfo();
2002 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2004 setTreeInfoToDefaults(artwork_new, type);
2006 artwork_new->filename = getStringCopy(directory_name);
2008 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2011 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2014 /* set all structure fields according to the token/value pairs */
2016 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2017 setSetupInfo(levelinfo_tokens, i,
2018 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2022 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2023 setString(&artwork_new->name, artwork_new->filename);
2025 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2027 free(artwork_new->name);
2028 artwork_new->name = getStringCopy(artwork_new->filename);
2033 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2036 if (artwork_new->identifier == NULL)
2037 artwork_new->identifier = getStringCopy(artwork_new->filename);
2039 if (artwork_new->name_sorting == NULL)
2040 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2043 if (node_parent == NULL) /* top level group */
2045 artwork_new->basepath = getStringCopy(base_directory);
2046 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2048 else /* sub level group */
2050 artwork_new->basepath = getStringCopy(node_parent->basepath);
2051 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2055 artwork_new->user_defined =
2056 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2058 artwork_new->user_defined =
2059 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2062 /* (may use ".sort_priority" from "setup_file_hash" above) */
2063 artwork_new->color = ARTWORKCOLOR(artwork_new);
2065 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2067 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2070 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2073 if (artwork_new->name != NULL)
2075 free(artwork_new->name);
2076 artwork_new->name = NULL;
2081 if (artwork_new->identifier != NULL)
2083 free(artwork_new->identifier);
2084 artwork_new->identifier = NULL;
2088 if (strcmp(artwork_new->filename, ".") == 0)
2090 if (artwork_new->user_defined)
2093 setString(&artwork_new->identifier, "private");
2095 artwork_new->identifier = getStringCopy("private");
2097 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2102 setString(&artwork_new->identifier, "classic");
2104 artwork_new->identifier = getStringCopy("classic");
2106 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2109 /* set to new values after changing ".sort_priority" */
2110 artwork_new->color = ARTWORKCOLOR(artwork_new);
2112 setString(&artwork_new->class_desc,
2113 getLevelClassDescription(artwork_new));
2115 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2121 setString(&artwork_new->identifier, artwork_new->filename);
2123 artwork_new->identifier = getStringCopy(artwork_new->filename);
2128 setString(&artwork_new->name, artwork_new->identifier);
2129 setString(&artwork_new->name_sorting, artwork_new->name);
2131 artwork_new->name = getStringCopy(artwork_new->identifier);
2132 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2136 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2138 pushTreeInfo(node_first, artwork_new);
2140 freeSetupFileHash(setup_file_hash);
2142 free(directory_path);
2148 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2149 TreeInfo *node_parent,
2150 char *base_directory, int type)
2153 struct dirent *dir_entry;
2154 boolean valid_entry_found = FALSE;
2156 if ((dir = opendir(base_directory)) == NULL)
2158 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2159 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2163 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2165 struct stat file_status;
2166 char *directory_name = dir_entry->d_name;
2167 char *directory_path = getPath2(base_directory, directory_name);
2169 /* skip entries for current and parent directory */
2170 if (strcmp(directory_name, ".") == 0 ||
2171 strcmp(directory_name, "..") == 0)
2173 free(directory_path);
2177 /* find out if directory entry is itself a directory */
2178 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2179 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2181 free(directory_path);
2185 free(directory_path);
2187 /* check if this directory contains artwork with or without config file */
2188 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2190 directory_name, type);
2195 /* check if this directory directly contains artwork itself */
2196 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2197 base_directory, ".",
2199 if (!valid_entry_found)
2200 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2204 static TreeInfo *getDummyArtworkInfo(int type)
2206 /* this is only needed when there is completely no artwork available */
2207 TreeInfo *artwork_new = newTreeInfo();
2209 setTreeInfoToDefaults(artwork_new, type);
2212 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2213 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2214 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2216 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2217 setString(&artwork_new->name, UNDEFINED_FILENAME);
2218 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2220 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2221 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2222 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2224 if (artwork_new->name != NULL)
2225 free(artwork_new->name);
2227 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2228 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2229 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2235 void LoadArtworkInfo()
2237 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2239 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2240 options.graphics_directory,
2241 TREE_TYPE_GRAPHICS_DIR);
2242 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2243 getUserGraphicsDir(),
2244 TREE_TYPE_GRAPHICS_DIR);
2246 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2247 options.sounds_directory,
2248 TREE_TYPE_SOUNDS_DIR);
2249 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2251 TREE_TYPE_SOUNDS_DIR);
2253 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2254 options.music_directory,
2255 TREE_TYPE_MUSIC_DIR);
2256 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2258 TREE_TYPE_MUSIC_DIR);
2260 if (artwork.gfx_first == NULL)
2261 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2262 if (artwork.snd_first == NULL)
2263 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2264 if (artwork.mus_first == NULL)
2265 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2267 /* before sorting, the first entries will be from the user directory */
2268 artwork.gfx_current =
2269 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2270 if (artwork.gfx_current == NULL)
2271 artwork.gfx_current =
2272 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2273 if (artwork.gfx_current == NULL)
2274 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2276 artwork.snd_current =
2277 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2278 if (artwork.snd_current == NULL)
2279 artwork.snd_current =
2280 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2281 if (artwork.snd_current == NULL)
2282 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2284 artwork.mus_current =
2285 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2286 if (artwork.mus_current == NULL)
2287 artwork.mus_current =
2288 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2289 if (artwork.mus_current == NULL)
2290 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2292 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2293 artwork.snd_current_identifier = artwork.snd_current->identifier;
2294 artwork.mus_current_identifier = artwork.mus_current->identifier;
2297 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2298 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2299 printf("music set == %s\n\n", artwork.mus_current_identifier);
2302 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2303 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2304 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2307 dumpTreeInfo(artwork.gfx_first, 0);
2308 dumpTreeInfo(artwork.snd_first, 0);
2309 dumpTreeInfo(artwork.mus_first, 0);
2313 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2314 LevelDirTree *level_node)
2316 /* recursively check all level directories for artwork sub-directories */
2320 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2321 ARTWORK_DIRECTORY((*artwork_node)->type));
2324 if (!level_node->parent_link)
2325 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2326 level_node->filename, level_node->name);
2329 if (!level_node->parent_link)
2331 TreeInfo *topnode_last = *artwork_node;
2333 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2334 (*artwork_node)->type);
2336 if (topnode_last != *artwork_node)
2338 free((*artwork_node)->identifier);
2339 free((*artwork_node)->name);
2340 free((*artwork_node)->name_sorting);
2342 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2343 (*artwork_node)->name = getStringCopy(level_node->name);
2344 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2346 (*artwork_node)->sort_priority = level_node->sort_priority;
2347 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2353 if (level_node->node_group != NULL)
2354 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2356 level_node = level_node->next;
2360 void LoadLevelArtworkInfo()
2362 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2364 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2365 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2366 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2368 /* needed for reloading level artwork not known at ealier stage */
2370 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2372 artwork.gfx_current =
2373 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2374 if (artwork.gfx_current == NULL)
2375 artwork.gfx_current =
2376 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2377 if (artwork.gfx_current == NULL)
2378 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2381 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2383 artwork.snd_current =
2384 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2385 if (artwork.snd_current == NULL)
2386 artwork.snd_current =
2387 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2388 if (artwork.snd_current == NULL)
2389 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2392 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2394 artwork.mus_current =
2395 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2396 if (artwork.mus_current == NULL)
2397 artwork.mus_current =
2398 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2399 if (artwork.mus_current == NULL)
2400 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2403 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2404 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2405 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2408 dumpTreeInfo(artwork.gfx_first, 0);
2409 dumpTreeInfo(artwork.snd_first, 0);
2410 dumpTreeInfo(artwork.mus_first, 0);
2414 static void SaveUserLevelInfo()
2416 LevelDirTree *level_info;
2421 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2423 if (!(file = fopen(filename, MODE_WRITE)))
2425 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2430 level_info = newTreeInfo();
2432 /* always start with reliable default values */
2433 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2436 setString(&level_info->name, getLoginName());
2437 setString(&level_info->author, getRealName());
2438 level_info->levels = 100;
2439 level_info->first_level = 1;
2440 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2441 level_info->readonly = FALSE;
2442 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2443 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2444 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2446 ldi.name = getStringCopy(getLoginName());
2447 ldi.author = getStringCopy(getRealName());
2449 ldi.first_level = 1;
2450 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2451 ldi.readonly = FALSE;
2452 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2453 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2454 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2457 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2458 getCookie("LEVELINFO")));
2461 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2462 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2463 i != LEVELINFO_TOKEN_NAME_SORTING &&
2464 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2465 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2469 SetFilePermissions(filename, PERMS_PRIVATE);
2471 freeTreeInfo(level_info);
2475 char *getSetupValue(int type, void *value)
2477 static char value_string[MAX_LINE_LEN];
2485 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2489 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2493 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2497 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2501 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2505 sprintf(value_string, "%d", *(int *)value);
2509 strcpy(value_string, *(char **)value);
2513 value_string[0] = '\0';
2517 return value_string;
2520 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2524 static char token_string[MAX_LINE_LEN];
2525 int token_type = token_info[token_nr].type;
2526 void *setup_value = token_info[token_nr].value;
2527 char *token_text = token_info[token_nr].text;
2528 char *value_string = getSetupValue(token_type, setup_value);
2530 /* build complete token string */
2531 sprintf(token_string, "%s%s", prefix, token_text);
2533 /* build setup entry line */
2534 line = getFormattedSetupEntry(token_string, value_string);
2536 if (token_type == TYPE_KEY_X11)
2538 Key key = *(Key *)setup_value;
2539 char *keyname = getKeyNameFromKey(key);
2541 /* add comment, if useful */
2542 if (strcmp(keyname, "(undefined)") != 0 &&
2543 strcmp(keyname, "(unknown)") != 0)
2545 /* add at least one whitespace */
2547 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2551 strcat(line, keyname);
2558 void LoadLevelSetup_LastSeries()
2561 SetupFileHash *level_setup_hash = NULL;
2563 /* always start with reliable default values */
2564 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2566 /* ----------------------------------------------------------------------- */
2567 /* ~/.<program>/levelsetup.conf */
2568 /* ----------------------------------------------------------------------- */
2570 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2572 if ((level_setup_hash = loadSetupFileHash(filename)))
2574 char *last_level_series =
2575 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2577 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2579 if (leveldir_current == NULL)
2580 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2582 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2584 freeSetupFileHash(level_setup_hash);
2587 Error(ERR_WARN, "using default setup values");
2592 void SaveLevelSetup_LastSeries()
2595 char *level_subdir = leveldir_current->filename;
2598 /* ----------------------------------------------------------------------- */
2599 /* ~/.<program>/levelsetup.conf */
2600 /* ----------------------------------------------------------------------- */
2602 InitUserDataDirectory();
2604 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2606 if (!(file = fopen(filename, MODE_WRITE)))
2608 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2613 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2614 getCookie("LEVELSETUP")));
2615 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2620 SetFilePermissions(filename, PERMS_PRIVATE);
2625 static void checkSeriesInfo()
2627 static char *level_directory = NULL;
2629 struct dirent *dir_entry;
2631 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2633 level_directory = getPath2((leveldir_current->user_defined ?
2634 getUserLevelDir(NULL) :
2635 options.level_directory),
2636 leveldir_current->fullpath);
2638 if ((dir = opendir(level_directory)) == NULL)
2640 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2644 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2646 if (strlen(dir_entry->d_name) > 4 &&
2647 dir_entry->d_name[3] == '.' &&
2648 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2650 char levelnum_str[4];
2653 strncpy(levelnum_str, dir_entry->d_name, 3);
2654 levelnum_str[3] = '\0';
2656 levelnum_value = atoi(levelnum_str);
2659 if (levelnum_value < leveldir_current->first_level)
2661 Error(ERR_WARN, "additional level %d found", levelnum_value);
2662 leveldir_current->first_level = levelnum_value;
2664 else if (levelnum_value > leveldir_current->last_level)
2666 Error(ERR_WARN, "additional level %d found", levelnum_value);
2667 leveldir_current->last_level = levelnum_value;
2676 void LoadLevelSetup_SeriesInfo()
2679 SetupFileHash *level_setup_hash = NULL;
2680 char *level_subdir = leveldir_current->filename;
2682 /* always start with reliable default values */
2683 level_nr = leveldir_current->first_level;
2685 checkSeriesInfo(leveldir_current);
2687 /* ----------------------------------------------------------------------- */
2688 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2689 /* ----------------------------------------------------------------------- */
2691 level_subdir = leveldir_current->filename;
2693 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2695 if ((level_setup_hash = loadSetupFileHash(filename)))
2699 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2703 level_nr = atoi(token_value);
2705 if (level_nr < leveldir_current->first_level)
2706 level_nr = leveldir_current->first_level;
2707 if (level_nr > leveldir_current->last_level)
2708 level_nr = leveldir_current->last_level;
2711 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2715 int level_nr = atoi(token_value);
2717 if (level_nr < leveldir_current->first_level)
2718 level_nr = leveldir_current->first_level;
2719 if (level_nr > leveldir_current->last_level + 1)
2720 level_nr = leveldir_current->last_level;
2722 if (leveldir_current->user_defined)
2723 level_nr = leveldir_current->last_level;
2725 leveldir_current->handicap_level = level_nr;
2728 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2730 freeSetupFileHash(level_setup_hash);
2733 Error(ERR_WARN, "using default setup values");
2738 void SaveLevelSetup_SeriesInfo()
2741 char *level_subdir = leveldir_current->filename;
2742 char *level_nr_str = int2str(level_nr, 0);
2743 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2746 /* ----------------------------------------------------------------------- */
2747 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2748 /* ----------------------------------------------------------------------- */
2750 InitLevelSetupDirectory(level_subdir);
2752 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2754 if (!(file = fopen(filename, MODE_WRITE)))
2756 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2761 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2762 getCookie("LEVELSETUP")));
2763 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2765 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2766 handicap_level_str));
2770 SetFilePermissions(filename, PERMS_PRIVATE);