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 char *getEditorSetupFilename()
410 static char *filename = NULL;
412 if (filename != NULL)
415 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
420 static char *getCorrectedArtworkBasename(char *basename)
422 char *basename_corrected = basename;
424 #if defined(PLATFORM_MSDOS)
425 if (program.filename_prefix != NULL)
427 int prefix_len = strlen(program.filename_prefix);
429 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
430 basename_corrected = &basename[prefix_len];
432 /* if corrected filename is still longer than standard MS-DOS filename
433 size (8 characters + 1 dot + 3 characters file extension), shorten
434 filename by writing file extension after 8th basename character */
435 if (strlen(basename_corrected) > 8 + 1 + 3)
437 static char *msdos_filename = NULL;
439 if (msdos_filename != NULL)
440 free(msdos_filename);
442 msdos_filename = getStringCopy(basename_corrected);
443 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
445 basename_corrected = msdos_filename;
450 return basename_corrected;
453 char *getCustomImageFilename(char *basename)
455 static char *filename = NULL;
456 boolean skip_setup_artwork = FALSE;
458 if (filename != NULL)
461 basename = getCorrectedArtworkBasename(basename);
463 if (!setup.override_level_graphics)
465 /* 1st try: look for special artwork in current level series directory */
466 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
467 if (fileExists(filename))
472 /* check if there is special artwork configured in level series config */
473 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
475 /* 2nd try: look for special artwork configured in level series config */
476 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
477 if (fileExists(filename))
482 /* take missing artwork configured in level set config from default */
483 skip_setup_artwork = TRUE;
487 if (!skip_setup_artwork)
489 /* 3rd try: look for special artwork in configured artwork directory */
490 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
491 if (fileExists(filename))
497 /* 4th try: look for default artwork in new default artwork directory */
498 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
499 if (fileExists(filename))
504 /* 5th try: look for default artwork in old default artwork directory */
505 filename = getPath2(options.graphics_directory, basename);
506 if (fileExists(filename))
509 return NULL; /* cannot find specified artwork file anywhere */
512 char *getCustomSoundFilename(char *basename)
514 static char *filename = NULL;
515 boolean skip_setup_artwork = FALSE;
517 if (filename != NULL)
520 basename = getCorrectedArtworkBasename(basename);
522 if (!setup.override_level_sounds)
524 /* 1st try: look for special artwork in current level series directory */
525 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
526 if (fileExists(filename))
531 /* check if there is special artwork configured in level series config */
532 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
534 /* 2nd try: look for special artwork configured in level series config */
535 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
536 if (fileExists(filename))
541 /* take missing artwork configured in level set config from default */
542 skip_setup_artwork = TRUE;
546 if (!skip_setup_artwork)
548 /* 3rd try: look for special artwork in configured artwork directory */
549 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
550 if (fileExists(filename))
556 /* 4th try: look for default artwork in new default artwork directory */
557 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
558 if (fileExists(filename))
563 /* 5th try: look for default artwork in old default artwork directory */
564 filename = getPath2(options.sounds_directory, basename);
565 if (fileExists(filename))
568 return NULL; /* cannot find specified artwork file anywhere */
571 char *getCustomMusicFilename(char *basename)
573 static char *filename = NULL;
574 boolean skip_setup_artwork = FALSE;
576 if (filename != NULL)
579 basename = getCorrectedArtworkBasename(basename);
581 if (!setup.override_level_music)
583 /* 1st try: look for special artwork in current level series directory */
584 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
585 if (fileExists(filename))
590 /* check if there is special artwork configured in level series config */
591 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
593 /* 2nd try: look for special artwork configured in level series config */
594 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
595 if (fileExists(filename))
600 /* take missing artwork configured in level set config from default */
601 skip_setup_artwork = TRUE;
605 if (!skip_setup_artwork)
607 /* 3rd try: look for special artwork in configured artwork directory */
608 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
609 if (fileExists(filename))
615 /* 4th try: look for default artwork in new default artwork directory */
616 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
617 if (fileExists(filename))
622 /* 5th try: look for default artwork in old default artwork directory */
623 filename = getPath2(options.music_directory, basename);
624 if (fileExists(filename))
627 return NULL; /* cannot find specified artwork file anywhere */
630 char *getCustomArtworkFilename(char *basename, int type)
632 if (type == ARTWORK_TYPE_GRAPHICS)
633 return getCustomImageFilename(basename);
634 else if (type == ARTWORK_TYPE_SOUNDS)
635 return getCustomSoundFilename(basename);
636 else if (type == ARTWORK_TYPE_MUSIC)
637 return getCustomMusicFilename(basename);
639 return UNDEFINED_FILENAME;
642 char *getCustomArtworkConfigFilename(int type)
644 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
647 char *getCustomArtworkLevelConfigFilename(int type)
649 static char *filename = NULL;
651 if (filename != NULL)
654 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
659 char *getCustomMusicDirectory(void)
661 static char *directory = NULL;
662 boolean skip_setup_artwork = FALSE;
664 if (directory != NULL)
667 if (!setup.override_level_music)
669 /* 1st try: look for special artwork in current level series directory */
670 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
671 if (fileExists(directory))
676 /* check if there is special artwork configured in level series config */
677 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
679 /* 2nd try: look for special artwork configured in level series config */
680 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
681 if (fileExists(directory))
686 /* take missing artwork configured in level set config from default */
687 skip_setup_artwork = TRUE;
691 if (!skip_setup_artwork)
693 /* 3rd try: look for special artwork in configured artwork directory */
694 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
695 if (fileExists(directory))
701 /* 4th try: look for default artwork in new default artwork directory */
702 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
703 if (fileExists(directory))
708 /* 5th try: look for default artwork in old default artwork directory */
709 directory = getStringCopy(options.music_directory);
710 if (fileExists(directory))
713 return NULL; /* cannot find specified artwork file anywhere */
716 void InitTapeDirectory(char *level_subdir)
718 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
719 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
720 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
723 void InitScoreDirectory(char *level_subdir)
725 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
726 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
727 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
730 static void SaveUserLevelInfo();
732 void InitUserLevelDirectory(char *level_subdir)
734 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
736 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
737 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
738 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
744 void InitLevelSetupDirectory(char *level_subdir)
746 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
747 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
748 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
752 /* ------------------------------------------------------------------------- */
753 /* some functions to handle lists of level directories */
754 /* ------------------------------------------------------------------------- */
756 TreeInfo *newTreeInfo()
758 return checked_calloc(sizeof(TreeInfo));
761 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
763 node_new->next = *node_first;
764 *node_first = node_new;
767 int numTreeInfo(TreeInfo *node)
780 boolean validLevelSeries(TreeInfo *node)
782 return (node != NULL && !node->node_group && !node->parent_link);
785 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
790 if (node->node_group) /* enter level group (step down into tree) */
791 return getFirstValidTreeInfoEntry(node->node_group);
792 else if (node->parent_link) /* skip start entry of level group */
794 if (node->next) /* get first real level series entry */
795 return getFirstValidTreeInfoEntry(node->next);
796 else /* leave empty level group and go on */
797 return getFirstValidTreeInfoEntry(node->node_parent->next);
799 else /* this seems to be a regular level series */
803 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
808 if (node->node_parent == NULL) /* top level group */
809 return *node->node_top;
810 else /* sub level group */
811 return node->node_parent->node_group;
814 int numTreeInfoInGroup(TreeInfo *node)
816 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
819 int posTreeInfo(TreeInfo *node)
821 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
826 if (node_cmp == node)
830 node_cmp = node_cmp->next;
836 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
838 TreeInfo *node_default = node;
853 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
855 if (identifier == NULL)
860 if (node->node_group)
862 TreeInfo *node_group;
864 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
869 else if (!node->parent_link)
871 if (strcmp(identifier, node->identifier) == 0)
881 void dumpTreeInfo(TreeInfo *node, int depth)
885 printf("Dumping TreeInfo:\n");
889 for (i=0; i<(depth + 1) * 3; i++)
893 printf("filename == '%s' ['%s', '%s'] [%d])\n",
894 node->filename, node->fullpath, node->basepath, node->user_defined);
896 printf("filename == '%s' (%s) [%s] (%d)\n",
897 node->filename, node->name, node->identifier, node->sort_priority);
900 if (node->node_group != NULL)
901 dumpTreeInfo(node->node_group, depth + 1);
907 void sortTreeInfo(TreeInfo **node_first,
908 int (*compare_function)(const void *, const void *))
910 int num_nodes = numTreeInfo(*node_first);
911 TreeInfo **sort_array;
912 TreeInfo *node = *node_first;
918 /* allocate array for sorting structure pointers */
919 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
921 /* writing structure pointers to sorting array */
922 while (i < num_nodes && node) /* double boundary check... */
924 sort_array[i] = node;
930 /* sorting the structure pointers in the sorting array */
931 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
934 /* update the linkage of list elements with the sorted node array */
935 for (i=0; i<num_nodes - 1; i++)
936 sort_array[i]->next = sort_array[i + 1];
937 sort_array[num_nodes - 1]->next = NULL;
939 /* update the linkage of the main list anchor pointer */
940 *node_first = sort_array[0];
944 /* now recursively sort the level group structures */
948 if (node->node_group != NULL)
949 sortTreeInfo(&node->node_group, compare_function);
956 /* ========================================================================= */
957 /* some stuff from "files.c" */
958 /* ========================================================================= */
960 #if defined(PLATFORM_WIN32)
962 #define S_IRGRP S_IRUSR
965 #define S_IROTH S_IRUSR
968 #define S_IWGRP S_IWUSR
971 #define S_IWOTH S_IWUSR
974 #define S_IXGRP S_IXUSR
977 #define S_IXOTH S_IXUSR
980 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
985 #endif /* PLATFORM_WIN32 */
987 /* file permissions for newly written files */
988 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
989 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
990 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
992 #define MODE_W_PRIVATE (S_IWUSR)
993 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
994 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
996 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
997 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
999 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1000 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1002 char *getUserDataDir(void)
1004 static char *userdata_dir = NULL;
1006 if (userdata_dir == NULL)
1007 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1009 return userdata_dir;
1012 char *getCommonDataDir(void)
1014 static char *common_data_dir = NULL;
1016 #if defined(PLATFORM_WIN32)
1017 if (common_data_dir == NULL)
1019 char *dir = checked_malloc(MAX_PATH + 1);
1021 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1022 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1023 common_data_dir = getPath2(dir, program.userdata_directory);
1025 common_data_dir = options.rw_base_directory;
1028 if (common_data_dir == NULL)
1029 common_data_dir = options.rw_base_directory;
1032 return common_data_dir;
1037 return getUserDataDir();
1040 static mode_t posix_umask(mode_t mask)
1042 #if defined(PLATFORM_UNIX)
1049 static int posix_mkdir(const char *pathname, mode_t mode)
1051 #if defined(PLATFORM_WIN32)
1052 return mkdir(pathname);
1054 return mkdir(pathname, mode);
1058 void createDirectory(char *dir, char *text, int permission_class)
1060 /* leave "other" permissions in umask untouched, but ensure group parts
1061 of USERDATA_DIR_MODE are not masked */
1062 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1063 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1064 mode_t normal_umask = posix_umask(0);
1065 mode_t group_umask = ~(dir_mode & S_IRWXG);
1066 posix_umask(normal_umask & group_umask);
1068 if (access(dir, F_OK) != 0)
1069 if (posix_mkdir(dir, dir_mode) != 0)
1070 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1072 posix_umask(normal_umask); /* reset normal umask */
1075 void InitUserDataDirectory()
1077 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1080 void SetFilePermissions(char *filename, int permission_class)
1082 chmod(filename, (permission_class == PERMS_PRIVATE ?
1083 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1086 char *getCookie(char *file_type)
1088 static char cookie[MAX_COOKIE_LEN + 1];
1090 if (strlen(program.cookie_prefix) + 1 +
1091 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1092 return "[COOKIE ERROR]"; /* should never happen */
1094 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1095 program.cookie_prefix, file_type,
1096 program.version_major, program.version_minor);
1101 int getFileVersionFromCookieString(const char *cookie)
1103 const char *ptr_cookie1, *ptr_cookie2;
1104 const char *pattern1 = "_FILE_VERSION_";
1105 const char *pattern2 = "?.?";
1106 const int len_cookie = strlen(cookie);
1107 const int len_pattern1 = strlen(pattern1);
1108 const int len_pattern2 = strlen(pattern2);
1109 const int len_pattern = len_pattern1 + len_pattern2;
1110 int version_major, version_minor;
1112 if (len_cookie <= len_pattern)
1115 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1116 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1118 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1121 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1122 ptr_cookie2[1] != '.' ||
1123 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1126 version_major = ptr_cookie2[0] - '0';
1127 version_minor = ptr_cookie2[2] - '0';
1129 return VERSION_IDENT(version_major, version_minor, 0, 0);
1132 boolean checkCookieString(const char *cookie, const char *template)
1134 const char *pattern = "_FILE_VERSION_?.?";
1135 const int len_cookie = strlen(cookie);
1136 const int len_template = strlen(template);
1137 const int len_pattern = strlen(pattern);
1139 if (len_cookie != len_template)
1142 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1148 /* ------------------------------------------------------------------------- */
1149 /* setup file list and hash handling functions */
1150 /* ------------------------------------------------------------------------- */
1152 char *getFormattedSetupEntry(char *token, char *value)
1155 static char entry[MAX_LINE_LEN];
1157 /* start with the token and some spaces to format output line */
1158 sprintf(entry, "%s:", token);
1159 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1162 /* continue with the token's value */
1163 strcat(entry, value);
1168 SetupFileList *newSetupFileList(char *token, char *value)
1170 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1172 new->token = getStringCopy(token);
1173 new->value = getStringCopy(value);
1180 void freeSetupFileList(SetupFileList *list)
1190 freeSetupFileList(list->next);
1194 char *getListEntry(SetupFileList *list, char *token)
1199 if (strcmp(list->token, token) == 0)
1202 return getListEntry(list->next, token);
1205 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1210 if (strcmp(list->token, token) == 0)
1215 list->value = getStringCopy(value);
1219 else if (list->next == NULL)
1220 return (list->next = newSetupFileList(token, value));
1222 return setListEntry(list->next, token, value);
1226 static void printSetupFileList(SetupFileList *list)
1231 printf("token: '%s'\n", list->token);
1232 printf("value: '%s'\n", list->value);
1234 printSetupFileList(list->next);
1239 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1240 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1241 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1242 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1244 #define insert_hash_entry hashtable_insert
1245 #define search_hash_entry hashtable_search
1246 #define change_hash_entry hashtable_change
1247 #define remove_hash_entry hashtable_remove
1250 static unsigned int get_hash_from_key(void *key)
1255 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1256 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1257 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1258 it works better than many other constants, prime or not) has never been
1259 adequately explained.
1261 If you just want to have a good hash function, and cannot wait, djb2
1262 is one of the best string hash functions i know. It has excellent
1263 distribution and speed on many different sets of keys and table sizes.
1264 You are not likely to do better with one of the "well known" functions
1265 such as PJW, K&R, etc.
1267 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1270 char *str = (char *)key;
1271 unsigned int hash = 5381;
1274 while ((c = *str++))
1275 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1280 static int keys_are_equal(void *key1, void *key2)
1282 return (strcmp((char *)key1, (char *)key2) == 0);
1285 SetupFileHash *newSetupFileHash()
1287 SetupFileHash *new_hash =
1288 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1290 if (new_hash == NULL)
1291 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1296 void freeSetupFileHash(SetupFileHash *hash)
1301 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1304 char *getHashEntry(SetupFileHash *hash, char *token)
1309 return search_hash_entry(hash, token);
1312 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1319 value_copy = getStringCopy(value);
1321 /* change value; if it does not exist, insert it as new */
1322 if (!change_hash_entry(hash, token, value_copy))
1323 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1324 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1329 static void printSetupFileHash(SetupFileHash *hash)
1331 BEGIN_HASH_ITERATION(hash, itr)
1333 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1334 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1336 END_HASH_ITERATION(hash, itr)
1341 static void *loadSetupFileData(char *filename, boolean use_hash)
1344 char line[MAX_LINE_LEN];
1345 char *token, *value, *line_ptr;
1346 void *setup_file_data, *insert_ptr = NULL;
1350 setup_file_data = newSetupFileHash();
1352 insert_ptr = setup_file_data = newSetupFileList("", "");
1354 if (!(file = fopen(filename, MODE_READ)))
1356 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1362 /* read next line of input file */
1363 if (!fgets(line, MAX_LINE_LEN, file))
1366 /* cut trailing comment or whitespace from input line */
1367 for (line_ptr = line; *line_ptr; line_ptr++)
1369 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1376 /* cut trailing whitespaces from input line */
1377 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1378 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1381 /* ignore empty lines */
1385 line_len = strlen(line);
1387 /* cut leading whitespaces from token */
1388 for (token = line; *token; token++)
1389 if (*token != ' ' && *token != '\t')
1392 /* find end of token */
1393 for (line_ptr = token; *line_ptr; line_ptr++)
1395 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1402 if (line_ptr < line + line_len)
1403 value = line_ptr + 1;
1406 value = "true"; /* treat tokens without value as "true" */
1411 /* cut leading whitespaces from value */
1412 for (; *value; value++)
1413 if (*value != ' ' && *value != '\t')
1416 if (*token && *value)
1419 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1421 insert_ptr = setListEntry((SetupFileList *)insert_ptr, token, value);
1429 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1430 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1434 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1435 SetupFileList *first_valid_list_entry = setup_file_list->next;
1437 /* free empty list header */
1438 setup_file_list->next = NULL;
1439 freeSetupFileList(setup_file_list);
1440 setup_file_data = first_valid_list_entry;
1442 if (first_valid_list_entry == NULL)
1443 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1446 return setup_file_data;
1449 SetupFileList *loadSetupFileList(char *filename)
1451 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1454 SetupFileHash *loadSetupFileHash(char *filename)
1456 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1459 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1462 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1465 Error(ERR_WARN, "configuration file has no file identifier");
1466 else if (!checkCookieString(value, identifier))
1467 Error(ERR_WARN, "configuration file has wrong file identifier");
1471 /* ========================================================================= */
1472 /* setup file stuff */
1473 /* ========================================================================= */
1475 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1476 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1477 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1479 /* level directory info */
1480 #define LEVELINFO_TOKEN_IDENTIFIER 0
1481 #define LEVELINFO_TOKEN_NAME 1
1482 #define LEVELINFO_TOKEN_NAME_SORTING 2
1483 #define LEVELINFO_TOKEN_AUTHOR 3
1484 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1485 #define LEVELINFO_TOKEN_LEVELS 5
1486 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1487 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1488 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1489 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1490 #define LEVELINFO_TOKEN_READONLY 10
1491 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1492 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1493 #define LEVELINFO_TOKEN_MUSIC_SET 13
1495 #define NUM_LEVELINFO_TOKENS 14
1497 static LevelDirTree ldi;
1499 static struct TokenInfo levelinfo_tokens[] =
1501 /* level directory info */
1502 { TYPE_STRING, &ldi.identifier, "identifier" },
1503 { TYPE_STRING, &ldi.name, "name" },
1504 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1505 { TYPE_STRING, &ldi.author, "author" },
1506 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1507 { TYPE_INTEGER, &ldi.levels, "levels" },
1508 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1509 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1510 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1511 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1512 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1513 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1514 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1515 { TYPE_STRING, &ldi.music_set, "music_set" }
1518 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1522 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1523 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1524 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1525 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1528 ldi->node_parent = NULL;
1529 ldi->node_group = NULL;
1533 ldi->cl_cursor = -1;
1535 ldi->filename = NULL;
1536 ldi->fullpath = NULL;
1537 ldi->basepath = NULL;
1538 ldi->identifier = NULL;
1539 ldi->name = getStringCopy(ANONYMOUS_NAME);
1540 ldi->name_sorting = NULL;
1541 ldi->author = getStringCopy(ANONYMOUS_NAME);
1543 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1544 ldi->latest_engine = FALSE; /* default: get from level */
1545 ldi->parent_link = FALSE;
1546 ldi->user_defined = FALSE;
1548 ldi->class_desc = NULL;
1550 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1552 ldi->imported_from = NULL;
1554 ldi->graphics_set = NULL;
1555 ldi->sounds_set = NULL;
1556 ldi->music_set = NULL;
1557 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1558 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1559 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1562 ldi->first_level = 0;
1563 ldi->last_level = 0;
1564 ldi->level_group = FALSE;
1565 ldi->handicap_level = 0;
1566 ldi->readonly = TRUE;
1570 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1574 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1576 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1582 /* copy all values from the parent structure */
1584 ldi->type = parent->type;
1586 ldi->node_top = parent->node_top;
1587 ldi->node_parent = parent;
1588 ldi->node_group = NULL;
1592 ldi->cl_cursor = -1;
1594 ldi->filename = NULL;
1595 ldi->fullpath = NULL;
1596 ldi->basepath = NULL;
1597 ldi->identifier = NULL;
1598 ldi->name = getStringCopy(ANONYMOUS_NAME);
1599 ldi->name_sorting = NULL;
1600 ldi->author = getStringCopy(parent->author);
1602 ldi->sort_priority = parent->sort_priority;
1603 ldi->latest_engine = parent->latest_engine;
1604 ldi->parent_link = FALSE;
1605 ldi->user_defined = parent->user_defined;
1606 ldi->color = parent->color;
1607 ldi->class_desc = getStringCopy(parent->class_desc);
1609 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1611 ldi->imported_from = getStringCopy(parent->imported_from);
1613 ldi->graphics_set = NULL;
1614 ldi->sounds_set = NULL;
1615 ldi->music_set = NULL;
1616 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1617 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1618 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1621 ldi->first_level = 0;
1622 ldi->last_level = 0;
1623 ldi->level_group = FALSE;
1624 ldi->handicap_level = 0;
1625 ldi->readonly = TRUE;
1631 /* first copy all values from the parent structure ... */
1634 /* ... then set all fields to default that cannot be inherited from parent.
1635 This is especially important for all those fields that can be set from
1636 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1637 calls 'free()' for all already set token values which requires that no
1638 other structure's pointer may point to them!
1641 ldi->filename = NULL;
1642 ldi->fullpath = NULL;
1643 ldi->basepath = NULL;
1644 ldi->identifier = NULL;
1645 ldi->name = getStringCopy(ANONYMOUS_NAME);
1646 ldi->name_sorting = NULL;
1647 ldi->author = getStringCopy(parent->author);
1649 ldi->imported_from = getStringCopy(parent->imported_from);
1650 ldi->class_desc = getStringCopy(parent->class_desc);
1652 ldi->graphics_set = NULL;
1653 ldi->sounds_set = NULL;
1654 ldi->music_set = NULL;
1655 ldi->graphics_path = NULL;
1656 ldi->sounds_path = NULL;
1657 ldi->music_path = NULL;
1659 ldi->level_group = FALSE;
1660 ldi->parent_link = FALSE;
1662 ldi->node_top = parent->node_top;
1663 ldi->node_parent = parent;
1664 ldi->node_group = NULL;
1670 static void freeTreeInfo(TreeInfo *ldi)
1673 free(ldi->filename);
1675 free(ldi->fullpath);
1677 free(ldi->basepath);
1678 if (ldi->identifier)
1679 free(ldi->identifier);
1683 if (ldi->name_sorting)
1684 free(ldi->name_sorting);
1688 if (ldi->class_desc)
1689 free(ldi->class_desc);
1691 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1693 if (ldi->graphics_set)
1694 free(ldi->graphics_set);
1695 if (ldi->sounds_set)
1696 free(ldi->sounds_set);
1698 free(ldi->music_set);
1700 if (ldi->graphics_path)
1701 free(ldi->graphics_path);
1702 if (ldi->sounds_path)
1703 free(ldi->sounds_path);
1704 if (ldi->music_path)
1705 free(ldi->music_path);
1709 void setSetupInfo(struct TokenInfo *token_info,
1710 int token_nr, char *token_value)
1712 int token_type = token_info[token_nr].type;
1713 void *setup_value = token_info[token_nr].value;
1715 if (token_value == NULL)
1718 /* set setup field to corresponding token value */
1723 *(boolean *)setup_value = get_boolean_from_string(token_value);
1727 *(Key *)setup_value = getKeyFromKeyName(token_value);
1731 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1735 *(int *)setup_value = get_integer_from_string(token_value);
1739 if (*(char **)setup_value != NULL)
1740 free(*(char **)setup_value);
1741 *(char **)setup_value = getStringCopy(token_value);
1749 static int compareTreeInfoEntries(const void *object1, const void *object2)
1751 const TreeInfo *entry1 = *((TreeInfo **)object1);
1752 const TreeInfo *entry2 = *((TreeInfo **)object2);
1753 int class_sorting1, class_sorting2;
1756 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1758 class_sorting1 = LEVELSORTING(entry1);
1759 class_sorting2 = LEVELSORTING(entry2);
1763 class_sorting1 = ARTWORKSORTING(entry1);
1764 class_sorting2 = ARTWORKSORTING(entry2);
1767 if (entry1->parent_link || entry2->parent_link)
1768 compare_result = (entry1->parent_link ? -1 : +1);
1769 else if (entry1->sort_priority == entry2->sort_priority)
1771 char *name1 = getStringToLower(entry1->name_sorting);
1772 char *name2 = getStringToLower(entry2->name_sorting);
1774 compare_result = strcmp(name1, name2);
1779 else if (class_sorting1 == class_sorting2)
1780 compare_result = entry1->sort_priority - entry2->sort_priority;
1782 compare_result = class_sorting1 - class_sorting2;
1784 return compare_result;
1787 static void createParentTreeInfoNode(TreeInfo *node_parent)
1791 if (node_parent == NULL)
1794 ti_new = newTreeInfo();
1795 setTreeInfoToDefaults(ti_new, node_parent->type);
1797 ti_new->node_parent = node_parent;
1798 ti_new->parent_link = TRUE;
1801 setString(&ti_new->identifier, node_parent->identifier);
1802 setString(&ti_new->name, ".. (parent directory)");
1803 setString(&ti_new->name_sorting, ti_new->name);
1805 setString(&ti_new->filename, "..");
1806 setString(&ti_new->fullpath, node_parent->fullpath);
1808 ti_new->sort_priority = node_parent->sort_priority;
1809 ti_new->latest_engine = node_parent->latest_engine;
1811 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1813 ti_new->identifier = getStringCopy(node_parent->identifier);
1814 ti_new->name = ".. (parent directory)";
1815 ti_new->name_sorting = getStringCopy(ti_new->name);
1817 ti_new->filename = "..";
1818 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1820 ti_new->sort_priority = node_parent->sort_priority;
1821 ti_new->latest_engine = node_parent->latest_engine;
1823 ti_new->class_desc = getLevelClassDescription(ti_new);
1826 pushTreeInfo(&node_parent->node_group, ti_new);
1829 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1830 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1832 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1833 TreeInfo *node_parent,
1834 char *level_directory,
1835 char *directory_name)
1837 char *directory_path = getPath2(level_directory, directory_name);
1838 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1839 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1840 LevelDirTree *leveldir_new = NULL;
1843 if (setup_file_hash == NULL)
1845 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1847 free(directory_path);
1853 leveldir_new = newTreeInfo();
1856 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1858 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1860 leveldir_new->filename = getStringCopy(directory_name);
1862 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1864 /* set all structure fields according to the token/value pairs */
1865 ldi = *leveldir_new;
1866 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1867 setSetupInfo(levelinfo_tokens, i,
1868 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1869 *leveldir_new = ldi;
1872 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1873 setString(&leveldir_new->name, leveldir_new->filename);
1875 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1877 free(leveldir_new->name);
1878 leveldir_new->name = getStringCopy(leveldir_new->filename);
1882 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1884 if (leveldir_new->identifier == NULL)
1885 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1887 if (leveldir_new->name_sorting == NULL)
1888 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1890 if (node_parent == NULL) /* top level group */
1892 leveldir_new->basepath = getStringCopy(level_directory);
1893 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1895 else /* sub level group */
1897 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1898 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1901 if (leveldir_new->levels < 1)
1902 leveldir_new->levels = 1;
1904 leveldir_new->last_level =
1905 leveldir_new->first_level + leveldir_new->levels - 1;
1908 leveldir_new->user_defined =
1909 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1911 leveldir_new->user_defined =
1912 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1915 leveldir_new->color = LEVELCOLOR(leveldir_new);
1917 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1919 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1922 leveldir_new->handicap_level = /* set handicap to default value */
1923 (leveldir_new->user_defined ?
1924 leveldir_new->last_level :
1925 leveldir_new->first_level);
1927 pushTreeInfo(node_first, leveldir_new);
1929 freeSetupFileHash(setup_file_hash);
1931 if (leveldir_new->level_group)
1933 /* create node to link back to current level directory */
1934 createParentTreeInfoNode(leveldir_new);
1936 /* step into sub-directory and look for more level series */
1937 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1938 leveldir_new, directory_path);
1941 free(directory_path);
1947 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1948 TreeInfo *node_parent,
1949 char *level_directory)
1952 struct dirent *dir_entry;
1953 boolean valid_entry_found = FALSE;
1955 if ((dir = opendir(level_directory)) == NULL)
1957 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1961 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1963 struct stat file_status;
1964 char *directory_name = dir_entry->d_name;
1965 char *directory_path = getPath2(level_directory, directory_name);
1967 /* skip entries for current and parent directory */
1968 if (strcmp(directory_name, ".") == 0 ||
1969 strcmp(directory_name, "..") == 0)
1971 free(directory_path);
1975 /* find out if directory entry is itself a directory */
1976 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1977 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1979 free(directory_path);
1983 free(directory_path);
1985 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1986 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1987 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1990 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1997 if (!valid_entry_found)
1999 /* check if this directory directly contains a file "levelinfo.conf" */
2000 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2001 level_directory, ".");
2004 if (!valid_entry_found)
2005 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2009 void LoadLevelInfo()
2011 InitUserLevelDirectory(getLoginName());
2013 DrawInitText("Loading level series:", 120, FC_GREEN);
2015 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2016 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2018 /* before sorting, the first entries will be from the user directory */
2019 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2021 if (leveldir_first == NULL)
2022 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2024 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2027 dumpTreeInfo(leveldir_first, 0);
2031 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2032 TreeInfo *node_parent,
2033 char *base_directory,
2034 char *directory_name, int type)
2036 char *directory_path = getPath2(base_directory, directory_name);
2037 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2038 SetupFileHash *setup_file_hash = NULL;
2039 TreeInfo *artwork_new = NULL;
2042 if (access(filename, F_OK) == 0) /* file exists */
2043 setup_file_hash = loadSetupFileHash(filename);
2045 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2048 struct dirent *dir_entry;
2049 boolean valid_file_found = FALSE;
2051 if ((dir = opendir(directory_path)) != NULL)
2053 while ((dir_entry = readdir(dir)) != NULL)
2055 char *entry_name = dir_entry->d_name;
2057 if (FileIsArtworkType(entry_name, type))
2059 valid_file_found = TRUE;
2067 if (!valid_file_found)
2069 if (strcmp(directory_name, ".") != 0)
2070 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2072 free(directory_path);
2079 artwork_new = newTreeInfo();
2082 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2084 setTreeInfoToDefaults(artwork_new, type);
2086 artwork_new->filename = getStringCopy(directory_name);
2088 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2091 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2094 /* set all structure fields according to the token/value pairs */
2096 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2097 setSetupInfo(levelinfo_tokens, i,
2098 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2102 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2103 setString(&artwork_new->name, artwork_new->filename);
2105 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2107 free(artwork_new->name);
2108 artwork_new->name = getStringCopy(artwork_new->filename);
2113 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2116 if (artwork_new->identifier == NULL)
2117 artwork_new->identifier = getStringCopy(artwork_new->filename);
2119 if (artwork_new->name_sorting == NULL)
2120 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2123 if (node_parent == NULL) /* top level group */
2125 artwork_new->basepath = getStringCopy(base_directory);
2126 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2128 else /* sub level group */
2130 artwork_new->basepath = getStringCopy(node_parent->basepath);
2131 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2135 artwork_new->user_defined =
2136 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2138 artwork_new->user_defined =
2139 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2142 /* (may use ".sort_priority" from "setup_file_hash" above) */
2143 artwork_new->color = ARTWORKCOLOR(artwork_new);
2145 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2147 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2150 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2153 if (artwork_new->name != NULL)
2155 free(artwork_new->name);
2156 artwork_new->name = NULL;
2161 if (artwork_new->identifier != NULL)
2163 free(artwork_new->identifier);
2164 artwork_new->identifier = NULL;
2168 if (strcmp(artwork_new->filename, ".") == 0)
2170 if (artwork_new->user_defined)
2173 setString(&artwork_new->identifier, "private");
2175 artwork_new->identifier = getStringCopy("private");
2177 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2182 setString(&artwork_new->identifier, "classic");
2184 artwork_new->identifier = getStringCopy("classic");
2186 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2189 /* set to new values after changing ".sort_priority" */
2190 artwork_new->color = ARTWORKCOLOR(artwork_new);
2192 setString(&artwork_new->class_desc,
2193 getLevelClassDescription(artwork_new));
2195 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2201 setString(&artwork_new->identifier, artwork_new->filename);
2203 artwork_new->identifier = getStringCopy(artwork_new->filename);
2208 setString(&artwork_new->name, artwork_new->identifier);
2209 setString(&artwork_new->name_sorting, artwork_new->name);
2211 artwork_new->name = getStringCopy(artwork_new->identifier);
2212 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2216 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2218 pushTreeInfo(node_first, artwork_new);
2220 freeSetupFileHash(setup_file_hash);
2222 free(directory_path);
2228 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2229 TreeInfo *node_parent,
2230 char *base_directory, int type)
2233 struct dirent *dir_entry;
2234 boolean valid_entry_found = FALSE;
2236 if ((dir = opendir(base_directory)) == NULL)
2238 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2239 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2243 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2245 struct stat file_status;
2246 char *directory_name = dir_entry->d_name;
2247 char *directory_path = getPath2(base_directory, directory_name);
2249 /* skip entries for current and parent directory */
2250 if (strcmp(directory_name, ".") == 0 ||
2251 strcmp(directory_name, "..") == 0)
2253 free(directory_path);
2257 /* find out if directory entry is itself a directory */
2258 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2259 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2261 free(directory_path);
2265 free(directory_path);
2267 /* check if this directory contains artwork with or without config file */
2268 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2270 directory_name, type);
2275 /* check if this directory directly contains artwork itself */
2276 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2277 base_directory, ".",
2279 if (!valid_entry_found)
2280 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2284 static TreeInfo *getDummyArtworkInfo(int type)
2286 /* this is only needed when there is completely no artwork available */
2287 TreeInfo *artwork_new = newTreeInfo();
2289 setTreeInfoToDefaults(artwork_new, type);
2292 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2293 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2294 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2296 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2297 setString(&artwork_new->name, UNDEFINED_FILENAME);
2298 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2300 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2301 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2302 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2304 if (artwork_new->name != NULL)
2305 free(artwork_new->name);
2307 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2308 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2309 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2315 void LoadArtworkInfo()
2317 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2319 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2320 options.graphics_directory,
2321 TREE_TYPE_GRAPHICS_DIR);
2322 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2323 getUserGraphicsDir(),
2324 TREE_TYPE_GRAPHICS_DIR);
2326 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2327 options.sounds_directory,
2328 TREE_TYPE_SOUNDS_DIR);
2329 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2331 TREE_TYPE_SOUNDS_DIR);
2333 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2334 options.music_directory,
2335 TREE_TYPE_MUSIC_DIR);
2336 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2338 TREE_TYPE_MUSIC_DIR);
2340 if (artwork.gfx_first == NULL)
2341 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2342 if (artwork.snd_first == NULL)
2343 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2344 if (artwork.mus_first == NULL)
2345 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2347 /* before sorting, the first entries will be from the user directory */
2348 artwork.gfx_current =
2349 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2350 if (artwork.gfx_current == NULL)
2351 artwork.gfx_current =
2352 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2353 if (artwork.gfx_current == NULL)
2354 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2356 artwork.snd_current =
2357 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2358 if (artwork.snd_current == NULL)
2359 artwork.snd_current =
2360 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2361 if (artwork.snd_current == NULL)
2362 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2364 artwork.mus_current =
2365 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2366 if (artwork.mus_current == NULL)
2367 artwork.mus_current =
2368 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2369 if (artwork.mus_current == NULL)
2370 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2372 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2373 artwork.snd_current_identifier = artwork.snd_current->identifier;
2374 artwork.mus_current_identifier = artwork.mus_current->identifier;
2377 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2378 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2379 printf("music set == %s\n\n", artwork.mus_current_identifier);
2382 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2383 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2384 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2387 dumpTreeInfo(artwork.gfx_first, 0);
2388 dumpTreeInfo(artwork.snd_first, 0);
2389 dumpTreeInfo(artwork.mus_first, 0);
2393 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2394 LevelDirTree *level_node)
2396 /* recursively check all level directories for artwork sub-directories */
2400 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2401 ARTWORK_DIRECTORY((*artwork_node)->type));
2404 if (!level_node->parent_link)
2405 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2406 level_node->filename, level_node->name);
2409 if (!level_node->parent_link)
2411 TreeInfo *topnode_last = *artwork_node;
2413 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2414 (*artwork_node)->type);
2416 if (topnode_last != *artwork_node)
2418 free((*artwork_node)->identifier);
2419 free((*artwork_node)->name);
2420 free((*artwork_node)->name_sorting);
2422 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2423 (*artwork_node)->name = getStringCopy(level_node->name);
2424 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2426 (*artwork_node)->sort_priority = level_node->sort_priority;
2427 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2433 if (level_node->node_group != NULL)
2434 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2436 level_node = level_node->next;
2440 void LoadLevelArtworkInfo()
2442 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2444 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2445 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2446 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2448 /* needed for reloading level artwork not known at ealier stage */
2450 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2452 artwork.gfx_current =
2453 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2454 if (artwork.gfx_current == NULL)
2455 artwork.gfx_current =
2456 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2457 if (artwork.gfx_current == NULL)
2458 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2461 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2463 artwork.snd_current =
2464 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2465 if (artwork.snd_current == NULL)
2466 artwork.snd_current =
2467 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2468 if (artwork.snd_current == NULL)
2469 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2472 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2474 artwork.mus_current =
2475 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2476 if (artwork.mus_current == NULL)
2477 artwork.mus_current =
2478 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2479 if (artwork.mus_current == NULL)
2480 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2483 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2484 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2485 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2488 dumpTreeInfo(artwork.gfx_first, 0);
2489 dumpTreeInfo(artwork.snd_first, 0);
2490 dumpTreeInfo(artwork.mus_first, 0);
2494 static void SaveUserLevelInfo()
2496 LevelDirTree *level_info;
2501 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2503 if (!(file = fopen(filename, MODE_WRITE)))
2505 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2510 level_info = newTreeInfo();
2512 /* always start with reliable default values */
2513 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2516 setString(&level_info->name, getLoginName());
2517 setString(&level_info->author, getRealName());
2518 level_info->levels = 100;
2519 level_info->first_level = 1;
2520 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2521 level_info->readonly = FALSE;
2522 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2523 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2524 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2526 ldi.name = getStringCopy(getLoginName());
2527 ldi.author = getStringCopy(getRealName());
2529 ldi.first_level = 1;
2530 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2531 ldi.readonly = FALSE;
2532 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2533 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2534 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2537 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2538 getCookie("LEVELINFO")));
2541 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2542 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2543 i != LEVELINFO_TOKEN_NAME_SORTING &&
2544 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2545 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2549 SetFilePermissions(filename, PERMS_PRIVATE);
2551 freeTreeInfo(level_info);
2555 char *getSetupValue(int type, void *value)
2557 static char value_string[MAX_LINE_LEN];
2565 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2569 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2573 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2577 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2581 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2585 sprintf(value_string, "%d", *(int *)value);
2589 strcpy(value_string, *(char **)value);
2593 value_string[0] = '\0';
2597 return value_string;
2600 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2604 static char token_string[MAX_LINE_LEN];
2605 int token_type = token_info[token_nr].type;
2606 void *setup_value = token_info[token_nr].value;
2607 char *token_text = token_info[token_nr].text;
2608 char *value_string = getSetupValue(token_type, setup_value);
2610 /* build complete token string */
2611 sprintf(token_string, "%s%s", prefix, token_text);
2613 /* build setup entry line */
2614 line = getFormattedSetupEntry(token_string, value_string);
2616 if (token_type == TYPE_KEY_X11)
2618 Key key = *(Key *)setup_value;
2619 char *keyname = getKeyNameFromKey(key);
2621 /* add comment, if useful */
2622 if (strcmp(keyname, "(undefined)") != 0 &&
2623 strcmp(keyname, "(unknown)") != 0)
2625 /* add at least one whitespace */
2627 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2631 strcat(line, keyname);
2638 void LoadLevelSetup_LastSeries()
2640 /* ----------------------------------------------------------------------- */
2641 /* ~/.<program>/levelsetup.conf */
2642 /* ----------------------------------------------------------------------- */
2644 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2645 SetupFileHash *level_setup_hash = NULL;
2647 /* always start with reliable default values */
2648 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2650 if ((level_setup_hash = loadSetupFileHash(filename)))
2652 char *last_level_series =
2653 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2655 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2657 if (leveldir_current == NULL)
2658 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2660 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2662 freeSetupFileHash(level_setup_hash);
2665 Error(ERR_WARN, "using default setup values");
2670 void SaveLevelSetup_LastSeries()
2672 /* ----------------------------------------------------------------------- */
2673 /* ~/.<program>/levelsetup.conf */
2674 /* ----------------------------------------------------------------------- */
2676 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2677 char *level_subdir = leveldir_current->filename;
2680 InitUserDataDirectory();
2682 if (!(file = fopen(filename, MODE_WRITE)))
2684 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2689 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2690 getCookie("LEVELSETUP")));
2691 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2696 SetFilePermissions(filename, PERMS_PRIVATE);
2701 static void checkSeriesInfo()
2703 static char *level_directory = NULL;
2705 struct dirent *dir_entry;
2707 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2709 level_directory = getPath2((leveldir_current->user_defined ?
2710 getUserLevelDir(NULL) :
2711 options.level_directory),
2712 leveldir_current->fullpath);
2714 if ((dir = opendir(level_directory)) == NULL)
2716 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2720 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2722 if (strlen(dir_entry->d_name) > 4 &&
2723 dir_entry->d_name[3] == '.' &&
2724 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2726 char levelnum_str[4];
2729 strncpy(levelnum_str, dir_entry->d_name, 3);
2730 levelnum_str[3] = '\0';
2732 levelnum_value = atoi(levelnum_str);
2735 if (levelnum_value < leveldir_current->first_level)
2737 Error(ERR_WARN, "additional level %d found", levelnum_value);
2738 leveldir_current->first_level = levelnum_value;
2740 else if (levelnum_value > leveldir_current->last_level)
2742 Error(ERR_WARN, "additional level %d found", levelnum_value);
2743 leveldir_current->last_level = levelnum_value;
2752 void LoadLevelSetup_SeriesInfo()
2755 SetupFileHash *level_setup_hash = NULL;
2756 char *level_subdir = leveldir_current->filename;
2758 /* always start with reliable default values */
2759 level_nr = leveldir_current->first_level;
2761 checkSeriesInfo(leveldir_current);
2763 /* ----------------------------------------------------------------------- */
2764 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2765 /* ----------------------------------------------------------------------- */
2767 level_subdir = leveldir_current->filename;
2769 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2771 if ((level_setup_hash = loadSetupFileHash(filename)))
2775 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2779 level_nr = atoi(token_value);
2781 if (level_nr < leveldir_current->first_level)
2782 level_nr = leveldir_current->first_level;
2783 if (level_nr > leveldir_current->last_level)
2784 level_nr = leveldir_current->last_level;
2787 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2791 int level_nr = atoi(token_value);
2793 if (level_nr < leveldir_current->first_level)
2794 level_nr = leveldir_current->first_level;
2795 if (level_nr > leveldir_current->last_level + 1)
2796 level_nr = leveldir_current->last_level;
2798 if (leveldir_current->user_defined)
2799 level_nr = leveldir_current->last_level;
2801 leveldir_current->handicap_level = level_nr;
2804 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2806 freeSetupFileHash(level_setup_hash);
2809 Error(ERR_WARN, "using default setup values");
2814 void SaveLevelSetup_SeriesInfo()
2817 char *level_subdir = leveldir_current->filename;
2818 char *level_nr_str = int2str(level_nr, 0);
2819 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2822 /* ----------------------------------------------------------------------- */
2823 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2824 /* ----------------------------------------------------------------------- */
2826 InitLevelSetupDirectory(level_subdir);
2828 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2830 if (!(file = fopen(filename, MODE_WRITE)))
2832 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2837 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2838 getCookie("LEVELSETUP")));
2839 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2841 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2842 handicap_level_str));
2846 SetFilePermissions(filename, PERMS_PRIVATE);