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 *getCustomMusicFilename(char *basename)
561 static char *filename = NULL;
562 boolean skip_setup_artwork = FALSE;
564 if (filename != NULL)
567 basename = getCorrectedArtworkBasename(basename);
569 if (!setup.override_level_music)
571 /* 1st try: look for special artwork in current level series directory */
572 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
573 if (fileExists(filename))
578 /* check if there is special artwork configured in level series config */
579 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
581 /* 2nd try: look for special artwork configured in level series config */
582 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
583 if (fileExists(filename))
588 /* take missing artwork configured in level set config from default */
589 skip_setup_artwork = TRUE;
593 if (!skip_setup_artwork)
595 /* 3rd try: look for special artwork in configured artwork directory */
596 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
597 if (fileExists(filename))
603 /* 4th try: look for default artwork in new default artwork directory */
604 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
605 if (fileExists(filename))
610 /* 5th try: look for default artwork in old default artwork directory */
611 filename = getPath2(options.music_directory, basename);
612 if (fileExists(filename))
615 return NULL; /* cannot find specified artwork file anywhere */
618 char *getCustomArtworkFilename(char *basename, int type)
620 if (type == ARTWORK_TYPE_GRAPHICS)
621 return getCustomImageFilename(basename);
622 else if (type == ARTWORK_TYPE_SOUNDS)
623 return getCustomSoundFilename(basename);
624 else if (type == ARTWORK_TYPE_MUSIC)
625 return getCustomMusicFilename(basename);
627 return UNDEFINED_FILENAME;
630 char *getCustomArtworkConfigFilename(int type)
632 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
635 char *getCustomArtworkLevelConfigFilename(int type)
637 static char *filename = NULL;
639 if (filename != NULL)
642 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
647 char *getCustomMusicDirectory(void)
649 static char *directory = NULL;
650 boolean skip_setup_artwork = FALSE;
652 if (directory != NULL)
655 if (!setup.override_level_music)
657 /* 1st try: look for special artwork in current level series directory */
658 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
659 if (fileExists(directory))
664 /* check if there is special artwork configured in level series config */
665 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
667 /* 2nd try: look for special artwork configured in level series config */
668 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
669 if (fileExists(directory))
674 /* take missing artwork configured in level set config from default */
675 skip_setup_artwork = TRUE;
679 if (!skip_setup_artwork)
681 /* 3rd try: look for special artwork in configured artwork directory */
682 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
683 if (fileExists(directory))
689 /* 4th try: look for default artwork in new default artwork directory */
690 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
691 if (fileExists(directory))
696 /* 5th try: look for default artwork in old default artwork directory */
697 directory = getStringCopy(options.music_directory);
698 if (fileExists(directory))
701 return NULL; /* cannot find specified artwork file anywhere */
704 void InitTapeDirectory(char *level_subdir)
706 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
707 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
708 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
711 void InitScoreDirectory(char *level_subdir)
713 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
714 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
715 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
718 static void SaveUserLevelInfo();
720 void InitUserLevelDirectory(char *level_subdir)
722 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
724 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
725 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
726 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
732 void InitLevelSetupDirectory(char *level_subdir)
734 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
735 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
736 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
740 /* ------------------------------------------------------------------------- */
741 /* some functions to handle lists of level directories */
742 /* ------------------------------------------------------------------------- */
744 TreeInfo *newTreeInfo()
746 return checked_calloc(sizeof(TreeInfo));
749 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
751 node_new->next = *node_first;
752 *node_first = node_new;
755 int numTreeInfo(TreeInfo *node)
768 boolean validLevelSeries(TreeInfo *node)
770 return (node != NULL && !node->node_group && !node->parent_link);
773 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
778 if (node->node_group) /* enter level group (step down into tree) */
779 return getFirstValidTreeInfoEntry(node->node_group);
780 else if (node->parent_link) /* skip start entry of level group */
782 if (node->next) /* get first real level series entry */
783 return getFirstValidTreeInfoEntry(node->next);
784 else /* leave empty level group and go on */
785 return getFirstValidTreeInfoEntry(node->node_parent->next);
787 else /* this seems to be a regular level series */
791 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
796 if (node->node_parent == NULL) /* top level group */
797 return *node->node_top;
798 else /* sub level group */
799 return node->node_parent->node_group;
802 int numTreeInfoInGroup(TreeInfo *node)
804 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
807 int posTreeInfo(TreeInfo *node)
809 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
814 if (node_cmp == node)
818 node_cmp = node_cmp->next;
824 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
826 TreeInfo *node_default = node;
841 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
843 if (identifier == NULL)
848 if (node->node_group)
850 TreeInfo *node_group;
852 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
857 else if (!node->parent_link)
859 if (strcmp(identifier, node->identifier) == 0)
869 void dumpTreeInfo(TreeInfo *node, int depth)
873 printf("Dumping TreeInfo:\n");
877 for (i=0; i<(depth + 1) * 3; i++)
881 printf("filename == '%s' ['%s', '%s'] [%d])\n",
882 node->filename, node->fullpath, node->basepath, node->user_defined);
884 printf("filename == '%s' (%s) [%s] (%d)\n",
885 node->filename, node->name, node->identifier, node->sort_priority);
888 if (node->node_group != NULL)
889 dumpTreeInfo(node->node_group, depth + 1);
895 void sortTreeInfo(TreeInfo **node_first,
896 int (*compare_function)(const void *, const void *))
898 int num_nodes = numTreeInfo(*node_first);
899 TreeInfo **sort_array;
900 TreeInfo *node = *node_first;
906 /* allocate array for sorting structure pointers */
907 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
909 /* writing structure pointers to sorting array */
910 while (i < num_nodes && node) /* double boundary check... */
912 sort_array[i] = node;
918 /* sorting the structure pointers in the sorting array */
919 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
922 /* update the linkage of list elements with the sorted node array */
923 for (i=0; i<num_nodes - 1; i++)
924 sort_array[i]->next = sort_array[i + 1];
925 sort_array[num_nodes - 1]->next = NULL;
927 /* update the linkage of the main list anchor pointer */
928 *node_first = sort_array[0];
932 /* now recursively sort the level group structures */
936 if (node->node_group != NULL)
937 sortTreeInfo(&node->node_group, compare_function);
944 /* ========================================================================= */
945 /* some stuff from "files.c" */
946 /* ========================================================================= */
948 #if defined(PLATFORM_WIN32)
950 #define S_IRGRP S_IRUSR
953 #define S_IROTH S_IRUSR
956 #define S_IWGRP S_IWUSR
959 #define S_IWOTH S_IWUSR
962 #define S_IXGRP S_IXUSR
965 #define S_IXOTH S_IXUSR
968 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
973 #endif /* PLATFORM_WIN32 */
975 /* file permissions for newly written files */
976 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
977 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
978 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
980 #define MODE_W_PRIVATE (S_IWUSR)
981 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
982 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
984 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
985 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
987 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
988 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
990 char *getUserDataDir(void)
992 static char *userdata_dir = NULL;
994 if (userdata_dir == NULL)
995 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1000 char *getCommonDataDir(void)
1002 static char *common_data_dir = NULL;
1004 #if defined(PLATFORM_WIN32)
1005 if (common_data_dir == NULL)
1007 char *dir = checked_malloc(MAX_PATH + 1);
1009 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1010 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1011 common_data_dir = getPath2(dir, program.userdata_directory);
1013 common_data_dir = options.rw_base_directory;
1016 if (common_data_dir == NULL)
1017 common_data_dir = options.rw_base_directory;
1020 return common_data_dir;
1025 return getUserDataDir();
1028 static mode_t posix_umask(mode_t mask)
1030 #if defined(PLATFORM_UNIX)
1037 static int posix_mkdir(const char *pathname, mode_t mode)
1039 #if defined(PLATFORM_WIN32)
1040 return mkdir(pathname);
1042 return mkdir(pathname, mode);
1046 void createDirectory(char *dir, char *text, int permission_class)
1048 /* leave "other" permissions in umask untouched, but ensure group parts
1049 of USERDATA_DIR_MODE are not masked */
1050 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1051 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1052 mode_t normal_umask = posix_umask(0);
1053 mode_t group_umask = ~(dir_mode & S_IRWXG);
1054 posix_umask(normal_umask & group_umask);
1056 if (access(dir, F_OK) != 0)
1057 if (posix_mkdir(dir, dir_mode) != 0)
1058 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1060 posix_umask(normal_umask); /* reset normal umask */
1063 void InitUserDataDirectory()
1065 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1068 void SetFilePermissions(char *filename, int permission_class)
1070 chmod(filename, (permission_class == PERMS_PRIVATE ?
1071 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1074 char *getCookie(char *file_type)
1076 static char cookie[MAX_COOKIE_LEN + 1];
1078 if (strlen(program.cookie_prefix) + 1 +
1079 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1080 return "[COOKIE ERROR]"; /* should never happen */
1082 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1083 program.cookie_prefix, file_type,
1084 program.version_major, program.version_minor);
1089 int getFileVersionFromCookieString(const char *cookie)
1091 const char *ptr_cookie1, *ptr_cookie2;
1092 const char *pattern1 = "_FILE_VERSION_";
1093 const char *pattern2 = "?.?";
1094 const int len_cookie = strlen(cookie);
1095 const int len_pattern1 = strlen(pattern1);
1096 const int len_pattern2 = strlen(pattern2);
1097 const int len_pattern = len_pattern1 + len_pattern2;
1098 int version_major, version_minor;
1100 if (len_cookie <= len_pattern)
1103 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1104 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1106 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1109 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1110 ptr_cookie2[1] != '.' ||
1111 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1114 version_major = ptr_cookie2[0] - '0';
1115 version_minor = ptr_cookie2[2] - '0';
1117 return VERSION_IDENT(version_major, version_minor, 0, 0);
1120 boolean checkCookieString(const char *cookie, const char *template)
1122 const char *pattern = "_FILE_VERSION_?.?";
1123 const int len_cookie = strlen(cookie);
1124 const int len_template = strlen(template);
1125 const int len_pattern = strlen(pattern);
1127 if (len_cookie != len_template)
1130 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1136 /* ------------------------------------------------------------------------- */
1137 /* setup file list and hash handling functions */
1138 /* ------------------------------------------------------------------------- */
1140 char *getFormattedSetupEntry(char *token, char *value)
1143 static char entry[MAX_LINE_LEN];
1145 /* start with the token and some spaces to format output line */
1146 sprintf(entry, "%s:", token);
1147 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1150 /* continue with the token's value */
1151 strcat(entry, value);
1156 SetupFileList *newSetupFileList(char *token, char *value)
1158 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1160 new->token = getStringCopy(token);
1161 new->value = getStringCopy(value);
1168 void freeSetupFileList(SetupFileList *list)
1178 freeSetupFileList(list->next);
1182 char *getListEntry(SetupFileList *list, char *token)
1187 if (strcmp(list->token, token) == 0)
1190 return getListEntry(list->next, token);
1193 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1198 if (strcmp(list->token, token) == 0)
1203 list->value = getStringCopy(value);
1207 else if (list->next == NULL)
1208 return (list->next = newSetupFileList(token, value));
1210 return setListEntry(list->next, token, value);
1214 static void printSetupFileList(SetupFileList *list)
1219 printf("token: '%s'\n", list->token);
1220 printf("value: '%s'\n", list->value);
1222 printSetupFileList(list->next);
1227 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1228 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1229 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1230 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1232 #define insert_hash_entry hashtable_insert
1233 #define search_hash_entry hashtable_search
1234 #define change_hash_entry hashtable_change
1235 #define remove_hash_entry hashtable_remove
1238 static unsigned int get_hash_from_key(void *key)
1243 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1244 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1245 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1246 it works better than many other constants, prime or not) has never been
1247 adequately explained.
1249 If you just want to have a good hash function, and cannot wait, djb2
1250 is one of the best string hash functions i know. It has excellent
1251 distribution and speed on many different sets of keys and table sizes.
1252 You are not likely to do better with one of the "well known" functions
1253 such as PJW, K&R, etc.
1255 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1258 char *str = (char *)key;
1259 unsigned int hash = 5381;
1262 while ((c = *str++))
1263 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1268 static int keys_are_equal(void *key1, void *key2)
1270 return (strcmp((char *)key1, (char *)key2) == 0);
1273 SetupFileHash *newSetupFileHash()
1275 SetupFileHash *new_hash =
1276 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1281 void freeSetupFileHash(SetupFileHash *hash)
1286 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1289 char *getHashEntry(SetupFileHash *hash, char *token)
1294 return search_hash_entry(hash, token);
1297 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1304 value_copy = getStringCopy(value);
1306 /* change value; if it does not exist, insert it as new */
1307 if (!change_hash_entry(hash, token, value_copy))
1308 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1309 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1314 static void printSetupFileHash(SetupFileHash *hash)
1316 BEGIN_HASH_ITERATION(hash, itr)
1318 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1319 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1321 END_HASH_ITERATION(hash, itr)
1326 static void *loadSetupFileData(char *filename, boolean use_hash)
1329 char line[MAX_LINE_LEN];
1330 char *token, *value, *line_ptr;
1331 void *setup_file_data, *insert_ptr = NULL;
1335 setup_file_data = newSetupFileHash();
1337 insert_ptr = setup_file_data = newSetupFileList("", "");
1339 if (!(file = fopen(filename, MODE_READ)))
1341 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1347 /* read next line of input file */
1348 if (!fgets(line, MAX_LINE_LEN, file))
1351 /* cut trailing comment or whitespace from input line */
1352 for (line_ptr = line; *line_ptr; line_ptr++)
1354 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1361 /* cut trailing whitespaces from input line */
1362 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1363 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1366 /* ignore empty lines */
1370 line_len = strlen(line);
1372 /* cut leading whitespaces from token */
1373 for (token = line; *token; token++)
1374 if (*token != ' ' && *token != '\t')
1377 /* find end of token */
1378 for (line_ptr = token; *line_ptr; line_ptr++)
1380 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1387 if (line_ptr < line + line_len)
1388 value = line_ptr + 1;
1392 /* cut leading whitespaces from value */
1393 for (; *value; value++)
1394 if (*value != ' ' && *value != '\t')
1397 if (*token && *value)
1400 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1402 insert_ptr = setListEntry((SetupFileList *)insert_ptr, token, value);
1410 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1411 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1415 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1416 SetupFileList *first_valid_list_entry = setup_file_list->next;
1418 /* free empty list header */
1419 setup_file_list->next = NULL;
1420 freeSetupFileList(setup_file_list);
1421 setup_file_data = first_valid_list_entry;
1423 if (first_valid_list_entry == NULL)
1424 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1427 return setup_file_data;
1430 SetupFileList *loadSetupFileList(char *filename)
1432 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1435 SetupFileHash *loadSetupFileHash(char *filename)
1437 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1440 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1443 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1446 Error(ERR_WARN, "configuration file has no file identifier");
1447 else if (!checkCookieString(value, identifier))
1448 Error(ERR_WARN, "configuration file has wrong file identifier");
1452 /* ========================================================================= */
1453 /* setup file stuff */
1454 /* ========================================================================= */
1456 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1457 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1458 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1460 /* level directory info */
1461 #define LEVELINFO_TOKEN_IDENTIFIER 0
1462 #define LEVELINFO_TOKEN_NAME 1
1463 #define LEVELINFO_TOKEN_NAME_SORTING 2
1464 #define LEVELINFO_TOKEN_AUTHOR 3
1465 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1466 #define LEVELINFO_TOKEN_LEVELS 5
1467 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1468 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1469 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1470 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1471 #define LEVELINFO_TOKEN_READONLY 10
1472 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1473 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1474 #define LEVELINFO_TOKEN_MUSIC_SET 13
1476 #define NUM_LEVELINFO_TOKENS 14
1478 static LevelDirTree ldi;
1480 static struct TokenInfo levelinfo_tokens[] =
1482 /* level directory info */
1483 { TYPE_STRING, &ldi.identifier, "identifier" },
1484 { TYPE_STRING, &ldi.name, "name" },
1485 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1486 { TYPE_STRING, &ldi.author, "author" },
1487 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1488 { TYPE_INTEGER, &ldi.levels, "levels" },
1489 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1490 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1491 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1492 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1493 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1494 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1495 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1496 { TYPE_STRING, &ldi.music_set, "music_set" }
1499 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1503 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1504 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1505 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1506 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1509 ldi->node_parent = NULL;
1510 ldi->node_group = NULL;
1514 ldi->cl_cursor = -1;
1516 ldi->filename = NULL;
1517 ldi->fullpath = NULL;
1518 ldi->basepath = NULL;
1519 ldi->identifier = NULL;
1520 ldi->name = getStringCopy(ANONYMOUS_NAME);
1521 ldi->name_sorting = NULL;
1522 ldi->author = getStringCopy(ANONYMOUS_NAME);
1524 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1525 ldi->latest_engine = FALSE; /* default: get from level */
1526 ldi->parent_link = FALSE;
1527 ldi->user_defined = FALSE;
1529 ldi->class_desc = NULL;
1531 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1533 ldi->imported_from = NULL;
1535 ldi->graphics_set = NULL;
1536 ldi->sounds_set = NULL;
1537 ldi->music_set = NULL;
1538 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1539 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1540 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1543 ldi->first_level = 0;
1544 ldi->last_level = 0;
1545 ldi->level_group = FALSE;
1546 ldi->handicap_level = 0;
1547 ldi->readonly = TRUE;
1551 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1555 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1557 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1563 /* copy all values from the parent structure */
1565 ldi->type = parent->type;
1567 ldi->node_top = parent->node_top;
1568 ldi->node_parent = parent;
1569 ldi->node_group = NULL;
1573 ldi->cl_cursor = -1;
1575 ldi->filename = NULL;
1576 ldi->fullpath = NULL;
1577 ldi->basepath = NULL;
1578 ldi->identifier = NULL;
1579 ldi->name = getStringCopy(ANONYMOUS_NAME);
1580 ldi->name_sorting = NULL;
1581 ldi->author = getStringCopy(parent->author);
1583 ldi->sort_priority = parent->sort_priority;
1584 ldi->latest_engine = parent->latest_engine;
1585 ldi->parent_link = FALSE;
1586 ldi->user_defined = parent->user_defined;
1587 ldi->color = parent->color;
1588 ldi->class_desc = getStringCopy(parent->class_desc);
1590 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1592 ldi->imported_from = getStringCopy(parent->imported_from);
1594 ldi->graphics_set = NULL;
1595 ldi->sounds_set = NULL;
1596 ldi->music_set = NULL;
1597 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1598 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1599 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1602 ldi->first_level = 0;
1603 ldi->last_level = 0;
1604 ldi->level_group = FALSE;
1605 ldi->handicap_level = 0;
1606 ldi->readonly = TRUE;
1612 /* first copy all values from the parent structure ... */
1615 /* ... then set all fields to default that cannot be inherited from parent.
1616 This is especially important for all those fields that can be set from
1617 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1618 calls 'free()' for all already set token values which requires that no
1619 other structure's pointer may point to them!
1622 ldi->filename = NULL;
1623 ldi->fullpath = NULL;
1624 ldi->basepath = NULL;
1625 ldi->identifier = NULL;
1626 ldi->name = getStringCopy(ANONYMOUS_NAME);
1627 ldi->name_sorting = NULL;
1628 ldi->author = getStringCopy(parent->author);
1630 ldi->imported_from = getStringCopy(parent->imported_from);
1631 ldi->class_desc = getStringCopy(parent->class_desc);
1633 ldi->graphics_set = NULL;
1634 ldi->sounds_set = NULL;
1635 ldi->music_set = NULL;
1636 ldi->graphics_path = NULL;
1637 ldi->sounds_path = NULL;
1638 ldi->music_path = NULL;
1640 ldi->level_group = FALSE;
1641 ldi->parent_link = FALSE;
1643 ldi->node_top = parent->node_top;
1644 ldi->node_parent = parent;
1645 ldi->node_group = NULL;
1651 static void freeTreeInfo(TreeInfo *ldi)
1654 free(ldi->filename);
1656 free(ldi->fullpath);
1658 free(ldi->basepath);
1659 if (ldi->identifier)
1660 free(ldi->identifier);
1664 if (ldi->name_sorting)
1665 free(ldi->name_sorting);
1669 if (ldi->class_desc)
1670 free(ldi->class_desc);
1672 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1674 if (ldi->graphics_set)
1675 free(ldi->graphics_set);
1676 if (ldi->sounds_set)
1677 free(ldi->sounds_set);
1679 free(ldi->music_set);
1681 if (ldi->graphics_path)
1682 free(ldi->graphics_path);
1683 if (ldi->sounds_path)
1684 free(ldi->sounds_path);
1685 if (ldi->music_path)
1686 free(ldi->music_path);
1690 void setSetupInfo(struct TokenInfo *token_info,
1691 int token_nr, char *token_value)
1693 int token_type = token_info[token_nr].type;
1694 void *setup_value = token_info[token_nr].value;
1696 if (token_value == NULL)
1699 /* set setup field to corresponding token value */
1704 *(boolean *)setup_value = get_boolean_from_string(token_value);
1708 *(Key *)setup_value = getKeyFromKeyName(token_value);
1712 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1716 *(int *)setup_value = get_integer_from_string(token_value);
1720 if (*(char **)setup_value != NULL)
1721 free(*(char **)setup_value);
1722 *(char **)setup_value = getStringCopy(token_value);
1730 static int compareTreeInfoEntries(const void *object1, const void *object2)
1732 const TreeInfo *entry1 = *((TreeInfo **)object1);
1733 const TreeInfo *entry2 = *((TreeInfo **)object2);
1734 int class_sorting1, class_sorting2;
1737 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1739 class_sorting1 = LEVELSORTING(entry1);
1740 class_sorting2 = LEVELSORTING(entry2);
1744 class_sorting1 = ARTWORKSORTING(entry1);
1745 class_sorting2 = ARTWORKSORTING(entry2);
1748 if (entry1->parent_link || entry2->parent_link)
1749 compare_result = (entry1->parent_link ? -1 : +1);
1750 else if (entry1->sort_priority == entry2->sort_priority)
1752 char *name1 = getStringToLower(entry1->name_sorting);
1753 char *name2 = getStringToLower(entry2->name_sorting);
1755 compare_result = strcmp(name1, name2);
1760 else if (class_sorting1 == class_sorting2)
1761 compare_result = entry1->sort_priority - entry2->sort_priority;
1763 compare_result = class_sorting1 - class_sorting2;
1765 return compare_result;
1768 static void createParentTreeInfoNode(TreeInfo *node_parent)
1772 if (node_parent == NULL)
1775 ti_new = newTreeInfo();
1776 setTreeInfoToDefaults(ti_new, node_parent->type);
1778 ti_new->node_parent = node_parent;
1779 ti_new->parent_link = TRUE;
1782 setString(&ti_new->identifier, node_parent->identifier);
1783 setString(&ti_new->name, ".. (parent directory)");
1784 setString(&ti_new->name_sorting, ti_new->name);
1786 setString(&ti_new->filename, "..");
1787 setString(&ti_new->fullpath, node_parent->fullpath);
1789 ti_new->sort_priority = node_parent->sort_priority;
1790 ti_new->latest_engine = node_parent->latest_engine;
1792 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1794 ti_new->identifier = getStringCopy(node_parent->identifier);
1795 ti_new->name = ".. (parent directory)";
1796 ti_new->name_sorting = getStringCopy(ti_new->name);
1798 ti_new->filename = "..";
1799 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1801 ti_new->sort_priority = node_parent->sort_priority;
1802 ti_new->latest_engine = node_parent->latest_engine;
1804 ti_new->class_desc = getLevelClassDescription(ti_new);
1807 pushTreeInfo(&node_parent->node_group, ti_new);
1810 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1811 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1813 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1814 TreeInfo *node_parent,
1815 char *level_directory,
1816 char *directory_name)
1818 char *directory_path = getPath2(level_directory, directory_name);
1819 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1820 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1821 LevelDirTree *leveldir_new = NULL;
1824 if (setup_file_hash == NULL)
1826 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1828 free(directory_path);
1834 leveldir_new = newTreeInfo();
1837 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1839 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1841 leveldir_new->filename = getStringCopy(directory_name);
1843 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1845 /* set all structure fields according to the token/value pairs */
1846 ldi = *leveldir_new;
1847 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1848 setSetupInfo(levelinfo_tokens, i,
1849 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1850 *leveldir_new = ldi;
1853 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1854 setString(&leveldir_new->name, leveldir_new->filename);
1856 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1858 free(leveldir_new->name);
1859 leveldir_new->name = getStringCopy(leveldir_new->filename);
1863 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1865 if (leveldir_new->identifier == NULL)
1866 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1868 if (leveldir_new->name_sorting == NULL)
1869 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1871 if (node_parent == NULL) /* top level group */
1873 leveldir_new->basepath = getStringCopy(level_directory);
1874 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1876 else /* sub level group */
1878 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1879 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1882 if (leveldir_new->levels < 1)
1883 leveldir_new->levels = 1;
1885 leveldir_new->last_level =
1886 leveldir_new->first_level + leveldir_new->levels - 1;
1889 leveldir_new->user_defined =
1890 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1892 leveldir_new->user_defined =
1893 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1896 leveldir_new->color = LEVELCOLOR(leveldir_new);
1898 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1900 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1903 leveldir_new->handicap_level = /* set handicap to default value */
1904 (leveldir_new->user_defined ?
1905 leveldir_new->last_level :
1906 leveldir_new->first_level);
1908 pushTreeInfo(node_first, leveldir_new);
1910 freeSetupFileHash(setup_file_hash);
1912 if (leveldir_new->level_group)
1914 /* create node to link back to current level directory */
1915 createParentTreeInfoNode(leveldir_new);
1917 /* step into sub-directory and look for more level series */
1918 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1919 leveldir_new, directory_path);
1922 free(directory_path);
1928 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1929 TreeInfo *node_parent,
1930 char *level_directory)
1933 struct dirent *dir_entry;
1934 boolean valid_entry_found = FALSE;
1936 if ((dir = opendir(level_directory)) == NULL)
1938 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1942 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1944 struct stat file_status;
1945 char *directory_name = dir_entry->d_name;
1946 char *directory_path = getPath2(level_directory, directory_name);
1948 /* skip entries for current and parent directory */
1949 if (strcmp(directory_name, ".") == 0 ||
1950 strcmp(directory_name, "..") == 0)
1952 free(directory_path);
1956 /* find out if directory entry is itself a directory */
1957 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1958 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1960 free(directory_path);
1964 free(directory_path);
1966 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1967 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1968 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1971 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1978 if (!valid_entry_found)
1980 /* check if this directory directly contains a file "levelinfo.conf" */
1981 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1982 level_directory, ".");
1985 if (!valid_entry_found)
1986 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1990 void LoadLevelInfo()
1992 InitUserLevelDirectory(getLoginName());
1994 DrawInitText("Loading level series:", 120, FC_GREEN);
1996 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1997 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1999 /* before sorting, the first entries will be from the user directory */
2000 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2002 if (leveldir_first == NULL)
2003 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2005 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2008 dumpTreeInfo(leveldir_first, 0);
2012 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2013 TreeInfo *node_parent,
2014 char *base_directory,
2015 char *directory_name, int type)
2017 char *directory_path = getPath2(base_directory, directory_name);
2018 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2019 SetupFileHash *setup_file_hash = NULL;
2020 TreeInfo *artwork_new = NULL;
2023 if (access(filename, F_OK) == 0) /* file exists */
2024 setup_file_hash = loadSetupFileHash(filename);
2026 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2029 struct dirent *dir_entry;
2030 boolean valid_file_found = FALSE;
2032 if ((dir = opendir(directory_path)) != NULL)
2034 while ((dir_entry = readdir(dir)) != NULL)
2036 char *entry_name = dir_entry->d_name;
2038 if (FileIsArtworkType(entry_name, type))
2040 valid_file_found = TRUE;
2048 if (!valid_file_found)
2050 if (strcmp(directory_name, ".") != 0)
2051 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2053 free(directory_path);
2060 artwork_new = newTreeInfo();
2063 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2065 setTreeInfoToDefaults(artwork_new, type);
2067 artwork_new->filename = getStringCopy(directory_name);
2069 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2072 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2075 /* set all structure fields according to the token/value pairs */
2077 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2078 setSetupInfo(levelinfo_tokens, i,
2079 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2083 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2084 setString(&artwork_new->name, artwork_new->filename);
2086 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2088 free(artwork_new->name);
2089 artwork_new->name = getStringCopy(artwork_new->filename);
2094 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2097 if (artwork_new->identifier == NULL)
2098 artwork_new->identifier = getStringCopy(artwork_new->filename);
2100 if (artwork_new->name_sorting == NULL)
2101 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2104 if (node_parent == NULL) /* top level group */
2106 artwork_new->basepath = getStringCopy(base_directory);
2107 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2109 else /* sub level group */
2111 artwork_new->basepath = getStringCopy(node_parent->basepath);
2112 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2116 artwork_new->user_defined =
2117 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2119 artwork_new->user_defined =
2120 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2123 /* (may use ".sort_priority" from "setup_file_hash" above) */
2124 artwork_new->color = ARTWORKCOLOR(artwork_new);
2126 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2128 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2131 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2134 if (artwork_new->name != NULL)
2136 free(artwork_new->name);
2137 artwork_new->name = NULL;
2142 if (artwork_new->identifier != NULL)
2144 free(artwork_new->identifier);
2145 artwork_new->identifier = NULL;
2149 if (strcmp(artwork_new->filename, ".") == 0)
2151 if (artwork_new->user_defined)
2154 setString(&artwork_new->identifier, "private");
2156 artwork_new->identifier = getStringCopy("private");
2158 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2163 setString(&artwork_new->identifier, "classic");
2165 artwork_new->identifier = getStringCopy("classic");
2167 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2170 /* set to new values after changing ".sort_priority" */
2171 artwork_new->color = ARTWORKCOLOR(artwork_new);
2173 setString(&artwork_new->class_desc,
2174 getLevelClassDescription(artwork_new));
2176 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2182 setString(&artwork_new->identifier, artwork_new->filename);
2184 artwork_new->identifier = getStringCopy(artwork_new->filename);
2189 setString(&artwork_new->name, artwork_new->identifier);
2190 setString(&artwork_new->name_sorting, artwork_new->name);
2192 artwork_new->name = getStringCopy(artwork_new->identifier);
2193 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2197 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2199 pushTreeInfo(node_first, artwork_new);
2201 freeSetupFileHash(setup_file_hash);
2203 free(directory_path);
2209 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2210 TreeInfo *node_parent,
2211 char *base_directory, int type)
2214 struct dirent *dir_entry;
2215 boolean valid_entry_found = FALSE;
2217 if ((dir = opendir(base_directory)) == NULL)
2219 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2220 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2224 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2226 struct stat file_status;
2227 char *directory_name = dir_entry->d_name;
2228 char *directory_path = getPath2(base_directory, directory_name);
2230 /* skip entries for current and parent directory */
2231 if (strcmp(directory_name, ".") == 0 ||
2232 strcmp(directory_name, "..") == 0)
2234 free(directory_path);
2238 /* find out if directory entry is itself a directory */
2239 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2240 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2242 free(directory_path);
2246 free(directory_path);
2248 /* check if this directory contains artwork with or without config file */
2249 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2251 directory_name, type);
2256 /* check if this directory directly contains artwork itself */
2257 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2258 base_directory, ".",
2260 if (!valid_entry_found)
2261 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2265 static TreeInfo *getDummyArtworkInfo(int type)
2267 /* this is only needed when there is completely no artwork available */
2268 TreeInfo *artwork_new = newTreeInfo();
2270 setTreeInfoToDefaults(artwork_new, type);
2273 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2274 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2275 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2277 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2278 setString(&artwork_new->name, UNDEFINED_FILENAME);
2279 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2281 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2282 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2283 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2285 if (artwork_new->name != NULL)
2286 free(artwork_new->name);
2288 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2289 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2290 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2296 void LoadArtworkInfo()
2298 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2300 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2301 options.graphics_directory,
2302 TREE_TYPE_GRAPHICS_DIR);
2303 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2304 getUserGraphicsDir(),
2305 TREE_TYPE_GRAPHICS_DIR);
2307 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2308 options.sounds_directory,
2309 TREE_TYPE_SOUNDS_DIR);
2310 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2312 TREE_TYPE_SOUNDS_DIR);
2314 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2315 options.music_directory,
2316 TREE_TYPE_MUSIC_DIR);
2317 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2319 TREE_TYPE_MUSIC_DIR);
2321 if (artwork.gfx_first == NULL)
2322 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2323 if (artwork.snd_first == NULL)
2324 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2325 if (artwork.mus_first == NULL)
2326 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2328 /* before sorting, the first entries will be from the user directory */
2329 artwork.gfx_current =
2330 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2331 if (artwork.gfx_current == NULL)
2332 artwork.gfx_current =
2333 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2334 if (artwork.gfx_current == NULL)
2335 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2337 artwork.snd_current =
2338 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2339 if (artwork.snd_current == NULL)
2340 artwork.snd_current =
2341 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2342 if (artwork.snd_current == NULL)
2343 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2345 artwork.mus_current =
2346 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2347 if (artwork.mus_current == NULL)
2348 artwork.mus_current =
2349 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2350 if (artwork.mus_current == NULL)
2351 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2353 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2354 artwork.snd_current_identifier = artwork.snd_current->identifier;
2355 artwork.mus_current_identifier = artwork.mus_current->identifier;
2358 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2359 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2360 printf("music set == %s\n\n", artwork.mus_current_identifier);
2363 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2364 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2365 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2368 dumpTreeInfo(artwork.gfx_first, 0);
2369 dumpTreeInfo(artwork.snd_first, 0);
2370 dumpTreeInfo(artwork.mus_first, 0);
2374 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2375 LevelDirTree *level_node)
2377 /* recursively check all level directories for artwork sub-directories */
2381 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2382 ARTWORK_DIRECTORY((*artwork_node)->type));
2385 if (!level_node->parent_link)
2386 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2387 level_node->filename, level_node->name);
2390 if (!level_node->parent_link)
2392 TreeInfo *topnode_last = *artwork_node;
2394 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2395 (*artwork_node)->type);
2397 if (topnode_last != *artwork_node)
2399 free((*artwork_node)->identifier);
2400 free((*artwork_node)->name);
2401 free((*artwork_node)->name_sorting);
2403 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2404 (*artwork_node)->name = getStringCopy(level_node->name);
2405 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2407 (*artwork_node)->sort_priority = level_node->sort_priority;
2408 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2414 if (level_node->node_group != NULL)
2415 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2417 level_node = level_node->next;
2421 void LoadLevelArtworkInfo()
2423 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2425 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2426 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2427 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2429 /* needed for reloading level artwork not known at ealier stage */
2431 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2433 artwork.gfx_current =
2434 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2435 if (artwork.gfx_current == NULL)
2436 artwork.gfx_current =
2437 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2438 if (artwork.gfx_current == NULL)
2439 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2442 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2444 artwork.snd_current =
2445 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2446 if (artwork.snd_current == NULL)
2447 artwork.snd_current =
2448 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2449 if (artwork.snd_current == NULL)
2450 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2453 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2455 artwork.mus_current =
2456 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2457 if (artwork.mus_current == NULL)
2458 artwork.mus_current =
2459 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2460 if (artwork.mus_current == NULL)
2461 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2464 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2465 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2466 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2469 dumpTreeInfo(artwork.gfx_first, 0);
2470 dumpTreeInfo(artwork.snd_first, 0);
2471 dumpTreeInfo(artwork.mus_first, 0);
2475 static void SaveUserLevelInfo()
2477 LevelDirTree *level_info;
2482 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2484 if (!(file = fopen(filename, MODE_WRITE)))
2486 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2491 level_info = newTreeInfo();
2493 /* always start with reliable default values */
2494 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2497 setString(&level_info->name, getLoginName());
2498 setString(&level_info->author, getRealName());
2499 level_info->levels = 100;
2500 level_info->first_level = 1;
2501 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2502 level_info->readonly = FALSE;
2503 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2504 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2505 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2507 ldi.name = getStringCopy(getLoginName());
2508 ldi.author = getStringCopy(getRealName());
2510 ldi.first_level = 1;
2511 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2512 ldi.readonly = FALSE;
2513 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2514 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2515 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2518 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2519 getCookie("LEVELINFO")));
2522 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2523 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2524 i != LEVELINFO_TOKEN_NAME_SORTING &&
2525 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2526 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2530 SetFilePermissions(filename, PERMS_PRIVATE);
2532 freeTreeInfo(level_info);
2536 char *getSetupValue(int type, void *value)
2538 static char value_string[MAX_LINE_LEN];
2546 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2550 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2554 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2558 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2562 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2566 sprintf(value_string, "%d", *(int *)value);
2570 strcpy(value_string, *(char **)value);
2574 value_string[0] = '\0';
2578 return value_string;
2581 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2585 static char token_string[MAX_LINE_LEN];
2586 int token_type = token_info[token_nr].type;
2587 void *setup_value = token_info[token_nr].value;
2588 char *token_text = token_info[token_nr].text;
2589 char *value_string = getSetupValue(token_type, setup_value);
2591 /* build complete token string */
2592 sprintf(token_string, "%s%s", prefix, token_text);
2594 /* build setup entry line */
2595 line = getFormattedSetupEntry(token_string, value_string);
2597 if (token_type == TYPE_KEY_X11)
2599 Key key = *(Key *)setup_value;
2600 char *keyname = getKeyNameFromKey(key);
2602 /* add comment, if useful */
2603 if (strcmp(keyname, "(undefined)") != 0 &&
2604 strcmp(keyname, "(unknown)") != 0)
2606 /* add at least one whitespace */
2608 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2612 strcat(line, keyname);
2619 void LoadLevelSetup_LastSeries()
2622 SetupFileHash *level_setup_hash = NULL;
2624 /* always start with reliable default values */
2625 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2627 /* ----------------------------------------------------------------------- */
2628 /* ~/.<program>/levelsetup.conf */
2629 /* ----------------------------------------------------------------------- */
2631 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2633 if ((level_setup_hash = loadSetupFileHash(filename)))
2635 char *last_level_series =
2636 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2638 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2640 if (leveldir_current == NULL)
2641 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2643 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2645 freeSetupFileHash(level_setup_hash);
2648 Error(ERR_WARN, "using default setup values");
2653 void SaveLevelSetup_LastSeries()
2656 char *level_subdir = leveldir_current->filename;
2659 /* ----------------------------------------------------------------------- */
2660 /* ~/.<program>/levelsetup.conf */
2661 /* ----------------------------------------------------------------------- */
2663 InitUserDataDirectory();
2665 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2667 if (!(file = fopen(filename, MODE_WRITE)))
2669 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2674 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2675 getCookie("LEVELSETUP")));
2676 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2681 SetFilePermissions(filename, PERMS_PRIVATE);
2686 static void checkSeriesInfo()
2688 static char *level_directory = NULL;
2690 struct dirent *dir_entry;
2692 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2694 level_directory = getPath2((leveldir_current->user_defined ?
2695 getUserLevelDir(NULL) :
2696 options.level_directory),
2697 leveldir_current->fullpath);
2699 if ((dir = opendir(level_directory)) == NULL)
2701 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2705 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2707 if (strlen(dir_entry->d_name) > 4 &&
2708 dir_entry->d_name[3] == '.' &&
2709 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2711 char levelnum_str[4];
2714 strncpy(levelnum_str, dir_entry->d_name, 3);
2715 levelnum_str[3] = '\0';
2717 levelnum_value = atoi(levelnum_str);
2720 if (levelnum_value < leveldir_current->first_level)
2722 Error(ERR_WARN, "additional level %d found", levelnum_value);
2723 leveldir_current->first_level = levelnum_value;
2725 else if (levelnum_value > leveldir_current->last_level)
2727 Error(ERR_WARN, "additional level %d found", levelnum_value);
2728 leveldir_current->last_level = levelnum_value;
2737 void LoadLevelSetup_SeriesInfo()
2740 SetupFileHash *level_setup_hash = NULL;
2741 char *level_subdir = leveldir_current->filename;
2743 /* always start with reliable default values */
2744 level_nr = leveldir_current->first_level;
2746 checkSeriesInfo(leveldir_current);
2748 /* ----------------------------------------------------------------------- */
2749 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2750 /* ----------------------------------------------------------------------- */
2752 level_subdir = leveldir_current->filename;
2754 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2756 if ((level_setup_hash = loadSetupFileHash(filename)))
2760 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2764 level_nr = atoi(token_value);
2766 if (level_nr < leveldir_current->first_level)
2767 level_nr = leveldir_current->first_level;
2768 if (level_nr > leveldir_current->last_level)
2769 level_nr = leveldir_current->last_level;
2772 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2776 int level_nr = atoi(token_value);
2778 if (level_nr < leveldir_current->first_level)
2779 level_nr = leveldir_current->first_level;
2780 if (level_nr > leveldir_current->last_level + 1)
2781 level_nr = leveldir_current->last_level;
2783 if (leveldir_current->user_defined)
2784 level_nr = leveldir_current->last_level;
2786 leveldir_current->handicap_level = level_nr;
2789 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2791 freeSetupFileHash(level_setup_hash);
2794 Error(ERR_WARN, "using default setup values");
2799 void SaveLevelSetup_SeriesInfo()
2802 char *level_subdir = leveldir_current->filename;
2803 char *level_nr_str = int2str(level_nr, 0);
2804 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2807 /* ----------------------------------------------------------------------- */
2808 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2809 /* ----------------------------------------------------------------------- */
2811 InitLevelSetupDirectory(level_subdir);
2813 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2815 if (!(file = fopen(filename, MODE_WRITE)))
2817 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2822 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2823 getCookie("LEVELSETUP")));
2824 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2826 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2827 handicap_level_str));
2831 SetFilePermissions(filename, PERMS_PRIVATE);