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 if (!valid_entry_found)
2118 /* check if this directory directly contains a file "levelinfo.conf" */
2119 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2120 level_directory, ".");
2123 if (!valid_entry_found)
2124 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2128 void LoadLevelInfo()
2130 InitUserLevelDirectory(getLoginName());
2132 DrawInitText("Loading level series:", 120, FC_GREEN);
2134 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2135 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2138 /* after loading all level set information, clone the level directory tree
2139 and remove all level sets without levels (these may still contain artwork
2140 to be offered in the setup menu as "custom artwork", and are therefore
2141 checked for existing artwork in the function "LoadLevelArtworkInfo()") */
2142 leveldir_first_all = leveldir_first;
2143 cloneTree(&leveldir_first, leveldir_first_all, TRUE);
2146 /* before sorting, the first entries will be from the user directory */
2147 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2149 if (leveldir_first == NULL)
2150 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2152 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2155 dumpTreeInfo(leveldir_first, 0);
2159 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2160 TreeInfo *node_parent,
2161 char *base_directory,
2162 char *directory_name, int type)
2164 char *directory_path = getPath2(base_directory, directory_name);
2165 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2166 SetupFileHash *setup_file_hash = NULL;
2167 TreeInfo *artwork_new = NULL;
2170 if (fileExists(filename))
2171 setup_file_hash = loadSetupFileHash(filename);
2173 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2176 struct dirent *dir_entry;
2177 boolean valid_file_found = FALSE;
2179 if ((dir = opendir(directory_path)) != NULL)
2181 while ((dir_entry = readdir(dir)) != NULL)
2183 char *entry_name = dir_entry->d_name;
2185 if (FileIsArtworkType(entry_name, type))
2187 valid_file_found = TRUE;
2195 if (!valid_file_found)
2197 if (strcmp(directory_name, ".") != 0)
2198 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2200 free(directory_path);
2207 artwork_new = newTreeInfo();
2210 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2212 setTreeInfoToDefaults(artwork_new, type);
2214 artwork_new->subdir = getStringCopy(directory_name);
2216 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2219 checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
2222 /* set all structure fields according to the token/value pairs */
2224 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2225 setSetupInfo(levelinfo_tokens, i,
2226 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2229 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2230 setString(&artwork_new->name, artwork_new->subdir);
2233 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2236 if (artwork_new->identifier == NULL)
2237 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2239 if (artwork_new->name_sorting == NULL)
2240 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2243 if (node_parent == NULL) /* top level group */
2245 artwork_new->basepath = getStringCopy(base_directory);
2246 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2248 else /* sub level group */
2250 artwork_new->basepath = getStringCopy(node_parent->basepath);
2251 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2254 artwork_new->in_user_dir =
2255 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2257 /* (may use ".sort_priority" from "setup_file_hash" above) */
2258 artwork_new->color = ARTWORKCOLOR(artwork_new);
2260 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2262 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2264 if (strcmp(artwork_new->subdir, ".") == 0)
2266 if (artwork_new->user_defined)
2268 setString(&artwork_new->identifier, "private");
2269 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2273 setString(&artwork_new->identifier, "classic");
2274 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2277 /* set to new values after changing ".sort_priority" */
2278 artwork_new->color = ARTWORKCOLOR(artwork_new);
2280 setString(&artwork_new->class_desc,
2281 getLevelClassDescription(artwork_new));
2285 setString(&artwork_new->identifier, artwork_new->subdir);
2288 setString(&artwork_new->name, artwork_new->identifier);
2289 setString(&artwork_new->name_sorting, artwork_new->name);
2292 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2294 pushTreeInfo(node_first, artwork_new);
2296 freeSetupFileHash(setup_file_hash);
2298 free(directory_path);
2304 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2305 TreeInfo *node_parent,
2306 char *base_directory, int type)
2309 struct dirent *dir_entry;
2310 boolean valid_entry_found = FALSE;
2312 if ((dir = opendir(base_directory)) == NULL)
2314 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2315 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2319 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2321 struct stat file_status;
2322 char *directory_name = dir_entry->d_name;
2323 char *directory_path = getPath2(base_directory, directory_name);
2325 /* skip entries for current and parent directory */
2326 if (strcmp(directory_name, ".") == 0 ||
2327 strcmp(directory_name, "..") == 0)
2329 free(directory_path);
2333 /* find out if directory entry is itself a directory */
2334 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2335 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2337 free(directory_path);
2341 free(directory_path);
2343 /* check if this directory contains artwork with or without config file */
2344 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2346 directory_name, type);
2351 /* check if this directory directly contains artwork itself */
2352 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2353 base_directory, ".",
2355 if (!valid_entry_found)
2356 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2360 static TreeInfo *getDummyArtworkInfo(int type)
2362 /* this is only needed when there is completely no artwork available */
2363 TreeInfo *artwork_new = newTreeInfo();
2365 setTreeInfoToDefaults(artwork_new, type);
2367 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2368 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2369 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2371 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2372 setString(&artwork_new->name, UNDEFINED_FILENAME);
2373 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2378 void LoadArtworkInfo()
2380 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2382 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2383 options.graphics_directory,
2384 TREE_TYPE_GRAPHICS_DIR);
2385 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2386 getUserGraphicsDir(),
2387 TREE_TYPE_GRAPHICS_DIR);
2389 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2390 options.sounds_directory,
2391 TREE_TYPE_SOUNDS_DIR);
2392 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2394 TREE_TYPE_SOUNDS_DIR);
2396 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2397 options.music_directory,
2398 TREE_TYPE_MUSIC_DIR);
2399 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2401 TREE_TYPE_MUSIC_DIR);
2403 if (artwork.gfx_first == NULL)
2404 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2405 if (artwork.snd_first == NULL)
2406 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2407 if (artwork.mus_first == NULL)
2408 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2410 /* before sorting, the first entries will be from the user directory */
2411 artwork.gfx_current =
2412 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2413 if (artwork.gfx_current == NULL)
2414 artwork.gfx_current =
2415 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2416 if (artwork.gfx_current == NULL)
2417 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2419 artwork.snd_current =
2420 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2421 if (artwork.snd_current == NULL)
2422 artwork.snd_current =
2423 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2424 if (artwork.snd_current == NULL)
2425 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2427 artwork.mus_current =
2428 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2429 if (artwork.mus_current == NULL)
2430 artwork.mus_current =
2431 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2432 if (artwork.mus_current == NULL)
2433 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2435 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2436 artwork.snd_current_identifier = artwork.snd_current->identifier;
2437 artwork.mus_current_identifier = artwork.mus_current->identifier;
2440 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2441 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2442 printf("music set == %s\n\n", artwork.mus_current_identifier);
2445 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2446 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2447 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2450 dumpTreeInfo(artwork.gfx_first, 0);
2451 dumpTreeInfo(artwork.snd_first, 0);
2452 dumpTreeInfo(artwork.mus_first, 0);
2456 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2457 LevelDirTree *level_node)
2459 /* recursively check all level directories for artwork sub-directories */
2463 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2464 ARTWORK_DIRECTORY((*artwork_node)->type));
2466 if (!level_node->parent_link)
2468 TreeInfo *topnode_last = *artwork_node;
2470 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2471 (*artwork_node)->type);
2473 if (topnode_last != *artwork_node)
2475 free((*artwork_node)->identifier);
2476 free((*artwork_node)->name);
2477 free((*artwork_node)->name_sorting);
2479 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2480 (*artwork_node)->name = getStringCopy(level_node->name);
2481 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2483 (*artwork_node)->sort_priority = level_node->sort_priority;
2484 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2490 if (level_node->node_group != NULL)
2491 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2493 level_node = level_node->next;
2497 void LoadLevelArtworkInfo()
2499 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2501 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2502 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2503 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2505 /* needed for reloading level artwork not known at ealier stage */
2507 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2509 artwork.gfx_current =
2510 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2511 if (artwork.gfx_current == NULL)
2512 artwork.gfx_current =
2513 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2514 if (artwork.gfx_current == NULL)
2515 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2518 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2520 artwork.snd_current =
2521 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2522 if (artwork.snd_current == NULL)
2523 artwork.snd_current =
2524 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2525 if (artwork.snd_current == NULL)
2526 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2529 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2531 artwork.mus_current =
2532 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2533 if (artwork.mus_current == NULL)
2534 artwork.mus_current =
2535 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2536 if (artwork.mus_current == NULL)
2537 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2540 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2541 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2542 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2545 dumpTreeInfo(artwork.gfx_first, 0);
2546 dumpTreeInfo(artwork.snd_first, 0);
2547 dumpTreeInfo(artwork.mus_first, 0);
2551 static void SaveUserLevelInfo()
2553 LevelDirTree *level_info;
2558 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2560 if (!(file = fopen(filename, MODE_WRITE)))
2562 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2567 level_info = newTreeInfo();
2569 /* always start with reliable default values */
2570 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2572 setString(&level_info->name, getLoginName());
2573 setString(&level_info->author, getRealName());
2574 level_info->levels = 100;
2575 level_info->first_level = 1;
2577 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2579 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2580 getCookie("LEVELINFO")));
2583 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2585 if (i == LEVELINFO_TOKEN_NAME ||
2586 i == LEVELINFO_TOKEN_AUTHOR ||
2587 i == LEVELINFO_TOKEN_LEVELS ||
2588 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2589 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2591 /* just to make things nicer :) */
2592 if (i == LEVELINFO_TOKEN_AUTHOR)
2593 fprintf(file, "\n");
2596 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2600 SetFilePermissions(filename, PERMS_PRIVATE);
2602 freeTreeInfo(level_info);
2606 char *getSetupValue(int type, void *value)
2608 static char value_string[MAX_LINE_LEN];
2616 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2620 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2624 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2628 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2632 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2636 sprintf(value_string, "%d", *(int *)value);
2640 strcpy(value_string, *(char **)value);
2644 value_string[0] = '\0';
2648 return value_string;
2651 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2655 static char token_string[MAX_LINE_LEN];
2656 int token_type = token_info[token_nr].type;
2657 void *setup_value = token_info[token_nr].value;
2658 char *token_text = token_info[token_nr].text;
2659 char *value_string = getSetupValue(token_type, setup_value);
2661 /* build complete token string */
2662 sprintf(token_string, "%s%s", prefix, token_text);
2664 /* build setup entry line */
2665 line = getFormattedSetupEntry(token_string, value_string);
2667 if (token_type == TYPE_KEY_X11)
2669 Key key = *(Key *)setup_value;
2670 char *keyname = getKeyNameFromKey(key);
2672 /* add comment, if useful */
2673 if (strcmp(keyname, "(undefined)") != 0 &&
2674 strcmp(keyname, "(unknown)") != 0)
2676 /* add at least one whitespace */
2678 for (i = strlen(line); i < token_comment_position; i++)
2682 strcat(line, keyname);
2689 void LoadLevelSetup_LastSeries()
2691 /* ----------------------------------------------------------------------- */
2692 /* ~/.<program>/levelsetup.conf */
2693 /* ----------------------------------------------------------------------- */
2695 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2696 SetupFileHash *level_setup_hash = NULL;
2698 /* always start with reliable default values */
2699 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2701 if ((level_setup_hash = loadSetupFileHash(filename)))
2703 char *last_level_series =
2704 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2706 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2708 if (leveldir_current == NULL)
2709 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2711 checkSetupFileHashIdentifier(level_setup_hash, filename,
2712 getCookie("LEVELSETUP"));
2714 freeSetupFileHash(level_setup_hash);
2717 Error(ERR_WARN, "using default setup values");
2722 void SaveLevelSetup_LastSeries()
2724 /* ----------------------------------------------------------------------- */
2725 /* ~/.<program>/levelsetup.conf */
2726 /* ----------------------------------------------------------------------- */
2728 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2729 char *level_subdir = leveldir_current->subdir;
2732 InitUserDataDirectory();
2734 if (!(file = fopen(filename, MODE_WRITE)))
2736 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2741 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2742 getCookie("LEVELSETUP")));
2743 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2748 SetFilePermissions(filename, PERMS_PRIVATE);
2753 static void checkSeriesInfo()
2755 static char *level_directory = NULL;
2757 struct dirent *dir_entry;
2759 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2761 level_directory = getPath2((leveldir_current->in_user_dir ?
2762 getUserLevelDir(NULL) :
2763 options.level_directory),
2764 leveldir_current->fullpath);
2766 if ((dir = opendir(level_directory)) == NULL)
2768 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2772 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2774 if (strlen(dir_entry->d_name) > 4 &&
2775 dir_entry->d_name[3] == '.' &&
2776 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2778 char levelnum_str[4];
2781 strncpy(levelnum_str, dir_entry->d_name, 3);
2782 levelnum_str[3] = '\0';
2784 levelnum_value = atoi(levelnum_str);
2787 if (levelnum_value < leveldir_current->first_level)
2789 Error(ERR_WARN, "additional level %d found", levelnum_value);
2790 leveldir_current->first_level = levelnum_value;
2792 else if (levelnum_value > leveldir_current->last_level)
2794 Error(ERR_WARN, "additional level %d found", levelnum_value);
2795 leveldir_current->last_level = levelnum_value;
2804 void LoadLevelSetup_SeriesInfo()
2807 SetupFileHash *level_setup_hash = NULL;
2808 char *level_subdir = leveldir_current->subdir;
2810 /* always start with reliable default values */
2811 level_nr = leveldir_current->first_level;
2813 checkSeriesInfo(leveldir_current);
2815 /* ----------------------------------------------------------------------- */
2816 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2817 /* ----------------------------------------------------------------------- */
2819 level_subdir = leveldir_current->subdir;
2821 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2823 if ((level_setup_hash = loadSetupFileHash(filename)))
2827 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2831 level_nr = atoi(token_value);
2833 if (level_nr < leveldir_current->first_level)
2834 level_nr = leveldir_current->first_level;
2835 if (level_nr > leveldir_current->last_level)
2836 level_nr = leveldir_current->last_level;
2839 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2843 int level_nr = atoi(token_value);
2845 if (level_nr < leveldir_current->first_level)
2846 level_nr = leveldir_current->first_level;
2847 if (level_nr > leveldir_current->last_level + 1)
2848 level_nr = leveldir_current->last_level;
2850 if (leveldir_current->user_defined || !leveldir_current->handicap)
2851 level_nr = leveldir_current->last_level;
2853 leveldir_current->handicap_level = level_nr;
2856 checkSetupFileHashIdentifier(level_setup_hash, filename,
2857 getCookie("LEVELSETUP"));
2859 freeSetupFileHash(level_setup_hash);
2862 Error(ERR_WARN, "using default setup values");
2867 void SaveLevelSetup_SeriesInfo()
2870 char *level_subdir = leveldir_current->subdir;
2871 char *level_nr_str = int2str(level_nr, 0);
2872 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2875 /* ----------------------------------------------------------------------- */
2876 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2877 /* ----------------------------------------------------------------------- */
2879 InitLevelSetupDirectory(level_subdir);
2881 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2883 if (!(file = fopen(filename, MODE_WRITE)))
2885 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2890 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2891 getCookie("LEVELSETUP")));
2892 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2894 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2895 handicap_level_str));
2899 SetFilePermissions(filename, PERMS_PRIVATE);