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_CONTRIBUTION(n) ? FC_GREEN : \
49 IS_LEVELCLASS_USER(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_CONTRIBUTION(n) ? 6 : \
59 IS_LEVELCLASS_USER(n) ? 7 : \
62 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
63 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
64 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
65 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
68 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
69 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
70 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
71 IS_ARTWORKCLASS_USER(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);
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;
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_LEVEL_GROUP 8
1409 #define LEVELINFO_TOKEN_READONLY 9
1410 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1411 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1412 #define LEVELINFO_TOKEN_MUSIC_SET 12
1414 #define NUM_LEVELINFO_TOKENS 13
1416 static LevelDirTree ldi;
1418 static struct TokenInfo levelinfo_tokens[] =
1420 /* level directory info */
1421 { TYPE_STRING, &ldi.identifier, "identifier" },
1422 { TYPE_STRING, &ldi.name, "name" },
1423 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1424 { TYPE_STRING, &ldi.author, "author" },
1425 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1426 { TYPE_INTEGER, &ldi.levels, "levels" },
1427 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1428 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1429 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1430 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1431 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1432 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1433 { TYPE_STRING, &ldi.music_set, "music_set" }
1436 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1440 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1441 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1442 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1443 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1446 ldi->node_parent = NULL;
1447 ldi->node_group = NULL;
1451 ldi->cl_cursor = -1;
1453 ldi->filename = NULL;
1454 ldi->fullpath = NULL;
1455 ldi->basepath = NULL;
1456 ldi->identifier = NULL;
1457 ldi->name = getStringCopy(ANONYMOUS_NAME);
1458 ldi->name_sorting = NULL;
1459 ldi->author = getStringCopy(ANONYMOUS_NAME);
1461 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1462 ldi->parent_link = FALSE;
1463 ldi->user_defined = FALSE;
1465 ldi->class_desc = NULL;
1467 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1469 ldi->imported_from = NULL;
1471 ldi->graphics_set = NULL;
1472 ldi->sounds_set = NULL;
1473 ldi->music_set = NULL;
1474 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1475 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1476 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1479 ldi->first_level = 0;
1480 ldi->last_level = 0;
1481 ldi->level_group = FALSE;
1482 ldi->handicap_level = 0;
1483 ldi->readonly = TRUE;
1487 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1491 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1493 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1499 /* copy all values from the parent structure */
1501 ldi->type = parent->type;
1503 ldi->node_top = parent->node_top;
1504 ldi->node_parent = parent;
1505 ldi->node_group = NULL;
1509 ldi->cl_cursor = -1;
1511 ldi->filename = NULL;
1512 ldi->fullpath = NULL;
1513 ldi->basepath = NULL;
1514 ldi->identifier = NULL;
1515 ldi->name = getStringCopy(ANONYMOUS_NAME);
1516 ldi->name_sorting = NULL;
1517 ldi->author = getStringCopy(parent->author);
1519 ldi->sort_priority = parent->sort_priority;
1520 ldi->parent_link = FALSE;
1521 ldi->user_defined = parent->user_defined;
1522 ldi->color = parent->color;
1523 ldi->class_desc = getStringCopy(parent->class_desc);
1525 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1527 ldi->imported_from = getStringCopy(parent->imported_from);
1529 ldi->graphics_set = NULL;
1530 ldi->sounds_set = NULL;
1531 ldi->music_set = NULL;
1532 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1533 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1534 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1537 ldi->first_level = 0;
1538 ldi->last_level = 0;
1539 ldi->level_group = FALSE;
1540 ldi->handicap_level = 0;
1541 ldi->readonly = TRUE;
1547 /* first copy all values from the parent structure ... */
1550 /* ... then set all fields to default that cannot be inherited from parent.
1551 This is especially important for all those fields that can be set from
1552 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1553 calls 'free()' for all already set token values which requires that no
1554 other structure's pointer may point to them!
1557 ldi->filename = NULL;
1558 ldi->fullpath = NULL;
1559 ldi->basepath = NULL;
1560 ldi->identifier = NULL;
1561 ldi->name = getStringCopy(ANONYMOUS_NAME);
1562 ldi->name_sorting = NULL;
1563 ldi->author = getStringCopy(parent->author);
1565 ldi->imported_from = getStringCopy(parent->imported_from);
1566 ldi->class_desc = getStringCopy(parent->class_desc);
1568 ldi->graphics_set = NULL;
1569 ldi->sounds_set = NULL;
1570 ldi->music_set = NULL;
1571 ldi->graphics_path = NULL;
1572 ldi->sounds_path = NULL;
1573 ldi->music_path = NULL;
1575 ldi->level_group = FALSE;
1576 ldi->parent_link = FALSE;
1578 ldi->node_top = parent->node_top;
1579 ldi->node_parent = parent;
1580 ldi->node_group = NULL;
1586 static void freeTreeInfo(TreeInfo *ldi)
1589 free(ldi->filename);
1591 free(ldi->fullpath);
1593 free(ldi->basepath);
1594 if (ldi->identifier)
1595 free(ldi->identifier);
1599 if (ldi->name_sorting)
1600 free(ldi->name_sorting);
1604 if (ldi->class_desc)
1605 free(ldi->class_desc);
1607 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1609 if (ldi->graphics_set)
1610 free(ldi->graphics_set);
1611 if (ldi->sounds_set)
1612 free(ldi->sounds_set);
1614 free(ldi->music_set);
1616 if (ldi->graphics_path)
1617 free(ldi->graphics_path);
1618 if (ldi->sounds_path)
1619 free(ldi->sounds_path);
1620 if (ldi->music_path)
1621 free(ldi->music_path);
1625 void setSetupInfo(struct TokenInfo *token_info,
1626 int token_nr, char *token_value)
1628 int token_type = token_info[token_nr].type;
1629 void *setup_value = token_info[token_nr].value;
1631 if (token_value == NULL)
1634 /* set setup field to corresponding token value */
1639 *(boolean *)setup_value = get_boolean_from_string(token_value);
1643 *(Key *)setup_value = getKeyFromKeyName(token_value);
1647 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1651 *(int *)setup_value = get_integer_from_string(token_value);
1655 if (*(char **)setup_value != NULL)
1656 free(*(char **)setup_value);
1657 *(char **)setup_value = getStringCopy(token_value);
1665 static int compareTreeInfoEntries(const void *object1, const void *object2)
1667 const TreeInfo *entry1 = *((TreeInfo **)object1);
1668 const TreeInfo *entry2 = *((TreeInfo **)object2);
1669 int class_sorting1, class_sorting2;
1672 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1674 class_sorting1 = LEVELSORTING(entry1);
1675 class_sorting2 = LEVELSORTING(entry2);
1679 class_sorting1 = ARTWORKSORTING(entry1);
1680 class_sorting2 = ARTWORKSORTING(entry2);
1683 if (entry1->parent_link || entry2->parent_link)
1684 compare_result = (entry1->parent_link ? -1 : +1);
1685 else if (entry1->sort_priority == entry2->sort_priority)
1687 char *name1 = getStringToLower(entry1->name_sorting);
1688 char *name2 = getStringToLower(entry2->name_sorting);
1690 compare_result = strcmp(name1, name2);
1695 else if (class_sorting1 == class_sorting2)
1696 compare_result = entry1->sort_priority - entry2->sort_priority;
1698 compare_result = class_sorting1 - class_sorting2;
1700 return compare_result;
1703 static void createParentTreeInfoNode(TreeInfo *node_parent)
1707 if (node_parent == NULL)
1710 ti_new = newTreeInfo();
1711 setTreeInfoToDefaults(ti_new, node_parent->type);
1713 ti_new->node_parent = node_parent;
1714 ti_new->parent_link = TRUE;
1717 setString(&ti_new->identifier, node_parent->identifier);
1718 setString(&ti_new->name, ".. (parent directory)");
1719 setString(&ti_new->name_sorting, ti_new->name);
1721 setString(&ti_new->filename, "..");
1722 setString(&ti_new->fullpath, node_parent->fullpath);
1724 ti_new->sort_priority = node_parent->sort_priority;
1726 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1728 ti_new->identifier = getStringCopy(node_parent->identifier);
1729 ti_new->name = ".. (parent directory)";
1730 ti_new->name_sorting = getStringCopy(ti_new->name);
1732 ti_new->filename = "..";
1733 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1735 ti_new->sort_priority = node_parent->sort_priority;
1737 ti_new->class_desc = getLevelClassDescription(ti_new);
1740 pushTreeInfo(&node_parent->node_group, ti_new);
1743 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1744 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1746 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1747 TreeInfo *node_parent,
1748 char *level_directory,
1749 char *directory_name)
1751 char *directory_path = getPath2(level_directory, directory_name);
1752 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1753 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1754 LevelDirTree *leveldir_new = NULL;
1757 if (setup_file_hash == NULL)
1759 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1761 free(directory_path);
1767 leveldir_new = newTreeInfo();
1770 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1772 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1774 leveldir_new->filename = getStringCopy(directory_name);
1776 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1778 /* set all structure fields according to the token/value pairs */
1779 ldi = *leveldir_new;
1780 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1781 setSetupInfo(levelinfo_tokens, i,
1782 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1783 *leveldir_new = ldi;
1786 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1787 setString(&leveldir_new->name, leveldir_new->filename);
1789 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1791 free(leveldir_new->name);
1792 leveldir_new->name = getStringCopy(leveldir_new->filename);
1796 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1798 if (leveldir_new->identifier == NULL)
1799 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1801 if (leveldir_new->name_sorting == NULL)
1802 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1804 if (node_parent == NULL) /* top level group */
1806 leveldir_new->basepath = getStringCopy(level_directory);
1807 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1809 else /* sub level group */
1811 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1812 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1815 if (leveldir_new->levels < 1)
1816 leveldir_new->levels = 1;
1818 leveldir_new->last_level =
1819 leveldir_new->first_level + leveldir_new->levels - 1;
1822 leveldir_new->user_defined =
1823 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1825 leveldir_new->user_defined =
1826 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1829 leveldir_new->color = LEVELCOLOR(leveldir_new);
1831 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1833 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1836 leveldir_new->handicap_level = /* set handicap to default value */
1837 (leveldir_new->user_defined ?
1838 leveldir_new->last_level :
1839 leveldir_new->first_level);
1841 pushTreeInfo(node_first, leveldir_new);
1843 freeSetupFileHash(setup_file_hash);
1845 if (leveldir_new->level_group)
1847 /* create node to link back to current level directory */
1848 createParentTreeInfoNode(leveldir_new);
1850 /* step into sub-directory and look for more level series */
1851 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1852 leveldir_new, directory_path);
1855 free(directory_path);
1861 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1862 TreeInfo *node_parent,
1863 char *level_directory)
1866 struct dirent *dir_entry;
1867 boolean valid_entry_found = FALSE;
1869 if ((dir = opendir(level_directory)) == NULL)
1871 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1875 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1877 struct stat file_status;
1878 char *directory_name = dir_entry->d_name;
1879 char *directory_path = getPath2(level_directory, directory_name);
1881 /* skip entries for current and parent directory */
1882 if (strcmp(directory_name, ".") == 0 ||
1883 strcmp(directory_name, "..") == 0)
1885 free(directory_path);
1889 /* find out if directory entry is itself a directory */
1890 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1891 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1893 free(directory_path);
1897 free(directory_path);
1899 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1900 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1901 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1904 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1911 if (!valid_entry_found)
1913 /* check if this directory directly contains a file "levelinfo.conf" */
1914 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1915 level_directory, ".");
1918 if (!valid_entry_found)
1919 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1923 void LoadLevelInfo()
1925 InitUserLevelDirectory(getLoginName());
1927 DrawInitText("Loading level series:", 120, FC_GREEN);
1929 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1930 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1932 /* before sorting, the first entries will be from the user directory */
1933 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1935 if (leveldir_first == NULL)
1936 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1938 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1941 dumpTreeInfo(leveldir_first, 0);
1945 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1946 TreeInfo *node_parent,
1947 char *base_directory,
1948 char *directory_name, int type)
1950 char *directory_path = getPath2(base_directory, directory_name);
1951 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1952 SetupFileHash *setup_file_hash = NULL;
1953 TreeInfo *artwork_new = NULL;
1956 if (access(filename, F_OK) == 0) /* file exists */
1957 setup_file_hash = loadSetupFileHash(filename);
1959 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1962 struct dirent *dir_entry;
1963 boolean valid_file_found = FALSE;
1965 if ((dir = opendir(directory_path)) != NULL)
1967 while ((dir_entry = readdir(dir)) != NULL)
1969 char *entry_name = dir_entry->d_name;
1971 if (FileIsArtworkType(entry_name, type))
1973 valid_file_found = TRUE;
1981 if (!valid_file_found)
1983 if (strcmp(directory_name, ".") != 0)
1984 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1986 free(directory_path);
1993 artwork_new = newTreeInfo();
1996 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1998 setTreeInfoToDefaults(artwork_new, type);
2000 artwork_new->filename = getStringCopy(directory_name);
2002 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2005 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2008 /* set all structure fields according to the token/value pairs */
2010 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2011 setSetupInfo(levelinfo_tokens, i,
2012 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2016 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2017 setString(&artwork_new->name, artwork_new->filename);
2019 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2021 free(artwork_new->name);
2022 artwork_new->name = getStringCopy(artwork_new->filename);
2027 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2030 if (artwork_new->identifier == NULL)
2031 artwork_new->identifier = getStringCopy(artwork_new->filename);
2033 if (artwork_new->name_sorting == NULL)
2034 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2037 if (node_parent == NULL) /* top level group */
2039 artwork_new->basepath = getStringCopy(base_directory);
2040 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2042 else /* sub level group */
2044 artwork_new->basepath = getStringCopy(node_parent->basepath);
2045 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2049 artwork_new->user_defined =
2050 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2052 artwork_new->user_defined =
2053 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2056 /* (may use ".sort_priority" from "setup_file_hash" above) */
2057 artwork_new->color = ARTWORKCOLOR(artwork_new);
2059 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2061 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2064 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2067 if (artwork_new->name != NULL)
2069 free(artwork_new->name);
2070 artwork_new->name = NULL;
2075 if (artwork_new->identifier != NULL)
2077 free(artwork_new->identifier);
2078 artwork_new->identifier = NULL;
2082 if (strcmp(artwork_new->filename, ".") == 0)
2084 if (artwork_new->user_defined)
2087 setString(&artwork_new->identifier, "private");
2089 artwork_new->identifier = getStringCopy("private");
2091 artwork_new->sort_priority = ARTWORKCLASS_USER;
2096 setString(&artwork_new->identifier, "classic");
2098 artwork_new->identifier = getStringCopy("classic");
2100 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2103 /* set to new values after changing ".sort_priority" */
2104 artwork_new->color = ARTWORKCOLOR(artwork_new);
2106 setString(&artwork_new->class_desc,
2107 getLevelClassDescription(artwork_new));
2109 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2115 setString(&artwork_new->identifier, artwork_new->filename);
2117 artwork_new->identifier = getStringCopy(artwork_new->filename);
2122 setString(&artwork_new->name, artwork_new->identifier);
2123 setString(&artwork_new->name_sorting, artwork_new->name);
2125 artwork_new->name = getStringCopy(artwork_new->identifier);
2126 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2130 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2132 pushTreeInfo(node_first, artwork_new);
2134 freeSetupFileHash(setup_file_hash);
2136 free(directory_path);
2142 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2143 TreeInfo *node_parent,
2144 char *base_directory, int type)
2147 struct dirent *dir_entry;
2148 boolean valid_entry_found = FALSE;
2150 if ((dir = opendir(base_directory)) == NULL)
2152 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2153 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2157 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2159 struct stat file_status;
2160 char *directory_name = dir_entry->d_name;
2161 char *directory_path = getPath2(base_directory, directory_name);
2163 /* skip entries for current and parent directory */
2164 if (strcmp(directory_name, ".") == 0 ||
2165 strcmp(directory_name, "..") == 0)
2167 free(directory_path);
2171 /* find out if directory entry is itself a directory */
2172 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2173 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2175 free(directory_path);
2179 free(directory_path);
2181 /* check if this directory contains artwork with or without config file */
2182 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2184 directory_name, type);
2189 /* check if this directory directly contains artwork itself */
2190 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2191 base_directory, ".",
2193 if (!valid_entry_found)
2194 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2198 static TreeInfo *getDummyArtworkInfo(int type)
2200 /* this is only needed when there is completely no artwork available */
2201 TreeInfo *artwork_new = newTreeInfo();
2203 setTreeInfoToDefaults(artwork_new, type);
2206 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2207 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2208 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2210 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2211 setString(&artwork_new->name, UNDEFINED_FILENAME);
2212 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2214 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2215 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2216 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2218 if (artwork_new->name != NULL)
2219 free(artwork_new->name);
2221 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2222 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2223 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2229 void LoadArtworkInfo()
2231 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2233 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2234 options.graphics_directory,
2235 TREE_TYPE_GRAPHICS_DIR);
2236 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2237 getUserGraphicsDir(),
2238 TREE_TYPE_GRAPHICS_DIR);
2240 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2241 options.sounds_directory,
2242 TREE_TYPE_SOUNDS_DIR);
2243 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2245 TREE_TYPE_SOUNDS_DIR);
2247 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2248 options.music_directory,
2249 TREE_TYPE_MUSIC_DIR);
2250 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2252 TREE_TYPE_MUSIC_DIR);
2254 if (artwork.gfx_first == NULL)
2255 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2256 if (artwork.snd_first == NULL)
2257 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2258 if (artwork.mus_first == NULL)
2259 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2261 /* before sorting, the first entries will be from the user directory */
2262 artwork.gfx_current =
2263 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2264 if (artwork.gfx_current == NULL)
2265 artwork.gfx_current =
2266 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2267 if (artwork.gfx_current == NULL)
2268 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2270 artwork.snd_current =
2271 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2272 if (artwork.snd_current == NULL)
2273 artwork.snd_current =
2274 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2275 if (artwork.snd_current == NULL)
2276 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2278 artwork.mus_current =
2279 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2280 if (artwork.mus_current == NULL)
2281 artwork.mus_current =
2282 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2283 if (artwork.mus_current == NULL)
2284 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2286 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2287 artwork.snd_current_identifier = artwork.snd_current->identifier;
2288 artwork.mus_current_identifier = artwork.mus_current->identifier;
2291 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2292 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2293 printf("music set == %s\n\n", artwork.mus_current_identifier);
2296 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2297 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2298 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2301 dumpTreeInfo(artwork.gfx_first, 0);
2302 dumpTreeInfo(artwork.snd_first, 0);
2303 dumpTreeInfo(artwork.mus_first, 0);
2307 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2308 LevelDirTree *level_node)
2310 /* recursively check all level directories for artwork sub-directories */
2314 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2315 ARTWORK_DIRECTORY((*artwork_node)->type));
2318 if (!level_node->parent_link)
2319 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2320 level_node->filename, level_node->name);
2323 if (!level_node->parent_link)
2325 TreeInfo *topnode_last = *artwork_node;
2327 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2328 (*artwork_node)->type);
2330 if (topnode_last != *artwork_node)
2332 free((*artwork_node)->identifier);
2333 free((*artwork_node)->name);
2334 free((*artwork_node)->name_sorting);
2336 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2337 (*artwork_node)->name = getStringCopy(level_node->name);
2338 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2340 (*artwork_node)->sort_priority = level_node->sort_priority;
2341 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2347 if (level_node->node_group != NULL)
2348 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2350 level_node = level_node->next;
2354 void LoadLevelArtworkInfo()
2356 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2358 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2359 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2360 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2362 /* needed for reloading level artwork not known at ealier stage */
2364 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2366 artwork.gfx_current =
2367 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2368 if (artwork.gfx_current == NULL)
2369 artwork.gfx_current =
2370 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2371 if (artwork.gfx_current == NULL)
2372 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2375 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2377 artwork.snd_current =
2378 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2379 if (artwork.snd_current == NULL)
2380 artwork.snd_current =
2381 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2382 if (artwork.snd_current == NULL)
2383 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2386 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2388 artwork.mus_current =
2389 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2390 if (artwork.mus_current == NULL)
2391 artwork.mus_current =
2392 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2393 if (artwork.mus_current == NULL)
2394 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2397 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2398 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2399 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2402 dumpTreeInfo(artwork.gfx_first, 0);
2403 dumpTreeInfo(artwork.snd_first, 0);
2404 dumpTreeInfo(artwork.mus_first, 0);
2408 static void SaveUserLevelInfo()
2410 LevelDirTree *level_info;
2415 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2417 if (!(file = fopen(filename, MODE_WRITE)))
2419 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2424 level_info = newTreeInfo();
2426 /* always start with reliable default values */
2427 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2430 setString(&level_info->name, getLoginName());
2431 setString(&level_info->author, getRealName());
2432 level_info->levels = 100;
2433 level_info->first_level = 1;
2434 level_info->sort_priority = LEVELCLASS_USER_START;
2435 level_info->readonly = FALSE;
2436 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2437 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2438 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2440 ldi.name = getStringCopy(getLoginName());
2441 ldi.author = getStringCopy(getRealName());
2443 ldi.first_level = 1;
2444 ldi.sort_priority = LEVELCLASS_USER_START;
2445 ldi.readonly = FALSE;
2446 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2447 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2448 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2451 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2452 getCookie("LEVELINFO")));
2455 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2456 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2457 i != LEVELINFO_TOKEN_NAME_SORTING &&
2458 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2459 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2463 SetFilePermissions(filename, PERMS_PRIVATE);
2465 freeTreeInfo(level_info);
2469 char *getSetupValue(int type, void *value)
2471 static char value_string[MAX_LINE_LEN];
2479 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2483 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2487 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2491 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2495 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2499 sprintf(value_string, "%d", *(int *)value);
2503 strcpy(value_string, *(char **)value);
2507 value_string[0] = '\0';
2511 return value_string;
2514 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2518 static char token_string[MAX_LINE_LEN];
2519 int token_type = token_info[token_nr].type;
2520 void *setup_value = token_info[token_nr].value;
2521 char *token_text = token_info[token_nr].text;
2522 char *value_string = getSetupValue(token_type, setup_value);
2524 /* build complete token string */
2525 sprintf(token_string, "%s%s", prefix, token_text);
2527 /* build setup entry line */
2528 line = getFormattedSetupEntry(token_string, value_string);
2530 if (token_type == TYPE_KEY_X11)
2532 Key key = *(Key *)setup_value;
2533 char *keyname = getKeyNameFromKey(key);
2535 /* add comment, if useful */
2536 if (strcmp(keyname, "(undefined)") != 0 &&
2537 strcmp(keyname, "(unknown)") != 0)
2539 /* add at least one whitespace */
2541 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2545 strcat(line, keyname);
2552 void LoadLevelSetup_LastSeries()
2555 SetupFileHash *level_setup_hash = NULL;
2557 /* always start with reliable default values */
2558 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2560 /* ----------------------------------------------------------------------- */
2561 /* ~/.<program>/levelsetup.conf */
2562 /* ----------------------------------------------------------------------- */
2564 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2566 if ((level_setup_hash = loadSetupFileHash(filename)))
2568 char *last_level_series =
2569 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2571 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2573 if (leveldir_current == NULL)
2574 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2576 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2578 freeSetupFileHash(level_setup_hash);
2581 Error(ERR_WARN, "using default setup values");
2586 void SaveLevelSetup_LastSeries()
2589 char *level_subdir = leveldir_current->filename;
2592 /* ----------------------------------------------------------------------- */
2593 /* ~/.<program>/levelsetup.conf */
2594 /* ----------------------------------------------------------------------- */
2596 InitUserDataDirectory();
2598 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2600 if (!(file = fopen(filename, MODE_WRITE)))
2602 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2607 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2608 getCookie("LEVELSETUP")));
2609 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2614 SetFilePermissions(filename, PERMS_PRIVATE);
2619 static void checkSeriesInfo()
2621 static char *level_directory = NULL;
2623 struct dirent *dir_entry;
2625 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2627 level_directory = getPath2((leveldir_current->user_defined ?
2628 getUserLevelDir(NULL) :
2629 options.level_directory),
2630 leveldir_current->fullpath);
2632 if ((dir = opendir(level_directory)) == NULL)
2634 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2638 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2640 if (strlen(dir_entry->d_name) > 4 &&
2641 dir_entry->d_name[3] == '.' &&
2642 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2644 char levelnum_str[4];
2647 strncpy(levelnum_str, dir_entry->d_name, 3);
2648 levelnum_str[3] = '\0';
2650 levelnum_value = atoi(levelnum_str);
2653 if (levelnum_value < leveldir_current->first_level)
2655 Error(ERR_WARN, "additional level %d found", levelnum_value);
2656 leveldir_current->first_level = levelnum_value;
2658 else if (levelnum_value > leveldir_current->last_level)
2660 Error(ERR_WARN, "additional level %d found", levelnum_value);
2661 leveldir_current->last_level = levelnum_value;
2670 void LoadLevelSetup_SeriesInfo()
2673 SetupFileHash *level_setup_hash = NULL;
2674 char *level_subdir = leveldir_current->filename;
2676 /* always start with reliable default values */
2677 level_nr = leveldir_current->first_level;
2679 checkSeriesInfo(leveldir_current);
2681 /* ----------------------------------------------------------------------- */
2682 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2683 /* ----------------------------------------------------------------------- */
2685 level_subdir = leveldir_current->filename;
2687 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2689 if ((level_setup_hash = loadSetupFileHash(filename)))
2693 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2697 level_nr = atoi(token_value);
2699 if (level_nr < leveldir_current->first_level)
2700 level_nr = leveldir_current->first_level;
2701 if (level_nr > leveldir_current->last_level)
2702 level_nr = leveldir_current->last_level;
2705 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2709 int level_nr = atoi(token_value);
2711 if (level_nr < leveldir_current->first_level)
2712 level_nr = leveldir_current->first_level;
2713 if (level_nr > leveldir_current->last_level + 1)
2714 level_nr = leveldir_current->last_level;
2716 if (leveldir_current->user_defined)
2717 level_nr = leveldir_current->last_level;
2719 leveldir_current->handicap_level = level_nr;
2722 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2724 freeSetupFileHash(level_setup_hash);
2727 Error(ERR_WARN, "using default setup values");
2732 void SaveLevelSetup_SeriesInfo()
2735 char *level_subdir = leveldir_current->filename;
2736 char *level_nr_str = int2str(level_nr, 0);
2737 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2740 /* ----------------------------------------------------------------------- */
2741 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2742 /* ----------------------------------------------------------------------- */
2744 InitLevelSetupDirectory(level_subdir);
2746 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2748 if (!(file = fopen(filename, MODE_WRITE)))
2750 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2755 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2756 getCookie("LEVELSETUP")));
2757 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2759 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2760 handicap_level_str));
2764 SetFilePermissions(filename, PERMS_PRIVATE);