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_YELLOW : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_YELLOW : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_SB(n) ? FC_YELLOW : \
49 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
50 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
53 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
54 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
55 IS_LEVELCLASS_BD(n) ? 2 : \
56 IS_LEVELCLASS_EM(n) ? 3 : \
57 IS_LEVELCLASS_SP(n) ? 4 : \
58 IS_LEVELCLASS_DX(n) ? 5 : \
59 IS_LEVELCLASS_SB(n) ? 6 : \
60 IS_LEVELCLASS_CONTRIB(n) ? 7 : \
61 IS_LEVELCLASS_PRIVATE(n) ? 8 : \
64 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
65 IS_ARTWORKCLASS_CONTRIB(n) ? FC_GREEN : \
66 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
67 IS_ARTWORKCLASS_LEVEL(n) ? FC_YELLOW : \
70 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
71 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
72 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
73 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
76 #define TOKEN_VALUE_POSITION_SHORT 32
77 #define TOKEN_VALUE_POSITION_DEFAULT 40
78 #define TOKEN_COMMENT_POSITION_DEFAULT 60
80 #define MAX_COOKIE_LEN 256
82 static int token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
83 static int token_comment_position = TOKEN_COMMENT_POSITION_DEFAULT;
86 /* ------------------------------------------------------------------------- */
88 /* ------------------------------------------------------------------------- */
90 static char *getLevelClassDescription(TreeInfo *ldi)
92 int position = ldi->sort_priority / 100;
94 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
95 return levelclass_desc[position];
97 return "Unknown Level Class";
100 static char *getUserLevelDir(char *level_subdir)
102 static char *userlevel_dir = NULL;
103 char *data_dir = getUserDataDir();
104 char *userlevel_subdir = LEVELS_DIRECTORY;
106 checked_free(userlevel_dir);
108 if (level_subdir != NULL)
109 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
111 userlevel_dir = getPath2(data_dir, userlevel_subdir);
113 return userlevel_dir;
116 static char *getScoreDir(char *level_subdir)
118 static char *score_dir = NULL;
119 char *data_dir = getCommonDataDir();
120 char *score_subdir = SCORES_DIRECTORY;
122 checked_free(score_dir);
124 if (level_subdir != NULL)
125 score_dir = getPath3(data_dir, score_subdir, level_subdir);
127 score_dir = getPath2(data_dir, score_subdir);
132 static char *getLevelSetupDir(char *level_subdir)
134 static char *levelsetup_dir = NULL;
135 char *data_dir = getUserDataDir();
136 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
138 checked_free(levelsetup_dir);
140 if (level_subdir != NULL)
141 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
143 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
145 return levelsetup_dir;
148 static char *getLevelDirFromTreeInfo(TreeInfo *node)
150 static char *level_dir = NULL;
153 return options.level_directory;
155 checked_free(level_dir);
157 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
158 options.level_directory), node->fullpath);
163 char *getCurrentLevelDir()
165 return getLevelDirFromTreeInfo(leveldir_current);
168 static char *getTapeDir(char *level_subdir)
170 static char *tape_dir = NULL;
171 char *data_dir = getUserDataDir();
172 char *tape_subdir = TAPES_DIRECTORY;
174 checked_free(tape_dir);
176 if (level_subdir != NULL)
177 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
179 tape_dir = getPath2(data_dir, tape_subdir);
184 static char *getSolutionTapeDir()
186 static char *tape_dir = NULL;
187 char *data_dir = getCurrentLevelDir();
188 char *tape_subdir = TAPES_DIRECTORY;
190 checked_free(tape_dir);
192 tape_dir = getPath2(data_dir, tape_subdir);
197 static char *getDefaultGraphicsDir(char *graphics_subdir)
199 static char *graphics_dir = NULL;
201 if (graphics_subdir == NULL)
202 return options.graphics_directory;
204 checked_free(graphics_dir);
206 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
211 static char *getDefaultSoundsDir(char *sounds_subdir)
213 static char *sounds_dir = NULL;
215 if (sounds_subdir == NULL)
216 return options.sounds_directory;
218 checked_free(sounds_dir);
220 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
225 static char *getDefaultMusicDir(char *music_subdir)
227 static char *music_dir = NULL;
229 if (music_subdir == NULL)
230 return options.music_directory;
232 checked_free(music_dir);
234 music_dir = getPath2(options.music_directory, music_subdir);
239 static char *getDefaultArtworkSet(int type)
241 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
242 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
243 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
246 static char *getDefaultArtworkDir(int type)
248 return (type == TREE_TYPE_GRAPHICS_DIR ?
249 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
250 type == TREE_TYPE_SOUNDS_DIR ?
251 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
252 type == TREE_TYPE_MUSIC_DIR ?
253 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
256 static char *getUserGraphicsDir()
258 static char *usergraphics_dir = NULL;
260 if (usergraphics_dir == NULL)
261 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
263 return usergraphics_dir;
266 static char *getUserSoundsDir()
268 static char *usersounds_dir = NULL;
270 if (usersounds_dir == NULL)
271 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
273 return usersounds_dir;
276 static char *getUserMusicDir()
278 static char *usermusic_dir = NULL;
280 if (usermusic_dir == NULL)
281 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
283 return usermusic_dir;
286 static char *getSetupArtworkDir(TreeInfo *ti)
288 static char *artwork_dir = NULL;
290 checked_free(artwork_dir);
292 artwork_dir = getPath2(ti->basepath, ti->fullpath);
297 char *setLevelArtworkDir(TreeInfo *ti)
299 char **artwork_path_ptr, **artwork_set_ptr;
300 TreeInfo *level_artwork;
302 if (ti == NULL || leveldir_current == NULL)
305 artwork_path_ptr = LEVELDIR_ARTWORK_PATH_PTR(leveldir_current, ti->type);
306 artwork_set_ptr = LEVELDIR_ARTWORK_SET_PTR( leveldir_current, ti->type);
308 checked_free(*artwork_path_ptr);
310 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
311 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
314 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
315 normally result in using the artwork configured in the setup menu. But
316 if an artwork subdirectory exists (which might contain custom artwork
317 or an artwork configuration file), this level artwork must be treated
318 as relative to the default "classic" artwork, not to the artwork that
319 is currently configured in the setup menu. */
321 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
323 checked_free(*artwork_set_ptr);
327 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
328 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
332 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
333 *artwork_set_ptr = NULL;
339 return *artwork_set_ptr;
342 inline static char *getLevelArtworkSet(int type)
344 if (leveldir_current == NULL)
347 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
350 inline static char *getLevelArtworkDir(int type)
352 if (leveldir_current == NULL)
353 return UNDEFINED_FILENAME;
355 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
358 char *getTapeFilename(int nr)
360 static char *filename = NULL;
361 char basename[MAX_FILENAME_LEN];
363 checked_free(filename);
365 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
366 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
371 char *getSolutionTapeFilename(int nr)
373 static char *filename = NULL;
374 char basename[MAX_FILENAME_LEN];
376 checked_free(filename);
378 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
379 filename = getPath2(getSolutionTapeDir(), basename);
384 char *getScoreFilename(int nr)
386 static char *filename = NULL;
387 char basename[MAX_FILENAME_LEN];
389 checked_free(filename);
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 checked_free(filename);
403 filename = getPath2(getSetupDir(), SETUP_FILENAME);
408 char *getEditorSetupFilename()
410 static char *filename = NULL;
412 checked_free(filename);
413 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
415 if (fileExists(filename))
418 checked_free(filename);
419 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
424 char *getHelpAnimFilename()
426 static char *filename = NULL;
428 checked_free(filename);
430 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
435 char *getHelpTextFilename()
437 static char *filename = NULL;
439 checked_free(filename);
441 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
446 char *getLevelSetInfoFilename()
448 static char *filename = NULL;
463 for (i = 0; basenames[i] != NULL; i++)
465 checked_free(filename);
466 filename = getPath2(getCurrentLevelDir(), basenames[i]);
468 if (fileExists(filename))
475 static char *getCorrectedArtworkBasename(char *basename)
477 char *basename_corrected = basename;
479 #if defined(PLATFORM_MSDOS)
480 if (program.filename_prefix != NULL)
482 int prefix_len = strlen(program.filename_prefix);
484 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
485 basename_corrected = &basename[prefix_len];
487 /* if corrected filename is still longer than standard MS-DOS filename
488 size (8 characters + 1 dot + 3 characters file extension), shorten
489 filename by writing file extension after 8th basename character */
490 if (strlen(basename_corrected) > 8 + 1 + 3)
492 static char *msdos_filename = NULL;
494 checked_free(msdos_filename);
496 msdos_filename = getStringCopy(basename_corrected);
497 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
499 basename_corrected = msdos_filename;
504 return basename_corrected;
507 char *getCustomImageFilename(char *basename)
509 static char *filename = NULL;
510 boolean skip_setup_artwork = FALSE;
512 checked_free(filename);
514 basename = getCorrectedArtworkBasename(basename);
516 if (!setup.override_level_graphics)
518 /* 1st try: look for special artwork in current level series directory */
519 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
520 if (fileExists(filename))
525 /* check if there is special artwork configured in level series config */
526 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
528 /* 2nd try: look for special artwork configured in level series config */
529 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
530 if (fileExists(filename))
535 /* take missing artwork configured in level set config from default */
536 skip_setup_artwork = TRUE;
540 if (!skip_setup_artwork)
542 /* 3rd try: look for special artwork in configured artwork directory */
543 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
544 if (fileExists(filename))
550 /* 4th try: look for default artwork in new default artwork directory */
551 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
552 if (fileExists(filename))
557 /* 5th try: look for default artwork in old default artwork directory */
558 filename = getPath2(options.graphics_directory, basename);
559 if (fileExists(filename))
562 return NULL; /* cannot find specified artwork file anywhere */
565 char *getCustomSoundFilename(char *basename)
567 static char *filename = NULL;
568 boolean skip_setup_artwork = FALSE;
570 checked_free(filename);
572 basename = getCorrectedArtworkBasename(basename);
574 if (!setup.override_level_sounds)
576 /* 1st try: look for special artwork in current level series directory */
577 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
578 if (fileExists(filename))
583 /* check if there is special artwork configured in level series config */
584 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
586 /* 2nd try: look for special artwork configured in level series config */
587 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
588 if (fileExists(filename))
593 /* take missing artwork configured in level set config from default */
594 skip_setup_artwork = TRUE;
598 if (!skip_setup_artwork)
600 /* 3rd try: look for special artwork in configured artwork directory */
601 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
602 if (fileExists(filename))
608 /* 4th try: look for default artwork in new default artwork directory */
609 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
610 if (fileExists(filename))
615 /* 5th try: look for default artwork in old default artwork directory */
616 filename = getPath2(options.sounds_directory, basename);
617 if (fileExists(filename))
620 return NULL; /* cannot find specified artwork file anywhere */
623 char *getCustomMusicFilename(char *basename)
625 static char *filename = NULL;
626 boolean skip_setup_artwork = FALSE;
628 checked_free(filename);
630 basename = getCorrectedArtworkBasename(basename);
632 if (!setup.override_level_music)
634 /* 1st try: look for special artwork in current level series directory */
635 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
636 if (fileExists(filename))
641 /* check if there is special artwork configured in level series config */
642 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
644 /* 2nd try: look for special artwork configured in level series config */
645 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
646 if (fileExists(filename))
651 /* take missing artwork configured in level set config from default */
652 skip_setup_artwork = TRUE;
656 if (!skip_setup_artwork)
658 /* 3rd try: look for special artwork in configured artwork directory */
659 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
660 if (fileExists(filename))
666 /* 4th try: look for default artwork in new default artwork directory */
667 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
668 if (fileExists(filename))
673 /* 5th try: look for default artwork in old default artwork directory */
674 filename = getPath2(options.music_directory, basename);
675 if (fileExists(filename))
678 return NULL; /* cannot find specified artwork file anywhere */
681 char *getCustomArtworkFilename(char *basename, int type)
683 if (type == ARTWORK_TYPE_GRAPHICS)
684 return getCustomImageFilename(basename);
685 else if (type == ARTWORK_TYPE_SOUNDS)
686 return getCustomSoundFilename(basename);
687 else if (type == ARTWORK_TYPE_MUSIC)
688 return getCustomMusicFilename(basename);
690 return UNDEFINED_FILENAME;
693 char *getCustomArtworkConfigFilename(int type)
695 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
698 char *getCustomArtworkLevelConfigFilename(int type)
700 static char *filename = NULL;
702 checked_free(filename);
704 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
709 char *getCustomMusicDirectory(void)
711 static char *directory = NULL;
712 boolean skip_setup_artwork = FALSE;
714 checked_free(directory);
716 if (!setup.override_level_music)
718 /* 1st try: look for special artwork in current level series directory */
719 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
720 if (fileExists(directory))
725 /* check if there is special artwork configured in level series config */
726 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
728 /* 2nd try: look for special artwork configured in level series config */
729 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
730 if (fileExists(directory))
735 /* take missing artwork configured in level set config from default */
736 skip_setup_artwork = TRUE;
740 if (!skip_setup_artwork)
742 /* 3rd try: look for special artwork in configured artwork directory */
743 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
744 if (fileExists(directory))
750 /* 4th try: look for default artwork in new default artwork directory */
751 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
752 if (fileExists(directory))
757 /* 5th try: look for default artwork in old default artwork directory */
758 directory = getStringCopy(options.music_directory);
759 if (fileExists(directory))
762 return NULL; /* cannot find specified artwork file anywhere */
765 void InitTapeDirectory(char *level_subdir)
767 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
768 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
769 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
772 void InitScoreDirectory(char *level_subdir)
774 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
775 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
776 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
779 static void SaveUserLevelInfo();
781 void InitUserLevelDirectory(char *level_subdir)
783 if (!fileExists(getUserLevelDir(level_subdir)))
785 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
786 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
787 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
793 void InitLevelSetupDirectory(char *level_subdir)
795 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
796 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
797 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
801 /* ------------------------------------------------------------------------- */
802 /* some functions to handle lists of level and artwork directories */
803 /* ------------------------------------------------------------------------- */
805 TreeInfo *newTreeInfo()
807 return checked_calloc(sizeof(TreeInfo));
810 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
812 node_new->next = *node_first;
813 *node_first = node_new;
816 int numTreeInfo(TreeInfo *node)
829 boolean validLevelSeries(TreeInfo *node)
831 return (node != NULL && !node->node_group && !node->parent_link);
834 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
839 if (node->node_group) /* enter level group (step down into tree) */
840 return getFirstValidTreeInfoEntry(node->node_group);
841 else if (node->parent_link) /* skip start entry of level group */
843 if (node->next) /* get first real level series entry */
844 return getFirstValidTreeInfoEntry(node->next);
845 else /* leave empty level group and go on */
846 return getFirstValidTreeInfoEntry(node->node_parent->next);
848 else /* this seems to be a regular level series */
852 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
857 if (node->node_parent == NULL) /* top level group */
858 return *node->node_top;
859 else /* sub level group */
860 return node->node_parent->node_group;
863 int numTreeInfoInGroup(TreeInfo *node)
865 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
868 int posTreeInfo(TreeInfo *node)
870 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
875 if (node_cmp == node)
879 node_cmp = node_cmp->next;
885 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
887 TreeInfo *node_default = node;
902 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
904 if (identifier == NULL)
909 if (node->node_group)
911 TreeInfo *node_group;
913 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
918 else if (!node->parent_link)
920 if (strcmp(identifier, node->identifier) == 0)
930 TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent,
931 TreeInfo *node, boolean skip_sets_without_levels)
938 if (!node->parent_link && !node->level_group &&
939 skip_sets_without_levels && node->levels == 0)
940 return cloneTreeNode(node_top, node_parent, node->next,
941 skip_sets_without_levels);
943 node_new = newTreeInfo();
945 *node_new = *node; /* copy complete node */
947 node_new->node_top = node_top; /* correct top node link */
948 node_new->node_parent = node_parent; /* correct parent node link */
950 if (node->level_group)
951 node_new->node_group = cloneTreeNode(node_top, node_new, node->node_group,
952 skip_sets_without_levels);
954 node_new->next = cloneTreeNode(node_top, node_parent, node->next,
955 skip_sets_without_levels);
960 void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets)
962 TreeInfo *ti_cloned = cloneTreeNode(ti_new, NULL, ti, skip_empty_sets);
967 void dumpTreeInfo(TreeInfo *node, int depth)
971 printf("Dumping TreeInfo:\n");
975 for (i = 0; i < (depth + 1) * 3; i++)
978 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
979 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
981 if (node->node_group != NULL)
982 dumpTreeInfo(node->node_group, depth + 1);
988 void sortTreeInfo(TreeInfo **node_first,
989 int (*compare_function)(const void *, const void *))
991 int num_nodes = numTreeInfo(*node_first);
992 TreeInfo **sort_array;
993 TreeInfo *node = *node_first;
999 /* allocate array for sorting structure pointers */
1000 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
1002 /* writing structure pointers to sorting array */
1003 while (i < num_nodes && node) /* double boundary check... */
1005 sort_array[i] = node;
1011 /* sorting the structure pointers in the sorting array */
1012 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
1015 /* update the linkage of list elements with the sorted node array */
1016 for (i = 0; i < num_nodes - 1; i++)
1017 sort_array[i]->next = sort_array[i + 1];
1018 sort_array[num_nodes - 1]->next = NULL;
1020 /* update the linkage of the main list anchor pointer */
1021 *node_first = sort_array[0];
1025 /* now recursively sort the level group structures */
1029 if (node->node_group != NULL)
1030 sortTreeInfo(&node->node_group, compare_function);
1037 /* ========================================================================= */
1038 /* some stuff from "files.c" */
1039 /* ========================================================================= */
1041 #if defined(PLATFORM_WIN32)
1043 #define S_IRGRP S_IRUSR
1046 #define S_IROTH S_IRUSR
1049 #define S_IWGRP S_IWUSR
1052 #define S_IWOTH S_IWUSR
1055 #define S_IXGRP S_IXUSR
1058 #define S_IXOTH S_IXUSR
1061 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1066 #endif /* PLATFORM_WIN32 */
1068 /* file permissions for newly written files */
1069 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1070 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1071 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1073 #define MODE_W_PRIVATE (S_IWUSR)
1074 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1075 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1077 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1078 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1080 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1081 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1083 char *getUserDataDir(void)
1085 static char *userdata_dir = NULL;
1087 if (userdata_dir == NULL)
1088 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1090 return userdata_dir;
1093 char *getCommonDataDir(void)
1095 static char *common_data_dir = NULL;
1097 #if defined(PLATFORM_WIN32)
1098 if (common_data_dir == NULL)
1100 char *dir = checked_malloc(MAX_PATH + 1);
1102 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1103 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1104 common_data_dir = getPath2(dir, program.userdata_directory);
1106 common_data_dir = options.rw_base_directory;
1109 if (common_data_dir == NULL)
1110 common_data_dir = options.rw_base_directory;
1113 return common_data_dir;
1118 return getUserDataDir();
1121 static mode_t posix_umask(mode_t mask)
1123 #if defined(PLATFORM_UNIX)
1130 static int posix_mkdir(const char *pathname, mode_t mode)
1132 #if defined(PLATFORM_WIN32)
1133 return mkdir(pathname);
1135 return mkdir(pathname, mode);
1139 void createDirectory(char *dir, char *text, int permission_class)
1141 /* leave "other" permissions in umask untouched, but ensure group parts
1142 of USERDATA_DIR_MODE are not masked */
1143 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1144 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1145 mode_t normal_umask = posix_umask(0);
1146 mode_t group_umask = ~(dir_mode & S_IRWXG);
1147 posix_umask(normal_umask & group_umask);
1149 if (!fileExists(dir))
1150 if (posix_mkdir(dir, dir_mode) != 0)
1151 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1153 posix_umask(normal_umask); /* reset normal umask */
1156 void InitUserDataDirectory()
1158 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1161 void SetFilePermissions(char *filename, int permission_class)
1163 chmod(filename, (permission_class == PERMS_PRIVATE ?
1164 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1167 char *getCookie(char *file_type)
1169 static char cookie[MAX_COOKIE_LEN + 1];
1171 if (strlen(program.cookie_prefix) + 1 +
1172 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1173 return "[COOKIE ERROR]"; /* should never happen */
1175 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1176 program.cookie_prefix, file_type,
1177 program.version_major, program.version_minor);
1182 int getFileVersionFromCookieString(const char *cookie)
1184 const char *ptr_cookie1, *ptr_cookie2;
1185 const char *pattern1 = "_FILE_VERSION_";
1186 const char *pattern2 = "?.?";
1187 const int len_cookie = strlen(cookie);
1188 const int len_pattern1 = strlen(pattern1);
1189 const int len_pattern2 = strlen(pattern2);
1190 const int len_pattern = len_pattern1 + len_pattern2;
1191 int version_major, version_minor;
1193 if (len_cookie <= len_pattern)
1196 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1197 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1199 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1202 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1203 ptr_cookie2[1] != '.' ||
1204 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1207 version_major = ptr_cookie2[0] - '0';
1208 version_minor = ptr_cookie2[2] - '0';
1210 return VERSION_IDENT(version_major, version_minor, 0, 0);
1213 boolean checkCookieString(const char *cookie, const char *template)
1215 const char *pattern = "_FILE_VERSION_?.?";
1216 const int len_cookie = strlen(cookie);
1217 const int len_template = strlen(template);
1218 const int len_pattern = strlen(pattern);
1220 if (len_cookie != len_template)
1223 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1229 /* ------------------------------------------------------------------------- */
1230 /* setup file list and hash handling functions */
1231 /* ------------------------------------------------------------------------- */
1233 char *getFormattedSetupEntry(char *token, char *value)
1236 static char entry[MAX_LINE_LEN];
1238 /* if value is an empty string, just return token without value */
1242 /* start with the token and some spaces to format output line */
1243 sprintf(entry, "%s:", token);
1244 for (i = strlen(entry); i < token_value_position; i++)
1247 /* continue with the token's value */
1248 strcat(entry, value);
1253 SetupFileList *newSetupFileList(char *token, char *value)
1255 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1257 new->token = getStringCopy(token);
1258 new->value = getStringCopy(value);
1265 void freeSetupFileList(SetupFileList *list)
1270 checked_free(list->token);
1271 checked_free(list->value);
1274 freeSetupFileList(list->next);
1279 char *getListEntry(SetupFileList *list, char *token)
1284 if (strcmp(list->token, token) == 0)
1287 return getListEntry(list->next, token);
1290 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1295 if (strcmp(list->token, token) == 0)
1297 checked_free(list->value);
1299 list->value = getStringCopy(value);
1303 else if (list->next == NULL)
1304 return (list->next = newSetupFileList(token, value));
1306 return setListEntry(list->next, token, value);
1309 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1314 if (list->next == NULL)
1315 return (list->next = newSetupFileList(token, value));
1317 return addListEntry(list->next, token, value);
1321 static void printSetupFileList(SetupFileList *list)
1326 printf("token: '%s'\n", list->token);
1327 printf("value: '%s'\n", list->value);
1329 printSetupFileList(list->next);
1334 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1335 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1336 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1337 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1339 #define insert_hash_entry hashtable_insert
1340 #define search_hash_entry hashtable_search
1341 #define change_hash_entry hashtable_change
1342 #define remove_hash_entry hashtable_remove
1345 static unsigned int get_hash_from_key(void *key)
1350 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1351 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1352 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1353 it works better than many other constants, prime or not) has never been
1354 adequately explained.
1356 If you just want to have a good hash function, and cannot wait, djb2
1357 is one of the best string hash functions i know. It has excellent
1358 distribution and speed on many different sets of keys and table sizes.
1359 You are not likely to do better with one of the "well known" functions
1360 such as PJW, K&R, etc.
1362 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1365 char *str = (char *)key;
1366 unsigned int hash = 5381;
1369 while ((c = *str++))
1370 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1375 static int keys_are_equal(void *key1, void *key2)
1377 return (strcmp((char *)key1, (char *)key2) == 0);
1380 SetupFileHash *newSetupFileHash()
1382 SetupFileHash *new_hash =
1383 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1385 if (new_hash == NULL)
1386 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1391 void freeSetupFileHash(SetupFileHash *hash)
1396 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1399 char *getHashEntry(SetupFileHash *hash, char *token)
1404 return search_hash_entry(hash, token);
1407 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1414 value_copy = getStringCopy(value);
1416 /* change value; if it does not exist, insert it as new */
1417 if (!change_hash_entry(hash, token, value_copy))
1418 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1419 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1422 char *removeHashEntry(SetupFileHash *hash, char *token)
1427 return remove_hash_entry(hash, token);
1431 static void printSetupFileHash(SetupFileHash *hash)
1433 BEGIN_HASH_ITERATION(hash, itr)
1435 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1436 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1438 END_HASH_ITERATION(hash, itr)
1442 static void *loadSetupFileData(char *filename, boolean use_hash)
1444 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1445 char *token, *value, *line_ptr;
1446 void *setup_file_data, *insert_ptr = NULL;
1447 boolean read_continued_line = FALSE;
1450 if (!(file = fopen(filename, MODE_READ)))
1452 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1458 setup_file_data = newSetupFileHash();
1460 insert_ptr = setup_file_data = newSetupFileList("", "");
1464 /* read next line of input file */
1465 if (!fgets(line, MAX_LINE_LEN, file))
1468 /* cut trailing newline or carriage return */
1469 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1470 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1473 if (read_continued_line)
1475 /* cut leading whitespaces from input line */
1476 for (line_ptr = line; *line_ptr; line_ptr++)
1477 if (*line_ptr != ' ' && *line_ptr != '\t')
1480 /* append new line to existing line, if there is enough space */
1481 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1482 strcat(previous_line, line_ptr);
1484 strcpy(line, previous_line); /* copy storage buffer to line */
1486 read_continued_line = FALSE;
1489 /* if the last character is '\', continue at next line */
1490 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1492 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1493 strcpy(previous_line, line); /* copy line to storage buffer */
1495 read_continued_line = TRUE;
1500 /* cut trailing comment from input line */
1501 for (line_ptr = line; *line_ptr; line_ptr++)
1503 if (*line_ptr == '#')
1510 /* cut trailing whitespaces from input line */
1511 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1512 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1515 /* ignore empty lines */
1519 /* cut leading whitespaces from token */
1520 for (token = line; *token; token++)
1521 if (*token != ' ' && *token != '\t')
1524 /* start with empty value as reliable default */
1527 /* find end of token to determine start of value */
1528 for (line_ptr = token; *line_ptr; line_ptr++)
1530 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1532 *line_ptr = '\0'; /* terminate token string */
1533 value = line_ptr + 1; /* set beginning of value */
1539 /* cut leading whitespaces from value */
1540 for (; *value; value++)
1541 if (*value != ' ' && *value != '\t')
1546 value = "true"; /* treat tokens without value as "true" */
1552 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1554 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1562 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1563 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1567 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1568 SetupFileList *first_valid_list_entry = setup_file_list->next;
1570 /* free empty list header */
1571 setup_file_list->next = NULL;
1572 freeSetupFileList(setup_file_list);
1573 setup_file_data = first_valid_list_entry;
1575 if (first_valid_list_entry == NULL)
1576 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1579 return setup_file_data;
1582 SetupFileList *loadSetupFileList(char *filename)
1584 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1587 SetupFileHash *loadSetupFileHash(char *filename)
1589 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1592 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1593 char *filename, char *identifier)
1595 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1598 Error(ERR_WARN, "config file '%s' has no file identifier", filename);
1599 else if (!checkCookieString(value, identifier))
1600 Error(ERR_WARN, "config file '%s' has wrong file identifier", filename);
1604 /* ========================================================================= */
1605 /* setup file stuff */
1606 /* ========================================================================= */
1608 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1609 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1610 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1612 /* level directory info */
1613 #define LEVELINFO_TOKEN_IDENTIFIER 0
1614 #define LEVELINFO_TOKEN_NAME 1
1615 #define LEVELINFO_TOKEN_NAME_SORTING 2
1616 #define LEVELINFO_TOKEN_AUTHOR 3
1617 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1618 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1619 #define LEVELINFO_TOKEN_LEVELS 6
1620 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1621 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1622 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1623 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1624 #define LEVELINFO_TOKEN_READONLY 11
1625 #define LEVELINFO_TOKEN_GRAPHICS_SET 12
1626 #define LEVELINFO_TOKEN_SOUNDS_SET 13
1627 #define LEVELINFO_TOKEN_MUSIC_SET 14
1628 #define LEVELINFO_TOKEN_FILENAME 15
1629 #define LEVELINFO_TOKEN_FILETYPE 16
1630 #define LEVELINFO_TOKEN_HANDICAP 17
1631 #define LEVELINFO_TOKEN_SKIP_LEVELS 18
1633 #define NUM_LEVELINFO_TOKENS 19
1635 static LevelDirTree ldi;
1637 static struct TokenInfo levelinfo_tokens[] =
1639 /* level directory info */
1640 { TYPE_STRING, &ldi.identifier, "identifier" },
1641 { TYPE_STRING, &ldi.name, "name" },
1642 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1643 { TYPE_STRING, &ldi.author, "author" },
1644 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1645 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1646 { TYPE_INTEGER, &ldi.levels, "levels" },
1647 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1648 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1649 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1650 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1651 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1652 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1653 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1654 { TYPE_STRING, &ldi.music_set, "music_set" },
1655 { TYPE_STRING, &ldi.level_filename, "filename" },
1656 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1657 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1658 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1661 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1665 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1666 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1667 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1668 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1671 ldi->node_parent = NULL;
1672 ldi->node_group = NULL;
1676 ldi->cl_cursor = -1;
1679 ldi->fullpath = NULL;
1680 ldi->basepath = NULL;
1681 ldi->identifier = NULL;
1682 ldi->name = getStringCopy(ANONYMOUS_NAME);
1683 ldi->name_sorting = NULL;
1684 ldi->author = getStringCopy(ANONYMOUS_NAME);
1686 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1687 ldi->latest_engine = FALSE; /* default: get from level */
1688 ldi->parent_link = FALSE;
1689 ldi->in_user_dir = FALSE;
1690 ldi->user_defined = FALSE;
1692 ldi->class_desc = NULL;
1694 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1696 ldi->imported_from = NULL;
1697 ldi->imported_by = NULL;
1699 ldi->graphics_set = NULL;
1700 ldi->sounds_set = NULL;
1701 ldi->music_set = NULL;
1702 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1703 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1704 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1706 ldi->level_filename = NULL;
1707 ldi->level_filetype = NULL;
1710 ldi->first_level = 0;
1711 ldi->last_level = 0;
1712 ldi->level_group = FALSE;
1713 ldi->handicap_level = 0;
1714 ldi->readonly = TRUE;
1715 ldi->handicap = TRUE;
1716 ldi->skip_levels = FALSE;
1720 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1724 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1726 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1731 /* copy all values from the parent structure */
1733 ldi->type = parent->type;
1735 ldi->node_top = parent->node_top;
1736 ldi->node_parent = parent;
1737 ldi->node_group = NULL;
1741 ldi->cl_cursor = -1;
1744 ldi->fullpath = NULL;
1745 ldi->basepath = NULL;
1746 ldi->identifier = NULL;
1747 ldi->name = getStringCopy(ANONYMOUS_NAME);
1748 ldi->name_sorting = NULL;
1749 ldi->author = getStringCopy(parent->author);
1751 ldi->sort_priority = parent->sort_priority;
1752 ldi->latest_engine = parent->latest_engine;
1753 ldi->parent_link = FALSE;
1754 ldi->in_user_dir = parent->in_user_dir;
1755 ldi->user_defined = parent->user_defined;
1756 ldi->color = parent->color;
1757 ldi->class_desc = getStringCopy(parent->class_desc);
1759 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1761 ldi->imported_from = getStringCopy(parent->imported_from);
1762 ldi->imported_by = getStringCopy(parent->imported_by);
1764 ldi->graphics_set = NULL;
1765 ldi->sounds_set = NULL;
1766 ldi->music_set = NULL;
1767 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1768 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1769 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1771 ldi->level_filename = NULL;
1772 ldi->level_filetype = NULL;
1775 ldi->first_level = 0;
1776 ldi->last_level = 0;
1777 ldi->level_group = FALSE;
1778 ldi->handicap_level = 0;
1779 ldi->readonly = TRUE;
1780 ldi->handicap = TRUE;
1781 ldi->skip_levels = FALSE;
1785 static void freeTreeInfo(TreeInfo *ldi)
1787 checked_free(ldi->subdir);
1788 checked_free(ldi->fullpath);
1789 checked_free(ldi->basepath);
1790 checked_free(ldi->identifier);
1792 checked_free(ldi->name);
1793 checked_free(ldi->name_sorting);
1794 checked_free(ldi->author);
1796 checked_free(ldi->class_desc);
1798 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1800 checked_free(ldi->imported_from);
1801 checked_free(ldi->imported_by);
1803 checked_free(ldi->graphics_set);
1804 checked_free(ldi->sounds_set);
1805 checked_free(ldi->music_set);
1807 checked_free(ldi->graphics_path);
1808 checked_free(ldi->sounds_path);
1809 checked_free(ldi->music_path);
1811 checked_free(ldi->level_filename);
1812 checked_free(ldi->level_filetype);
1816 void setSetupInfo(struct TokenInfo *token_info,
1817 int token_nr, char *token_value)
1819 int token_type = token_info[token_nr].type;
1820 void *setup_value = token_info[token_nr].value;
1822 if (token_value == NULL)
1825 /* set setup field to corresponding token value */
1830 *(boolean *)setup_value = get_boolean_from_string(token_value);
1834 *(Key *)setup_value = getKeyFromKeyName(token_value);
1838 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1842 *(int *)setup_value = get_integer_from_string(token_value);
1846 checked_free(*(char **)setup_value);
1847 *(char **)setup_value = getStringCopy(token_value);
1855 static int compareTreeInfoEntries(const void *object1, const void *object2)
1857 const TreeInfo *entry1 = *((TreeInfo **)object1);
1858 const TreeInfo *entry2 = *((TreeInfo **)object2);
1859 int class_sorting1, class_sorting2;
1862 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1864 class_sorting1 = LEVELSORTING(entry1);
1865 class_sorting2 = LEVELSORTING(entry2);
1869 class_sorting1 = ARTWORKSORTING(entry1);
1870 class_sorting2 = ARTWORKSORTING(entry2);
1873 if (entry1->parent_link || entry2->parent_link)
1874 compare_result = (entry1->parent_link ? -1 : +1);
1875 else if (entry1->sort_priority == entry2->sort_priority)
1877 char *name1 = getStringToLower(entry1->name_sorting);
1878 char *name2 = getStringToLower(entry2->name_sorting);
1880 compare_result = strcmp(name1, name2);
1885 else if (class_sorting1 == class_sorting2)
1886 compare_result = entry1->sort_priority - entry2->sort_priority;
1888 compare_result = class_sorting1 - class_sorting2;
1890 return compare_result;
1893 static void createParentTreeInfoNode(TreeInfo *node_parent)
1897 if (node_parent == NULL)
1900 ti_new = newTreeInfo();
1901 setTreeInfoToDefaults(ti_new, node_parent->type);
1903 ti_new->node_parent = node_parent;
1904 ti_new->parent_link = TRUE;
1906 setString(&ti_new->identifier, node_parent->identifier);
1907 setString(&ti_new->name, ".. (parent directory)");
1908 setString(&ti_new->name_sorting, ti_new->name);
1910 setString(&ti_new->subdir, "..");
1911 setString(&ti_new->fullpath, node_parent->fullpath);
1913 ti_new->sort_priority = node_parent->sort_priority;
1914 ti_new->latest_engine = node_parent->latest_engine;
1916 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1918 pushTreeInfo(&node_parent->node_group, ti_new);
1921 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1922 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1924 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1925 TreeInfo *node_parent,
1926 char *level_directory,
1927 char *directory_name)
1929 char *directory_path = getPath2(level_directory, directory_name);
1930 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1931 SetupFileHash *setup_file_hash;
1932 LevelDirTree *leveldir_new = NULL;
1935 /* unless debugging, silently ignore directories without "levelinfo.conf" */
1936 if (!options.debug && !fileExists(filename))
1938 free(directory_path);
1944 setup_file_hash = loadSetupFileHash(filename);
1946 if (setup_file_hash == NULL)
1948 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1950 free(directory_path);
1956 leveldir_new = newTreeInfo();
1959 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1961 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1963 leveldir_new->subdir = getStringCopy(directory_name);
1965 checkSetupFileHashIdentifier(setup_file_hash, filename,
1966 getCookie("LEVELINFO"));
1968 /* set all structure fields according to the token/value pairs */
1969 ldi = *leveldir_new;
1970 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1971 setSetupInfo(levelinfo_tokens, i,
1972 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1973 *leveldir_new = ldi;
1975 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1976 setString(&leveldir_new->name, leveldir_new->subdir);
1978 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1980 if (leveldir_new->identifier == NULL)
1981 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
1983 if (leveldir_new->name_sorting == NULL)
1984 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1986 if (node_parent == NULL) /* top level group */
1988 leveldir_new->basepath = getStringCopy(level_directory);
1989 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
1991 else /* sub level group */
1993 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1994 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1998 if (leveldir_new->levels < 1)
1999 leveldir_new->levels = 1;
2002 leveldir_new->last_level =
2003 leveldir_new->first_level + leveldir_new->levels - 1;
2005 leveldir_new->in_user_dir =
2006 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2008 /* adjust some settings if user's private level directory was detected */
2009 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2010 leveldir_new->in_user_dir &&
2011 (strcmp(leveldir_new->subdir, getLoginName()) == 0 ||
2012 strcmp(leveldir_new->name, getLoginName()) == 0 ||
2013 strcmp(leveldir_new->author, getRealName()) == 0))
2015 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2016 leveldir_new->readonly = FALSE;
2019 leveldir_new->user_defined =
2020 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2022 leveldir_new->color = LEVELCOLOR(leveldir_new);
2024 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2026 leveldir_new->handicap_level = /* set handicap to default value */
2027 (leveldir_new->user_defined || !leveldir_new->handicap ?
2028 leveldir_new->last_level : leveldir_new->first_level);
2031 /* !!! don't skip sets without levels (else artwork base sets are missing) */
2033 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
2035 /* skip level sets without levels (which are probably artwork base sets) */
2037 freeSetupFileHash(setup_file_hash);
2038 free(directory_path);
2046 pushTreeInfo(node_first, leveldir_new);
2048 freeSetupFileHash(setup_file_hash);
2050 if (leveldir_new->level_group)
2052 /* create node to link back to current level directory */
2053 createParentTreeInfoNode(leveldir_new);
2055 /* step into sub-directory and look for more level series */
2056 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2057 leveldir_new, directory_path);
2060 free(directory_path);
2066 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2067 TreeInfo *node_parent,
2068 char *level_directory)
2071 struct dirent *dir_entry;
2072 boolean valid_entry_found = FALSE;
2074 if ((dir = opendir(level_directory)) == NULL)
2076 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2080 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2082 struct stat file_status;
2083 char *directory_name = dir_entry->d_name;
2084 char *directory_path = getPath2(level_directory, directory_name);
2086 /* skip entries for current and parent directory */
2087 if (strcmp(directory_name, ".") == 0 ||
2088 strcmp(directory_name, "..") == 0)
2090 free(directory_path);
2094 /* find out if directory entry is itself a directory */
2095 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2096 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2098 free(directory_path);
2102 free(directory_path);
2104 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2105 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2106 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2109 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2116 /* special case: top level directory may directly contain "levelinfo.conf" */
2117 if (node_parent == NULL && !valid_entry_found)
2119 /* check if this directory directly contains a file "levelinfo.conf" */
2120 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2121 level_directory, ".");
2124 if (!valid_entry_found)
2125 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2129 void LoadLevelInfo()
2131 InitUserLevelDirectory(getLoginName());
2133 DrawInitText("Loading level series:", 120, FC_GREEN);
2135 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2136 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2139 /* after loading all level set information, clone the level directory tree
2140 and remove all level sets without levels (these may still contain artwork
2141 to be offered in the setup menu as "custom artwork", and are therefore
2142 checked for existing artwork in the function "LoadLevelArtworkInfo()") */
2143 leveldir_first_all = leveldir_first;
2144 cloneTree(&leveldir_first, leveldir_first_all, TRUE);
2147 /* before sorting, the first entries will be from the user directory */
2148 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2150 if (leveldir_first == NULL)
2151 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2153 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2156 dumpTreeInfo(leveldir_first, 0);
2160 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2161 TreeInfo *node_parent,
2162 char *base_directory,
2163 char *directory_name, int type)
2165 char *directory_path = getPath2(base_directory, directory_name);
2166 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2167 SetupFileHash *setup_file_hash = NULL;
2168 TreeInfo *artwork_new = NULL;
2171 if (fileExists(filename))
2172 setup_file_hash = loadSetupFileHash(filename);
2174 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2177 struct dirent *dir_entry;
2178 boolean valid_file_found = FALSE;
2180 if ((dir = opendir(directory_path)) != NULL)
2182 while ((dir_entry = readdir(dir)) != NULL)
2184 char *entry_name = dir_entry->d_name;
2186 if (FileIsArtworkType(entry_name, type))
2188 valid_file_found = TRUE;
2196 if (!valid_file_found)
2198 if (strcmp(directory_name, ".") != 0)
2199 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2201 free(directory_path);
2208 artwork_new = newTreeInfo();
2211 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2213 setTreeInfoToDefaults(artwork_new, type);
2215 artwork_new->subdir = getStringCopy(directory_name);
2217 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2220 checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
2223 /* set all structure fields according to the token/value pairs */
2225 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2226 setSetupInfo(levelinfo_tokens, i,
2227 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2230 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2231 setString(&artwork_new->name, artwork_new->subdir);
2234 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2237 if (artwork_new->identifier == NULL)
2238 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2240 if (artwork_new->name_sorting == NULL)
2241 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2244 if (node_parent == NULL) /* top level group */
2246 artwork_new->basepath = getStringCopy(base_directory);
2247 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2249 else /* sub level group */
2251 artwork_new->basepath = getStringCopy(node_parent->basepath);
2252 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2255 artwork_new->in_user_dir =
2256 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2258 /* (may use ".sort_priority" from "setup_file_hash" above) */
2259 artwork_new->color = ARTWORKCOLOR(artwork_new);
2261 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2263 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2265 if (strcmp(artwork_new->subdir, ".") == 0)
2267 if (artwork_new->user_defined)
2269 setString(&artwork_new->identifier, "private");
2270 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2274 setString(&artwork_new->identifier, "classic");
2275 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2278 /* set to new values after changing ".sort_priority" */
2279 artwork_new->color = ARTWORKCOLOR(artwork_new);
2281 setString(&artwork_new->class_desc,
2282 getLevelClassDescription(artwork_new));
2286 setString(&artwork_new->identifier, artwork_new->subdir);
2289 setString(&artwork_new->name, artwork_new->identifier);
2290 setString(&artwork_new->name_sorting, artwork_new->name);
2293 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2295 pushTreeInfo(node_first, artwork_new);
2297 freeSetupFileHash(setup_file_hash);
2299 free(directory_path);
2305 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2306 TreeInfo *node_parent,
2307 char *base_directory, int type)
2310 struct dirent *dir_entry;
2311 boolean valid_entry_found = FALSE;
2313 if ((dir = opendir(base_directory)) == NULL)
2315 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2316 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2320 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2322 struct stat file_status;
2323 char *directory_name = dir_entry->d_name;
2324 char *directory_path = getPath2(base_directory, directory_name);
2326 /* skip entries for current and parent directory */
2327 if (strcmp(directory_name, ".") == 0 ||
2328 strcmp(directory_name, "..") == 0)
2330 free(directory_path);
2334 /* find out if directory entry is itself a directory */
2335 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2336 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2338 free(directory_path);
2342 free(directory_path);
2344 /* check if this directory contains artwork with or without config file */
2345 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2347 directory_name, type);
2352 /* check if this directory directly contains artwork itself */
2353 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2354 base_directory, ".",
2356 if (!valid_entry_found)
2357 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2361 static TreeInfo *getDummyArtworkInfo(int type)
2363 /* this is only needed when there is completely no artwork available */
2364 TreeInfo *artwork_new = newTreeInfo();
2366 setTreeInfoToDefaults(artwork_new, type);
2368 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2369 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2370 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2372 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2373 setString(&artwork_new->name, UNDEFINED_FILENAME);
2374 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2379 void LoadArtworkInfo()
2381 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2383 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2384 options.graphics_directory,
2385 TREE_TYPE_GRAPHICS_DIR);
2386 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2387 getUserGraphicsDir(),
2388 TREE_TYPE_GRAPHICS_DIR);
2390 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2391 options.sounds_directory,
2392 TREE_TYPE_SOUNDS_DIR);
2393 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2395 TREE_TYPE_SOUNDS_DIR);
2397 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2398 options.music_directory,
2399 TREE_TYPE_MUSIC_DIR);
2400 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2402 TREE_TYPE_MUSIC_DIR);
2404 if (artwork.gfx_first == NULL)
2405 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2406 if (artwork.snd_first == NULL)
2407 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2408 if (artwork.mus_first == NULL)
2409 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2411 /* before sorting, the first entries will be from the user directory */
2412 artwork.gfx_current =
2413 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2414 if (artwork.gfx_current == NULL)
2415 artwork.gfx_current =
2416 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2417 if (artwork.gfx_current == NULL)
2418 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2420 artwork.snd_current =
2421 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2422 if (artwork.snd_current == NULL)
2423 artwork.snd_current =
2424 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2425 if (artwork.snd_current == NULL)
2426 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2428 artwork.mus_current =
2429 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2430 if (artwork.mus_current == NULL)
2431 artwork.mus_current =
2432 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2433 if (artwork.mus_current == NULL)
2434 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2436 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2437 artwork.snd_current_identifier = artwork.snd_current->identifier;
2438 artwork.mus_current_identifier = artwork.mus_current->identifier;
2441 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2442 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2443 printf("music set == %s\n\n", artwork.mus_current_identifier);
2446 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2447 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2448 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2451 dumpTreeInfo(artwork.gfx_first, 0);
2452 dumpTreeInfo(artwork.snd_first, 0);
2453 dumpTreeInfo(artwork.mus_first, 0);
2457 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2458 LevelDirTree *level_node)
2460 /* recursively check all level directories for artwork sub-directories */
2464 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2465 ARTWORK_DIRECTORY((*artwork_node)->type));
2467 if (!level_node->parent_link)
2469 TreeInfo *topnode_last = *artwork_node;
2471 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2472 (*artwork_node)->type);
2474 if (topnode_last != *artwork_node)
2476 free((*artwork_node)->identifier);
2477 free((*artwork_node)->name);
2478 free((*artwork_node)->name_sorting);
2480 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2481 (*artwork_node)->name = getStringCopy(level_node->name);
2482 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2484 (*artwork_node)->sort_priority = level_node->sort_priority;
2485 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2491 if (level_node->node_group != NULL)
2492 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2494 level_node = level_node->next;
2498 void LoadLevelArtworkInfo()
2500 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2502 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2503 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2504 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2506 /* needed for reloading level artwork not known at ealier stage */
2508 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2510 artwork.gfx_current =
2511 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2512 if (artwork.gfx_current == NULL)
2513 artwork.gfx_current =
2514 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2515 if (artwork.gfx_current == NULL)
2516 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2519 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2521 artwork.snd_current =
2522 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2523 if (artwork.snd_current == NULL)
2524 artwork.snd_current =
2525 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2526 if (artwork.snd_current == NULL)
2527 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2530 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2532 artwork.mus_current =
2533 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2534 if (artwork.mus_current == NULL)
2535 artwork.mus_current =
2536 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2537 if (artwork.mus_current == NULL)
2538 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2541 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2542 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2543 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2546 dumpTreeInfo(artwork.gfx_first, 0);
2547 dumpTreeInfo(artwork.snd_first, 0);
2548 dumpTreeInfo(artwork.mus_first, 0);
2552 static void SaveUserLevelInfo()
2554 LevelDirTree *level_info;
2559 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2561 if (!(file = fopen(filename, MODE_WRITE)))
2563 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2568 level_info = newTreeInfo();
2570 /* always start with reliable default values */
2571 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2573 setString(&level_info->name, getLoginName());
2574 setString(&level_info->author, getRealName());
2575 level_info->levels = 100;
2576 level_info->first_level = 1;
2578 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2580 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2581 getCookie("LEVELINFO")));
2584 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2586 if (i == LEVELINFO_TOKEN_NAME ||
2587 i == LEVELINFO_TOKEN_AUTHOR ||
2588 i == LEVELINFO_TOKEN_LEVELS ||
2589 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2590 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2592 /* just to make things nicer :) */
2593 if (i == LEVELINFO_TOKEN_AUTHOR)
2594 fprintf(file, "\n");
2597 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2601 SetFilePermissions(filename, PERMS_PRIVATE);
2603 freeTreeInfo(level_info);
2607 char *getSetupValue(int type, void *value)
2609 static char value_string[MAX_LINE_LEN];
2617 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2621 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2625 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2629 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2633 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2637 sprintf(value_string, "%d", *(int *)value);
2641 strcpy(value_string, *(char **)value);
2645 value_string[0] = '\0';
2649 if (type & TYPE_GHOSTED)
2650 strcpy(value_string, "n/a");
2652 return value_string;
2655 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2659 static char token_string[MAX_LINE_LEN];
2660 int token_type = token_info[token_nr].type;
2661 void *setup_value = token_info[token_nr].value;
2662 char *token_text = token_info[token_nr].text;
2663 char *value_string = getSetupValue(token_type, setup_value);
2665 /* build complete token string */
2666 sprintf(token_string, "%s%s", prefix, token_text);
2668 /* build setup entry line */
2669 line = getFormattedSetupEntry(token_string, value_string);
2671 if (token_type == TYPE_KEY_X11)
2673 Key key = *(Key *)setup_value;
2674 char *keyname = getKeyNameFromKey(key);
2676 /* add comment, if useful */
2677 if (strcmp(keyname, "(undefined)") != 0 &&
2678 strcmp(keyname, "(unknown)") != 0)
2680 /* add at least one whitespace */
2682 for (i = strlen(line); i < token_comment_position; i++)
2686 strcat(line, keyname);
2693 void LoadLevelSetup_LastSeries()
2695 /* ----------------------------------------------------------------------- */
2696 /* ~/.<program>/levelsetup.conf */
2697 /* ----------------------------------------------------------------------- */
2699 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2700 SetupFileHash *level_setup_hash = NULL;
2702 /* always start with reliable default values */
2703 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2705 if ((level_setup_hash = loadSetupFileHash(filename)))
2707 char *last_level_series =
2708 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2710 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2712 if (leveldir_current == NULL)
2713 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2715 checkSetupFileHashIdentifier(level_setup_hash, filename,
2716 getCookie("LEVELSETUP"));
2718 freeSetupFileHash(level_setup_hash);
2721 Error(ERR_WARN, "using default setup values");
2726 void SaveLevelSetup_LastSeries()
2728 /* ----------------------------------------------------------------------- */
2729 /* ~/.<program>/levelsetup.conf */
2730 /* ----------------------------------------------------------------------- */
2732 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2733 char *level_subdir = leveldir_current->subdir;
2736 InitUserDataDirectory();
2738 if (!(file = fopen(filename, MODE_WRITE)))
2740 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2745 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2746 getCookie("LEVELSETUP")));
2747 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2752 SetFilePermissions(filename, PERMS_PRIVATE);
2757 static void checkSeriesInfo()
2759 static char *level_directory = NULL;
2761 struct dirent *dir_entry;
2763 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2765 level_directory = getPath2((leveldir_current->in_user_dir ?
2766 getUserLevelDir(NULL) :
2767 options.level_directory),
2768 leveldir_current->fullpath);
2770 if ((dir = opendir(level_directory)) == NULL)
2772 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2776 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2778 if (strlen(dir_entry->d_name) > 4 &&
2779 dir_entry->d_name[3] == '.' &&
2780 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2782 char levelnum_str[4];
2785 strncpy(levelnum_str, dir_entry->d_name, 3);
2786 levelnum_str[3] = '\0';
2788 levelnum_value = atoi(levelnum_str);
2791 if (levelnum_value < leveldir_current->first_level)
2793 Error(ERR_WARN, "additional level %d found", levelnum_value);
2794 leveldir_current->first_level = levelnum_value;
2796 else if (levelnum_value > leveldir_current->last_level)
2798 Error(ERR_WARN, "additional level %d found", levelnum_value);
2799 leveldir_current->last_level = levelnum_value;
2808 void LoadLevelSetup_SeriesInfo()
2811 SetupFileHash *level_setup_hash = NULL;
2812 char *level_subdir = leveldir_current->subdir;
2814 /* always start with reliable default values */
2815 level_nr = leveldir_current->first_level;
2817 checkSeriesInfo(leveldir_current);
2819 /* ----------------------------------------------------------------------- */
2820 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2821 /* ----------------------------------------------------------------------- */
2823 level_subdir = leveldir_current->subdir;
2825 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2827 if ((level_setup_hash = loadSetupFileHash(filename)))
2831 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2835 level_nr = atoi(token_value);
2837 if (level_nr < leveldir_current->first_level)
2838 level_nr = leveldir_current->first_level;
2839 if (level_nr > leveldir_current->last_level)
2840 level_nr = leveldir_current->last_level;
2843 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2847 int level_nr = atoi(token_value);
2849 if (level_nr < leveldir_current->first_level)
2850 level_nr = leveldir_current->first_level;
2851 if (level_nr > leveldir_current->last_level + 1)
2852 level_nr = leveldir_current->last_level;
2854 if (leveldir_current->user_defined || !leveldir_current->handicap)
2855 level_nr = leveldir_current->last_level;
2857 leveldir_current->handicap_level = level_nr;
2860 checkSetupFileHashIdentifier(level_setup_hash, filename,
2861 getCookie("LEVELSETUP"));
2863 freeSetupFileHash(level_setup_hash);
2866 Error(ERR_WARN, "using default setup values");
2871 void SaveLevelSetup_SeriesInfo()
2874 char *level_subdir = leveldir_current->subdir;
2875 char *level_nr_str = int2str(level_nr, 0);
2876 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2879 /* ----------------------------------------------------------------------- */
2880 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2881 /* ----------------------------------------------------------------------- */
2883 InitLevelSetupDirectory(level_subdir);
2885 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2887 if (!(file = fopen(filename, MODE_WRITE)))
2889 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2894 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2895 getCookie("LEVELSETUP")));
2896 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2898 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2899 handicap_level_str));
2903 SetFilePermissions(filename, PERMS_PRIVATE);