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,
1595 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1598 Error(ERR_WARN, "configuration file has no file identifier");
1599 else if (!checkCookieString(value, identifier))
1600 Error(ERR_WARN, "configuration file has wrong file identifier");
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, getCookie("LEVELINFO"));
1967 /* set all structure fields according to the token/value pairs */
1968 ldi = *leveldir_new;
1969 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1970 setSetupInfo(levelinfo_tokens, i,
1971 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1972 *leveldir_new = ldi;
1974 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1975 setString(&leveldir_new->name, leveldir_new->subdir);
1977 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1979 if (leveldir_new->identifier == NULL)
1980 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
1982 if (leveldir_new->name_sorting == NULL)
1983 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1985 if (node_parent == NULL) /* top level group */
1987 leveldir_new->basepath = getStringCopy(level_directory);
1988 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
1990 else /* sub level group */
1992 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1993 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1997 if (leveldir_new->levels < 1)
1998 leveldir_new->levels = 1;
2001 leveldir_new->last_level =
2002 leveldir_new->first_level + leveldir_new->levels - 1;
2004 leveldir_new->in_user_dir =
2005 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2007 /* adjust some settings if user's private level directory was detected */
2008 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2009 leveldir_new->in_user_dir &&
2010 (strcmp(leveldir_new->subdir, getLoginName()) == 0 ||
2011 strcmp(leveldir_new->name, getLoginName()) == 0 ||
2012 strcmp(leveldir_new->author, getRealName()) == 0))
2014 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2015 leveldir_new->readonly = FALSE;
2018 leveldir_new->user_defined =
2019 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2021 leveldir_new->color = LEVELCOLOR(leveldir_new);
2023 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2025 leveldir_new->handicap_level = /* set handicap to default value */
2026 (leveldir_new->user_defined || !leveldir_new->handicap ?
2027 leveldir_new->last_level : leveldir_new->first_level);
2030 /* !!! don't skip sets without levels (else artwork base sets are missing) */
2032 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
2034 /* skip level sets without levels (which are probably artwork base sets) */
2036 freeSetupFileHash(setup_file_hash);
2037 free(directory_path);
2045 pushTreeInfo(node_first, leveldir_new);
2047 freeSetupFileHash(setup_file_hash);
2049 if (leveldir_new->level_group)
2051 /* create node to link back to current level directory */
2052 createParentTreeInfoNode(leveldir_new);
2054 /* step into sub-directory and look for more level series */
2055 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2056 leveldir_new, directory_path);
2059 free(directory_path);
2065 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2066 TreeInfo *node_parent,
2067 char *level_directory)
2070 struct dirent *dir_entry;
2071 boolean valid_entry_found = FALSE;
2073 if ((dir = opendir(level_directory)) == NULL)
2075 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2079 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2081 struct stat file_status;
2082 char *directory_name = dir_entry->d_name;
2083 char *directory_path = getPath2(level_directory, directory_name);
2085 /* skip entries for current and parent directory */
2086 if (strcmp(directory_name, ".") == 0 ||
2087 strcmp(directory_name, "..") == 0)
2089 free(directory_path);
2093 /* find out if directory entry is itself a directory */
2094 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2095 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2097 free(directory_path);
2101 free(directory_path);
2103 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2104 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2105 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2108 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2115 if (!valid_entry_found)
2117 /* check if this directory directly contains a file "levelinfo.conf" */
2118 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2119 level_directory, ".");
2122 if (!valid_entry_found)
2123 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2127 void LoadLevelInfo()
2129 InitUserLevelDirectory(getLoginName());
2131 DrawInitText("Loading level series:", 120, FC_GREEN);
2133 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2134 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2137 leveldir_first_all = leveldir_first;
2138 cloneTree(&leveldir_first, leveldir_first_all, TRUE);
2141 /* before sorting, the first entries will be from the user directory */
2142 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2144 if (leveldir_first == NULL)
2145 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2147 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2150 dumpTreeInfo(leveldir_first, 0);
2154 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2155 TreeInfo *node_parent,
2156 char *base_directory,
2157 char *directory_name, int type)
2159 char *directory_path = getPath2(base_directory, directory_name);
2160 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2161 SetupFileHash *setup_file_hash = NULL;
2162 TreeInfo *artwork_new = NULL;
2165 if (fileExists(filename))
2166 setup_file_hash = loadSetupFileHash(filename);
2168 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2171 struct dirent *dir_entry;
2172 boolean valid_file_found = FALSE;
2174 if ((dir = opendir(directory_path)) != NULL)
2176 while ((dir_entry = readdir(dir)) != NULL)
2178 char *entry_name = dir_entry->d_name;
2180 if (FileIsArtworkType(entry_name, type))
2182 valid_file_found = TRUE;
2190 if (!valid_file_found)
2192 if (strcmp(directory_name, ".") != 0)
2193 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2195 free(directory_path);
2202 artwork_new = newTreeInfo();
2205 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2207 setTreeInfoToDefaults(artwork_new, type);
2209 artwork_new->subdir = getStringCopy(directory_name);
2211 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2214 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2217 /* set all structure fields according to the token/value pairs */
2219 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2220 setSetupInfo(levelinfo_tokens, i,
2221 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2224 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2225 setString(&artwork_new->name, artwork_new->subdir);
2228 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2231 if (artwork_new->identifier == NULL)
2232 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2234 if (artwork_new->name_sorting == NULL)
2235 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2238 if (node_parent == NULL) /* top level group */
2240 artwork_new->basepath = getStringCopy(base_directory);
2241 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2243 else /* sub level group */
2245 artwork_new->basepath = getStringCopy(node_parent->basepath);
2246 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2249 artwork_new->in_user_dir =
2250 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2252 /* (may use ".sort_priority" from "setup_file_hash" above) */
2253 artwork_new->color = ARTWORKCOLOR(artwork_new);
2255 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2257 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2259 if (strcmp(artwork_new->subdir, ".") == 0)
2261 if (artwork_new->user_defined)
2263 setString(&artwork_new->identifier, "private");
2264 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2268 setString(&artwork_new->identifier, "classic");
2269 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2272 /* set to new values after changing ".sort_priority" */
2273 artwork_new->color = ARTWORKCOLOR(artwork_new);
2275 setString(&artwork_new->class_desc,
2276 getLevelClassDescription(artwork_new));
2280 setString(&artwork_new->identifier, artwork_new->subdir);
2283 setString(&artwork_new->name, artwork_new->identifier);
2284 setString(&artwork_new->name_sorting, artwork_new->name);
2287 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2289 pushTreeInfo(node_first, artwork_new);
2291 freeSetupFileHash(setup_file_hash);
2293 free(directory_path);
2299 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2300 TreeInfo *node_parent,
2301 char *base_directory, int type)
2304 struct dirent *dir_entry;
2305 boolean valid_entry_found = FALSE;
2307 if ((dir = opendir(base_directory)) == NULL)
2309 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2310 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2314 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2316 struct stat file_status;
2317 char *directory_name = dir_entry->d_name;
2318 char *directory_path = getPath2(base_directory, directory_name);
2320 /* skip entries for current and parent directory */
2321 if (strcmp(directory_name, ".") == 0 ||
2322 strcmp(directory_name, "..") == 0)
2324 free(directory_path);
2328 /* find out if directory entry is itself a directory */
2329 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2330 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2332 free(directory_path);
2336 free(directory_path);
2338 /* check if this directory contains artwork with or without config file */
2339 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2341 directory_name, type);
2346 /* check if this directory directly contains artwork itself */
2347 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2348 base_directory, ".",
2350 if (!valid_entry_found)
2351 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2355 static TreeInfo *getDummyArtworkInfo(int type)
2357 /* this is only needed when there is completely no artwork available */
2358 TreeInfo *artwork_new = newTreeInfo();
2360 setTreeInfoToDefaults(artwork_new, type);
2362 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2363 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2364 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2366 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2367 setString(&artwork_new->name, UNDEFINED_FILENAME);
2368 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2373 void LoadArtworkInfo()
2375 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2377 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2378 options.graphics_directory,
2379 TREE_TYPE_GRAPHICS_DIR);
2380 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2381 getUserGraphicsDir(),
2382 TREE_TYPE_GRAPHICS_DIR);
2384 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2385 options.sounds_directory,
2386 TREE_TYPE_SOUNDS_DIR);
2387 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2389 TREE_TYPE_SOUNDS_DIR);
2391 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2392 options.music_directory,
2393 TREE_TYPE_MUSIC_DIR);
2394 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2396 TREE_TYPE_MUSIC_DIR);
2398 if (artwork.gfx_first == NULL)
2399 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2400 if (artwork.snd_first == NULL)
2401 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2402 if (artwork.mus_first == NULL)
2403 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2405 /* before sorting, the first entries will be from the user directory */
2406 artwork.gfx_current =
2407 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2408 if (artwork.gfx_current == NULL)
2409 artwork.gfx_current =
2410 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2411 if (artwork.gfx_current == NULL)
2412 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2414 artwork.snd_current =
2415 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2416 if (artwork.snd_current == NULL)
2417 artwork.snd_current =
2418 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2419 if (artwork.snd_current == NULL)
2420 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2422 artwork.mus_current =
2423 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2424 if (artwork.mus_current == NULL)
2425 artwork.mus_current =
2426 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2427 if (artwork.mus_current == NULL)
2428 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2430 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2431 artwork.snd_current_identifier = artwork.snd_current->identifier;
2432 artwork.mus_current_identifier = artwork.mus_current->identifier;
2435 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2436 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2437 printf("music set == %s\n\n", artwork.mus_current_identifier);
2440 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2441 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2442 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2445 dumpTreeInfo(artwork.gfx_first, 0);
2446 dumpTreeInfo(artwork.snd_first, 0);
2447 dumpTreeInfo(artwork.mus_first, 0);
2451 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2452 LevelDirTree *level_node)
2454 /* recursively check all level directories for artwork sub-directories */
2458 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2459 ARTWORK_DIRECTORY((*artwork_node)->type));
2461 if (!level_node->parent_link)
2463 TreeInfo *topnode_last = *artwork_node;
2465 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2466 (*artwork_node)->type);
2468 if (topnode_last != *artwork_node)
2470 free((*artwork_node)->identifier);
2471 free((*artwork_node)->name);
2472 free((*artwork_node)->name_sorting);
2474 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2475 (*artwork_node)->name = getStringCopy(level_node->name);
2476 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2478 (*artwork_node)->sort_priority = level_node->sort_priority;
2479 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2485 if (level_node->node_group != NULL)
2486 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2488 level_node = level_node->next;
2492 void LoadLevelArtworkInfo()
2494 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2496 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2497 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2498 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2500 /* needed for reloading level artwork not known at ealier stage */
2502 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2504 artwork.gfx_current =
2505 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2506 if (artwork.gfx_current == NULL)
2507 artwork.gfx_current =
2508 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2509 if (artwork.gfx_current == NULL)
2510 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2513 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2515 artwork.snd_current =
2516 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2517 if (artwork.snd_current == NULL)
2518 artwork.snd_current =
2519 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2520 if (artwork.snd_current == NULL)
2521 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2524 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2526 artwork.mus_current =
2527 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2528 if (artwork.mus_current == NULL)
2529 artwork.mus_current =
2530 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2531 if (artwork.mus_current == NULL)
2532 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2535 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2536 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2537 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2540 dumpTreeInfo(artwork.gfx_first, 0);
2541 dumpTreeInfo(artwork.snd_first, 0);
2542 dumpTreeInfo(artwork.mus_first, 0);
2546 static void SaveUserLevelInfo()
2548 LevelDirTree *level_info;
2553 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2555 if (!(file = fopen(filename, MODE_WRITE)))
2557 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2562 level_info = newTreeInfo();
2564 /* always start with reliable default values */
2565 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2567 setString(&level_info->name, getLoginName());
2568 setString(&level_info->author, getRealName());
2569 level_info->levels = 100;
2570 level_info->first_level = 1;
2572 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2574 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2575 getCookie("LEVELINFO")));
2578 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2580 if (i == LEVELINFO_TOKEN_NAME ||
2581 i == LEVELINFO_TOKEN_AUTHOR ||
2582 i == LEVELINFO_TOKEN_LEVELS ||
2583 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2584 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2586 /* just to make things nicer :) */
2587 if (i == LEVELINFO_TOKEN_AUTHOR)
2588 fprintf(file, "\n");
2591 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2595 SetFilePermissions(filename, PERMS_PRIVATE);
2597 freeTreeInfo(level_info);
2601 char *getSetupValue(int type, void *value)
2603 static char value_string[MAX_LINE_LEN];
2611 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2615 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2619 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2623 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2627 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2631 sprintf(value_string, "%d", *(int *)value);
2635 strcpy(value_string, *(char **)value);
2639 value_string[0] = '\0';
2643 return value_string;
2646 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2650 static char token_string[MAX_LINE_LEN];
2651 int token_type = token_info[token_nr].type;
2652 void *setup_value = token_info[token_nr].value;
2653 char *token_text = token_info[token_nr].text;
2654 char *value_string = getSetupValue(token_type, setup_value);
2656 /* build complete token string */
2657 sprintf(token_string, "%s%s", prefix, token_text);
2659 /* build setup entry line */
2660 line = getFormattedSetupEntry(token_string, value_string);
2662 if (token_type == TYPE_KEY_X11)
2664 Key key = *(Key *)setup_value;
2665 char *keyname = getKeyNameFromKey(key);
2667 /* add comment, if useful */
2668 if (strcmp(keyname, "(undefined)") != 0 &&
2669 strcmp(keyname, "(unknown)") != 0)
2671 /* add at least one whitespace */
2673 for (i = strlen(line); i < token_comment_position; i++)
2677 strcat(line, keyname);
2684 void LoadLevelSetup_LastSeries()
2686 /* ----------------------------------------------------------------------- */
2687 /* ~/.<program>/levelsetup.conf */
2688 /* ----------------------------------------------------------------------- */
2690 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2691 SetupFileHash *level_setup_hash = NULL;
2693 /* always start with reliable default values */
2694 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2696 if ((level_setup_hash = loadSetupFileHash(filename)))
2698 char *last_level_series =
2699 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2701 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2703 if (leveldir_current == NULL)
2704 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2706 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2708 freeSetupFileHash(level_setup_hash);
2711 Error(ERR_WARN, "using default setup values");
2716 void SaveLevelSetup_LastSeries()
2718 /* ----------------------------------------------------------------------- */
2719 /* ~/.<program>/levelsetup.conf */
2720 /* ----------------------------------------------------------------------- */
2722 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2723 char *level_subdir = leveldir_current->subdir;
2726 InitUserDataDirectory();
2728 if (!(file = fopen(filename, MODE_WRITE)))
2730 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2735 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2736 getCookie("LEVELSETUP")));
2737 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2742 SetFilePermissions(filename, PERMS_PRIVATE);
2747 static void checkSeriesInfo()
2749 static char *level_directory = NULL;
2751 struct dirent *dir_entry;
2753 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2755 level_directory = getPath2((leveldir_current->in_user_dir ?
2756 getUserLevelDir(NULL) :
2757 options.level_directory),
2758 leveldir_current->fullpath);
2760 if ((dir = opendir(level_directory)) == NULL)
2762 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2766 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2768 if (strlen(dir_entry->d_name) > 4 &&
2769 dir_entry->d_name[3] == '.' &&
2770 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2772 char levelnum_str[4];
2775 strncpy(levelnum_str, dir_entry->d_name, 3);
2776 levelnum_str[3] = '\0';
2778 levelnum_value = atoi(levelnum_str);
2781 if (levelnum_value < leveldir_current->first_level)
2783 Error(ERR_WARN, "additional level %d found", levelnum_value);
2784 leveldir_current->first_level = levelnum_value;
2786 else if (levelnum_value > leveldir_current->last_level)
2788 Error(ERR_WARN, "additional level %d found", levelnum_value);
2789 leveldir_current->last_level = levelnum_value;
2798 void LoadLevelSetup_SeriesInfo()
2801 SetupFileHash *level_setup_hash = NULL;
2802 char *level_subdir = leveldir_current->subdir;
2804 /* always start with reliable default values */
2805 level_nr = leveldir_current->first_level;
2807 checkSeriesInfo(leveldir_current);
2809 /* ----------------------------------------------------------------------- */
2810 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2811 /* ----------------------------------------------------------------------- */
2813 level_subdir = leveldir_current->subdir;
2815 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2817 if ((level_setup_hash = loadSetupFileHash(filename)))
2821 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2825 level_nr = atoi(token_value);
2827 if (level_nr < leveldir_current->first_level)
2828 level_nr = leveldir_current->first_level;
2829 if (level_nr > leveldir_current->last_level)
2830 level_nr = leveldir_current->last_level;
2833 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2837 int level_nr = atoi(token_value);
2839 if (level_nr < leveldir_current->first_level)
2840 level_nr = leveldir_current->first_level;
2841 if (level_nr > leveldir_current->last_level + 1)
2842 level_nr = leveldir_current->last_level;
2844 if (leveldir_current->user_defined || !leveldir_current->handicap)
2845 level_nr = leveldir_current->last_level;
2847 leveldir_current->handicap_level = level_nr;
2850 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2852 freeSetupFileHash(level_setup_hash);
2855 Error(ERR_WARN, "using default setup values");
2860 void SaveLevelSetup_SeriesInfo()
2863 char *level_subdir = leveldir_current->subdir;
2864 char *level_nr_str = int2str(level_nr, 0);
2865 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2868 /* ----------------------------------------------------------------------- */
2869 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2870 /* ----------------------------------------------------------------------- */
2872 InitLevelSetupDirectory(level_subdir);
2874 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2876 if (!(file = fopen(filename, MODE_WRITE)))
2878 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2883 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2884 getCookie("LEVELSETUP")));
2885 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2887 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2888 handicap_level_str));
2892 SetFilePermissions(filename, PERMS_PRIVATE);