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 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 void dumpTreeInfo(TreeInfo *node, int depth)
934 printf("Dumping TreeInfo:\n");
938 for (i = 0; i < (depth + 1) * 3; i++)
942 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
943 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
945 printf("subdir == '%s' (%s) [%s] (%d)\n",
946 node->subdir, node->name, node->identifier, node->sort_priority);
949 if (node->node_group != NULL)
950 dumpTreeInfo(node->node_group, depth + 1);
956 void sortTreeInfo(TreeInfo **node_first,
957 int (*compare_function)(const void *, const void *))
959 int num_nodes = numTreeInfo(*node_first);
960 TreeInfo **sort_array;
961 TreeInfo *node = *node_first;
967 /* allocate array for sorting structure pointers */
968 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
970 /* writing structure pointers to sorting array */
971 while (i < num_nodes && node) /* double boundary check... */
973 sort_array[i] = node;
979 /* sorting the structure pointers in the sorting array */
980 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
983 /* update the linkage of list elements with the sorted node array */
984 for (i = 0; i < num_nodes - 1; i++)
985 sort_array[i]->next = sort_array[i + 1];
986 sort_array[num_nodes - 1]->next = NULL;
988 /* update the linkage of the main list anchor pointer */
989 *node_first = sort_array[0];
993 /* now recursively sort the level group structures */
997 if (node->node_group != NULL)
998 sortTreeInfo(&node->node_group, compare_function);
1005 /* ========================================================================= */
1006 /* some stuff from "files.c" */
1007 /* ========================================================================= */
1009 #if defined(PLATFORM_WIN32)
1011 #define S_IRGRP S_IRUSR
1014 #define S_IROTH S_IRUSR
1017 #define S_IWGRP S_IWUSR
1020 #define S_IWOTH S_IWUSR
1023 #define S_IXGRP S_IXUSR
1026 #define S_IXOTH S_IXUSR
1029 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1034 #endif /* PLATFORM_WIN32 */
1036 /* file permissions for newly written files */
1037 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1038 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1039 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1041 #define MODE_W_PRIVATE (S_IWUSR)
1042 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1043 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1045 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1046 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1048 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1049 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1051 char *getUserDataDir(void)
1053 static char *userdata_dir = NULL;
1055 if (userdata_dir == NULL)
1056 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1058 return userdata_dir;
1061 char *getCommonDataDir(void)
1063 static char *common_data_dir = NULL;
1065 #if defined(PLATFORM_WIN32)
1066 if (common_data_dir == NULL)
1068 char *dir = checked_malloc(MAX_PATH + 1);
1070 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1071 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1072 common_data_dir = getPath2(dir, program.userdata_directory);
1074 common_data_dir = options.rw_base_directory;
1077 if (common_data_dir == NULL)
1078 common_data_dir = options.rw_base_directory;
1081 return common_data_dir;
1086 return getUserDataDir();
1089 static mode_t posix_umask(mode_t mask)
1091 #if defined(PLATFORM_UNIX)
1098 static int posix_mkdir(const char *pathname, mode_t mode)
1100 #if defined(PLATFORM_WIN32)
1101 return mkdir(pathname);
1103 return mkdir(pathname, mode);
1107 void createDirectory(char *dir, char *text, int permission_class)
1109 /* leave "other" permissions in umask untouched, but ensure group parts
1110 of USERDATA_DIR_MODE are not masked */
1111 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1112 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1113 mode_t normal_umask = posix_umask(0);
1114 mode_t group_umask = ~(dir_mode & S_IRWXG);
1115 posix_umask(normal_umask & group_umask);
1117 if (!fileExists(dir))
1118 if (posix_mkdir(dir, dir_mode) != 0)
1119 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1121 posix_umask(normal_umask); /* reset normal umask */
1124 void InitUserDataDirectory()
1126 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1129 void SetFilePermissions(char *filename, int permission_class)
1131 chmod(filename, (permission_class == PERMS_PRIVATE ?
1132 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1135 char *getCookie(char *file_type)
1137 static char cookie[MAX_COOKIE_LEN + 1];
1139 if (strlen(program.cookie_prefix) + 1 +
1140 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1141 return "[COOKIE ERROR]"; /* should never happen */
1143 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1144 program.cookie_prefix, file_type,
1145 program.version_major, program.version_minor);
1150 int getFileVersionFromCookieString(const char *cookie)
1152 const char *ptr_cookie1, *ptr_cookie2;
1153 const char *pattern1 = "_FILE_VERSION_";
1154 const char *pattern2 = "?.?";
1155 const int len_cookie = strlen(cookie);
1156 const int len_pattern1 = strlen(pattern1);
1157 const int len_pattern2 = strlen(pattern2);
1158 const int len_pattern = len_pattern1 + len_pattern2;
1159 int version_major, version_minor;
1161 if (len_cookie <= len_pattern)
1164 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1165 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1167 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1170 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1171 ptr_cookie2[1] != '.' ||
1172 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1175 version_major = ptr_cookie2[0] - '0';
1176 version_minor = ptr_cookie2[2] - '0';
1178 return VERSION_IDENT(version_major, version_minor, 0, 0);
1181 boolean checkCookieString(const char *cookie, const char *template)
1183 const char *pattern = "_FILE_VERSION_?.?";
1184 const int len_cookie = strlen(cookie);
1185 const int len_template = strlen(template);
1186 const int len_pattern = strlen(pattern);
1188 if (len_cookie != len_template)
1191 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1197 /* ------------------------------------------------------------------------- */
1198 /* setup file list and hash handling functions */
1199 /* ------------------------------------------------------------------------- */
1201 char *getFormattedSetupEntry(char *token, char *value)
1204 static char entry[MAX_LINE_LEN];
1206 /* if value is an empty string, just return token without value */
1210 /* start with the token and some spaces to format output line */
1211 sprintf(entry, "%s:", token);
1212 for (i = strlen(entry); i < token_value_position; i++)
1215 /* continue with the token's value */
1216 strcat(entry, value);
1221 SetupFileList *newSetupFileList(char *token, char *value)
1223 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1225 new->token = getStringCopy(token);
1226 new->value = getStringCopy(value);
1233 void freeSetupFileList(SetupFileList *list)
1238 checked_free(list->token);
1239 checked_free(list->value);
1242 freeSetupFileList(list->next);
1247 char *getListEntry(SetupFileList *list, char *token)
1252 if (strcmp(list->token, token) == 0)
1255 return getListEntry(list->next, token);
1258 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1263 if (strcmp(list->token, token) == 0)
1265 checked_free(list->value);
1267 list->value = getStringCopy(value);
1271 else if (list->next == NULL)
1272 return (list->next = newSetupFileList(token, value));
1274 return setListEntry(list->next, token, value);
1277 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1282 if (list->next == NULL)
1283 return (list->next = newSetupFileList(token, value));
1285 return addListEntry(list->next, token, value);
1289 static void printSetupFileList(SetupFileList *list)
1294 printf("token: '%s'\n", list->token);
1295 printf("value: '%s'\n", list->value);
1297 printSetupFileList(list->next);
1302 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1303 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1304 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1305 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1307 #define insert_hash_entry hashtable_insert
1308 #define search_hash_entry hashtable_search
1309 #define change_hash_entry hashtable_change
1310 #define remove_hash_entry hashtable_remove
1313 static unsigned int get_hash_from_key(void *key)
1318 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1319 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1320 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1321 it works better than many other constants, prime or not) has never been
1322 adequately explained.
1324 If you just want to have a good hash function, and cannot wait, djb2
1325 is one of the best string hash functions i know. It has excellent
1326 distribution and speed on many different sets of keys and table sizes.
1327 You are not likely to do better with one of the "well known" functions
1328 such as PJW, K&R, etc.
1330 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1333 char *str = (char *)key;
1334 unsigned int hash = 5381;
1337 while ((c = *str++))
1338 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1343 static int keys_are_equal(void *key1, void *key2)
1345 return (strcmp((char *)key1, (char *)key2) == 0);
1348 SetupFileHash *newSetupFileHash()
1350 SetupFileHash *new_hash =
1351 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1353 if (new_hash == NULL)
1354 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1359 void freeSetupFileHash(SetupFileHash *hash)
1364 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1367 char *getHashEntry(SetupFileHash *hash, char *token)
1372 return search_hash_entry(hash, token);
1375 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1382 value_copy = getStringCopy(value);
1384 /* change value; if it does not exist, insert it as new */
1385 if (!change_hash_entry(hash, token, value_copy))
1386 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1387 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1390 char *removeHashEntry(SetupFileHash *hash, char *token)
1395 return remove_hash_entry(hash, token);
1400 static void printSetupFileHash(SetupFileHash *hash)
1402 BEGIN_HASH_ITERATION(hash, itr)
1404 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1405 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1407 END_HASH_ITERATION(hash, itr)
1412 static void *loadSetupFileData(char *filename, boolean use_hash)
1414 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1415 char *token, *value, *line_ptr;
1416 void *setup_file_data, *insert_ptr = NULL;
1417 boolean read_continued_line = FALSE;
1420 if (!(file = fopen(filename, MODE_READ)))
1422 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1428 setup_file_data = newSetupFileHash();
1430 insert_ptr = setup_file_data = newSetupFileList("", "");
1434 /* read next line of input file */
1435 if (!fgets(line, MAX_LINE_LEN, file))
1438 /* cut trailing newline or carriage return */
1439 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1440 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1443 if (read_continued_line)
1445 /* cut leading whitespaces from input line */
1446 for (line_ptr = line; *line_ptr; line_ptr++)
1447 if (*line_ptr != ' ' && *line_ptr != '\t')
1450 /* append new line to existing line, if there is enough space */
1451 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1452 strcat(previous_line, line_ptr);
1454 strcpy(line, previous_line); /* copy storage buffer to line */
1456 read_continued_line = FALSE;
1459 /* if the last character is '\', continue at next line */
1460 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1462 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1463 strcpy(previous_line, line); /* copy line to storage buffer */
1465 read_continued_line = TRUE;
1470 /* cut trailing comment from input line */
1471 for (line_ptr = line; *line_ptr; line_ptr++)
1473 if (*line_ptr == '#')
1480 /* cut trailing whitespaces from input line */
1481 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1482 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1485 /* ignore empty lines */
1489 /* cut leading whitespaces from token */
1490 for (token = line; *token; token++)
1491 if (*token != ' ' && *token != '\t')
1494 /* start with empty value as reliable default */
1497 /* find end of token to determine start of value */
1498 for (line_ptr = token; *line_ptr; line_ptr++)
1500 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1502 *line_ptr = '\0'; /* terminate token string */
1503 value = line_ptr + 1; /* set beginning of value */
1509 /* cut leading whitespaces from value */
1510 for (; *value; value++)
1511 if (*value != ' ' && *value != '\t')
1516 value = "true"; /* treat tokens without value as "true" */
1522 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1524 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1532 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1533 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1537 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1538 SetupFileList *first_valid_list_entry = setup_file_list->next;
1540 /* free empty list header */
1541 setup_file_list->next = NULL;
1542 freeSetupFileList(setup_file_list);
1543 setup_file_data = first_valid_list_entry;
1545 if (first_valid_list_entry == NULL)
1546 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1549 return setup_file_data;
1552 SetupFileList *loadSetupFileList(char *filename)
1554 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1557 SetupFileHash *loadSetupFileHash(char *filename)
1559 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1562 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1565 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1568 Error(ERR_WARN, "configuration file has no file identifier");
1569 else if (!checkCookieString(value, identifier))
1570 Error(ERR_WARN, "configuration file has wrong file identifier");
1574 /* ========================================================================= */
1575 /* setup file stuff */
1576 /* ========================================================================= */
1578 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1579 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1580 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1582 /* level directory info */
1583 #define LEVELINFO_TOKEN_IDENTIFIER 0
1584 #define LEVELINFO_TOKEN_NAME 1
1585 #define LEVELINFO_TOKEN_NAME_SORTING 2
1586 #define LEVELINFO_TOKEN_AUTHOR 3
1587 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1588 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1589 #define LEVELINFO_TOKEN_LEVELS 6
1590 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1591 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1592 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1593 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1594 #define LEVELINFO_TOKEN_READONLY 11
1595 #define LEVELINFO_TOKEN_GRAPHICS_SET 12
1596 #define LEVELINFO_TOKEN_SOUNDS_SET 13
1597 #define LEVELINFO_TOKEN_MUSIC_SET 14
1598 #define LEVELINFO_TOKEN_FILENAME 15
1599 #define LEVELINFO_TOKEN_FILETYPE 16
1600 #define LEVELINFO_TOKEN_HANDICAP 17
1601 #define LEVELINFO_TOKEN_SKIP_LEVELS 18
1603 #define NUM_LEVELINFO_TOKENS 19
1605 static LevelDirTree ldi;
1607 static struct TokenInfo levelinfo_tokens[] =
1609 /* level directory info */
1610 { TYPE_STRING, &ldi.identifier, "identifier" },
1611 { TYPE_STRING, &ldi.name, "name" },
1612 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1613 { TYPE_STRING, &ldi.author, "author" },
1614 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1615 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1616 { TYPE_INTEGER, &ldi.levels, "levels" },
1617 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1618 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1619 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1620 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1621 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1622 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1623 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1624 { TYPE_STRING, &ldi.music_set, "music_set" },
1625 { TYPE_STRING, &ldi.level_filename, "filename" },
1626 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1627 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1628 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1631 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1635 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1636 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1637 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1638 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1641 ldi->node_parent = NULL;
1642 ldi->node_group = NULL;
1646 ldi->cl_cursor = -1;
1649 ldi->fullpath = NULL;
1650 ldi->basepath = NULL;
1651 ldi->identifier = NULL;
1652 ldi->name = getStringCopy(ANONYMOUS_NAME);
1653 ldi->name_sorting = NULL;
1654 ldi->author = getStringCopy(ANONYMOUS_NAME);
1656 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1657 ldi->latest_engine = FALSE; /* default: get from level */
1658 ldi->parent_link = FALSE;
1659 ldi->in_user_dir = FALSE;
1660 ldi->user_defined = FALSE;
1662 ldi->class_desc = NULL;
1664 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1666 ldi->imported_from = NULL;
1667 ldi->imported_by = NULL;
1669 ldi->graphics_set = NULL;
1670 ldi->sounds_set = NULL;
1671 ldi->music_set = NULL;
1672 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1673 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1674 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1676 ldi->level_filename = NULL;
1677 ldi->level_filetype = NULL;
1680 ldi->first_level = 0;
1681 ldi->last_level = 0;
1682 ldi->level_group = FALSE;
1683 ldi->handicap_level = 0;
1684 ldi->readonly = TRUE;
1685 ldi->handicap = TRUE;
1686 ldi->skip_levels = FALSE;
1690 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1694 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1696 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1702 /* copy all values from the parent structure */
1704 ldi->type = parent->type;
1706 ldi->node_top = parent->node_top;
1707 ldi->node_parent = parent;
1708 ldi->node_group = NULL;
1712 ldi->cl_cursor = -1;
1715 ldi->fullpath = NULL;
1716 ldi->basepath = NULL;
1717 ldi->identifier = NULL;
1718 ldi->name = getStringCopy(ANONYMOUS_NAME);
1719 ldi->name_sorting = NULL;
1720 ldi->author = getStringCopy(parent->author);
1722 ldi->sort_priority = parent->sort_priority;
1723 ldi->latest_engine = parent->latest_engine;
1724 ldi->parent_link = FALSE;
1725 ldi->in_user_dir = parent->in_user_dir;
1726 ldi->user_defined = parent->user_defined;
1727 ldi->color = parent->color;
1728 ldi->class_desc = getStringCopy(parent->class_desc);
1730 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1732 ldi->imported_from = getStringCopy(parent->imported_from);
1733 ldi->imported_by = getStringCopy(parent->imported_by);
1735 ldi->graphics_set = NULL;
1736 ldi->sounds_set = NULL;
1737 ldi->music_set = NULL;
1738 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1739 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1740 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1742 ldi->level_filename = NULL;
1743 ldi->level_filetype = NULL;
1746 ldi->first_level = 0;
1747 ldi->last_level = 0;
1748 ldi->level_group = FALSE;
1749 ldi->handicap_level = 0;
1750 ldi->readonly = TRUE;
1751 ldi->handicap = TRUE;
1752 ldi->skip_levels = FALSE;
1757 /* first copy all values from the parent structure ... */
1760 /* ... then set all fields to default that cannot be inherited from parent.
1761 This is especially important for all those fields that can be set from
1762 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1763 calls 'free()' for all already set token values which requires that no
1764 other structure's pointer may point to them!
1768 ldi->fullpath = NULL;
1769 ldi->basepath = NULL;
1770 ldi->identifier = NULL;
1771 ldi->name = getStringCopy(ANONYMOUS_NAME);
1772 ldi->name_sorting = NULL;
1773 ldi->author = getStringCopy(parent->author);
1775 ldi->imported_from = getStringCopy(parent->imported_from);
1776 ldi->imported_by = getStringCopy(parent->imported_by);
1777 ldi->class_desc = getStringCopy(parent->class_desc);
1779 ldi->graphics_set = NULL;
1780 ldi->sounds_set = NULL;
1781 ldi->music_set = NULL;
1782 ldi->graphics_path = NULL;
1783 ldi->sounds_path = NULL;
1784 ldi->music_path = NULL;
1786 ldi->level_group = FALSE;
1787 ldi->parent_link = FALSE;
1789 ldi->node_top = parent->node_top;
1790 ldi->node_parent = parent;
1791 ldi->node_group = NULL;
1797 static void freeTreeInfo(TreeInfo *ldi)
1799 checked_free(ldi->subdir);
1800 checked_free(ldi->fullpath);
1801 checked_free(ldi->basepath);
1802 checked_free(ldi->identifier);
1804 checked_free(ldi->name);
1805 checked_free(ldi->name_sorting);
1806 checked_free(ldi->author);
1808 checked_free(ldi->class_desc);
1810 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1812 checked_free(ldi->imported_from);
1813 checked_free(ldi->imported_by);
1815 checked_free(ldi->graphics_set);
1816 checked_free(ldi->sounds_set);
1817 checked_free(ldi->music_set);
1819 checked_free(ldi->graphics_path);
1820 checked_free(ldi->sounds_path);
1821 checked_free(ldi->music_path);
1823 checked_free(ldi->level_filename);
1824 checked_free(ldi->level_filetype);
1828 void setSetupInfo(struct TokenInfo *token_info,
1829 int token_nr, char *token_value)
1831 int token_type = token_info[token_nr].type;
1832 void *setup_value = token_info[token_nr].value;
1834 if (token_value == NULL)
1837 /* set setup field to corresponding token value */
1842 *(boolean *)setup_value = get_boolean_from_string(token_value);
1846 *(Key *)setup_value = getKeyFromKeyName(token_value);
1850 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1854 *(int *)setup_value = get_integer_from_string(token_value);
1858 checked_free(*(char **)setup_value);
1859 *(char **)setup_value = getStringCopy(token_value);
1867 static int compareTreeInfoEntries(const void *object1, const void *object2)
1869 const TreeInfo *entry1 = *((TreeInfo **)object1);
1870 const TreeInfo *entry2 = *((TreeInfo **)object2);
1871 int class_sorting1, class_sorting2;
1874 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1876 class_sorting1 = LEVELSORTING(entry1);
1877 class_sorting2 = LEVELSORTING(entry2);
1881 class_sorting1 = ARTWORKSORTING(entry1);
1882 class_sorting2 = ARTWORKSORTING(entry2);
1885 if (entry1->parent_link || entry2->parent_link)
1886 compare_result = (entry1->parent_link ? -1 : +1);
1887 else if (entry1->sort_priority == entry2->sort_priority)
1889 char *name1 = getStringToLower(entry1->name_sorting);
1890 char *name2 = getStringToLower(entry2->name_sorting);
1892 compare_result = strcmp(name1, name2);
1897 else if (class_sorting1 == class_sorting2)
1898 compare_result = entry1->sort_priority - entry2->sort_priority;
1900 compare_result = class_sorting1 - class_sorting2;
1902 return compare_result;
1905 static void createParentTreeInfoNode(TreeInfo *node_parent)
1909 if (node_parent == NULL)
1912 ti_new = newTreeInfo();
1913 setTreeInfoToDefaults(ti_new, node_parent->type);
1915 ti_new->node_parent = node_parent;
1916 ti_new->parent_link = TRUE;
1919 setString(&ti_new->identifier, node_parent->identifier);
1920 setString(&ti_new->name, ".. (parent directory)");
1921 setString(&ti_new->name_sorting, ti_new->name);
1923 setString(&ti_new->subdir, "..");
1924 setString(&ti_new->fullpath, node_parent->fullpath);
1926 ti_new->sort_priority = node_parent->sort_priority;
1927 ti_new->latest_engine = node_parent->latest_engine;
1929 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1931 ti_new->identifier = getStringCopy(node_parent->identifier);
1932 ti_new->name = ".. (parent directory)";
1933 ti_new->name_sorting = getStringCopy(ti_new->name);
1935 ti_new->subdir = "..";
1936 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1938 ti_new->sort_priority = node_parent->sort_priority;
1939 ti_new->latest_engine = node_parent->latest_engine;
1941 ti_new->class_desc = getLevelClassDescription(ti_new);
1944 pushTreeInfo(&node_parent->node_group, ti_new);
1947 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1948 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1950 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1951 TreeInfo *node_parent,
1952 char *level_directory,
1953 char *directory_name)
1955 char *directory_path = getPath2(level_directory, directory_name);
1956 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1957 SetupFileHash *setup_file_hash;
1958 LevelDirTree *leveldir_new = NULL;
1961 /* unless debugging, silently ignore directories without "levelinfo.conf" */
1962 if (!options.debug && !fileExists(filename))
1964 free(directory_path);
1970 setup_file_hash = loadSetupFileHash(filename);
1972 if (setup_file_hash == NULL)
1974 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1976 free(directory_path);
1982 leveldir_new = newTreeInfo();
1985 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1987 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1989 leveldir_new->subdir = getStringCopy(directory_name);
1991 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1993 /* set all structure fields according to the token/value pairs */
1994 ldi = *leveldir_new;
1995 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1996 setSetupInfo(levelinfo_tokens, i,
1997 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1998 *leveldir_new = ldi;
2001 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2002 setString(&leveldir_new->name, leveldir_new->subdir);
2004 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2006 free(leveldir_new->name);
2007 leveldir_new->name = getStringCopy(leveldir_new->subdir);
2011 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2013 if (leveldir_new->identifier == NULL)
2014 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2016 if (leveldir_new->name_sorting == NULL)
2017 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2019 if (node_parent == NULL) /* top level group */
2021 leveldir_new->basepath = getStringCopy(level_directory);
2022 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2024 else /* sub level group */
2026 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2027 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2030 if (leveldir_new->levels < 1)
2031 leveldir_new->levels = 1;
2033 leveldir_new->last_level =
2034 leveldir_new->first_level + leveldir_new->levels - 1;
2037 leveldir_new->in_user_dir =
2038 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2040 leveldir_new->in_user_dir =
2041 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2045 /* adjust some settings if user's private level directory was detected */
2046 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2047 leveldir_new->in_user_dir &&
2048 (strcmp(leveldir_new->subdir, getLoginName()) == 0 ||
2049 strcmp(leveldir_new->name, getLoginName()) == 0 ||
2050 strcmp(leveldir_new->author, getRealName()) == 0))
2052 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2053 leveldir_new->readonly = FALSE;
2057 /* adjust sort priority if user's private level directory was detected */
2058 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2059 leveldir_new->in_user_dir &&
2060 strcmp(leveldir_new->subdir, getLoginName()) == 0)
2061 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2064 leveldir_new->user_defined =
2065 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2067 leveldir_new->color = LEVELCOLOR(leveldir_new);
2069 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2071 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2074 leveldir_new->handicap_level = /* set handicap to default value */
2075 (leveldir_new->user_defined || !leveldir_new->handicap ?
2076 leveldir_new->last_level : leveldir_new->first_level);
2078 pushTreeInfo(node_first, leveldir_new);
2080 freeSetupFileHash(setup_file_hash);
2082 if (leveldir_new->level_group)
2084 /* create node to link back to current level directory */
2085 createParentTreeInfoNode(leveldir_new);
2087 /* step into sub-directory and look for more level series */
2088 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2089 leveldir_new, directory_path);
2092 free(directory_path);
2098 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2099 TreeInfo *node_parent,
2100 char *level_directory)
2103 struct dirent *dir_entry;
2104 boolean valid_entry_found = FALSE;
2106 if ((dir = opendir(level_directory)) == NULL)
2108 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2112 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2114 struct stat file_status;
2115 char *directory_name = dir_entry->d_name;
2116 char *directory_path = getPath2(level_directory, directory_name);
2118 /* skip entries for current and parent directory */
2119 if (strcmp(directory_name, ".") == 0 ||
2120 strcmp(directory_name, "..") == 0)
2122 free(directory_path);
2126 /* find out if directory entry is itself a directory */
2127 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2128 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2130 free(directory_path);
2134 free(directory_path);
2136 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2137 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2138 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2141 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2148 if (!valid_entry_found)
2150 /* check if this directory directly contains a file "levelinfo.conf" */
2151 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2152 level_directory, ".");
2155 if (!valid_entry_found)
2156 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2160 void LoadLevelInfo()
2162 InitUserLevelDirectory(getLoginName());
2164 DrawInitText("Loading level series:", 120, FC_GREEN);
2166 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2167 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2169 /* before sorting, the first entries will be from the user directory */
2170 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2172 if (leveldir_first == NULL)
2173 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2175 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2178 dumpTreeInfo(leveldir_first, 0);
2182 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2183 TreeInfo *node_parent,
2184 char *base_directory,
2185 char *directory_name, int type)
2187 char *directory_path = getPath2(base_directory, directory_name);
2188 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2189 SetupFileHash *setup_file_hash = NULL;
2190 TreeInfo *artwork_new = NULL;
2193 if (fileExists(filename))
2194 setup_file_hash = loadSetupFileHash(filename);
2196 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2199 struct dirent *dir_entry;
2200 boolean valid_file_found = FALSE;
2202 if ((dir = opendir(directory_path)) != NULL)
2204 while ((dir_entry = readdir(dir)) != NULL)
2206 char *entry_name = dir_entry->d_name;
2208 if (FileIsArtworkType(entry_name, type))
2210 valid_file_found = TRUE;
2218 if (!valid_file_found)
2220 if (strcmp(directory_name, ".") != 0)
2221 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2223 free(directory_path);
2230 artwork_new = newTreeInfo();
2233 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2235 setTreeInfoToDefaults(artwork_new, type);
2237 artwork_new->subdir = getStringCopy(directory_name);
2239 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2242 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2245 /* set all structure fields according to the token/value pairs */
2247 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2248 setSetupInfo(levelinfo_tokens, i,
2249 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2253 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2254 setString(&artwork_new->name, artwork_new->subdir);
2256 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2258 free(artwork_new->name);
2259 artwork_new->name = getStringCopy(artwork_new->subdir);
2264 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2267 if (artwork_new->identifier == NULL)
2268 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2270 if (artwork_new->name_sorting == NULL)
2271 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2274 if (node_parent == NULL) /* top level group */
2276 artwork_new->basepath = getStringCopy(base_directory);
2277 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2279 else /* sub level group */
2281 artwork_new->basepath = getStringCopy(node_parent->basepath);
2282 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2286 artwork_new->in_user_dir =
2287 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2289 artwork_new->in_user_dir =
2290 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2293 /* (may use ".sort_priority" from "setup_file_hash" above) */
2294 artwork_new->color = ARTWORKCOLOR(artwork_new);
2296 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2298 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2301 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2304 if (artwork_new->name != NULL)
2306 free(artwork_new->name);
2307 artwork_new->name = NULL;
2312 if (artwork_new->identifier != NULL)
2314 free(artwork_new->identifier);
2315 artwork_new->identifier = NULL;
2319 if (strcmp(artwork_new->subdir, ".") == 0)
2321 if (artwork_new->user_defined)
2324 setString(&artwork_new->identifier, "private");
2326 artwork_new->identifier = getStringCopy("private");
2328 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2333 setString(&artwork_new->identifier, "classic");
2335 artwork_new->identifier = getStringCopy("classic");
2337 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2340 /* set to new values after changing ".sort_priority" */
2341 artwork_new->color = ARTWORKCOLOR(artwork_new);
2343 setString(&artwork_new->class_desc,
2344 getLevelClassDescription(artwork_new));
2346 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2352 setString(&artwork_new->identifier, artwork_new->subdir);
2354 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2359 setString(&artwork_new->name, artwork_new->identifier);
2360 setString(&artwork_new->name_sorting, artwork_new->name);
2362 artwork_new->name = getStringCopy(artwork_new->identifier);
2363 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2367 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2369 pushTreeInfo(node_first, artwork_new);
2371 freeSetupFileHash(setup_file_hash);
2373 free(directory_path);
2379 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2380 TreeInfo *node_parent,
2381 char *base_directory, int type)
2384 struct dirent *dir_entry;
2385 boolean valid_entry_found = FALSE;
2387 if ((dir = opendir(base_directory)) == NULL)
2389 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2390 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2394 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2396 struct stat file_status;
2397 char *directory_name = dir_entry->d_name;
2398 char *directory_path = getPath2(base_directory, directory_name);
2400 /* skip entries for current and parent directory */
2401 if (strcmp(directory_name, ".") == 0 ||
2402 strcmp(directory_name, "..") == 0)
2404 free(directory_path);
2408 /* find out if directory entry is itself a directory */
2409 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2410 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2412 free(directory_path);
2416 free(directory_path);
2418 /* check if this directory contains artwork with or without config file */
2419 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2421 directory_name, type);
2426 /* check if this directory directly contains artwork itself */
2427 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2428 base_directory, ".",
2430 if (!valid_entry_found)
2431 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2435 static TreeInfo *getDummyArtworkInfo(int type)
2437 /* this is only needed when there is completely no artwork available */
2438 TreeInfo *artwork_new = newTreeInfo();
2440 setTreeInfoToDefaults(artwork_new, type);
2443 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2444 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2445 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2447 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2448 setString(&artwork_new->name, UNDEFINED_FILENAME);
2449 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2451 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2452 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2453 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2455 checked_free(artwork_new->name);
2457 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2458 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2459 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2465 void LoadArtworkInfo()
2467 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2469 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2470 options.graphics_directory,
2471 TREE_TYPE_GRAPHICS_DIR);
2472 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2473 getUserGraphicsDir(),
2474 TREE_TYPE_GRAPHICS_DIR);
2476 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2477 options.sounds_directory,
2478 TREE_TYPE_SOUNDS_DIR);
2479 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2481 TREE_TYPE_SOUNDS_DIR);
2483 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2484 options.music_directory,
2485 TREE_TYPE_MUSIC_DIR);
2486 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2488 TREE_TYPE_MUSIC_DIR);
2490 if (artwork.gfx_first == NULL)
2491 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2492 if (artwork.snd_first == NULL)
2493 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2494 if (artwork.mus_first == NULL)
2495 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2497 /* before sorting, the first entries will be from the user directory */
2498 artwork.gfx_current =
2499 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2500 if (artwork.gfx_current == NULL)
2501 artwork.gfx_current =
2502 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2503 if (artwork.gfx_current == NULL)
2504 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2506 artwork.snd_current =
2507 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2508 if (artwork.snd_current == NULL)
2509 artwork.snd_current =
2510 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2511 if (artwork.snd_current == NULL)
2512 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2514 artwork.mus_current =
2515 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2516 if (artwork.mus_current == NULL)
2517 artwork.mus_current =
2518 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2519 if (artwork.mus_current == NULL)
2520 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2522 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2523 artwork.snd_current_identifier = artwork.snd_current->identifier;
2524 artwork.mus_current_identifier = artwork.mus_current->identifier;
2527 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2528 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2529 printf("music set == %s\n\n", artwork.mus_current_identifier);
2532 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2533 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2534 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2537 dumpTreeInfo(artwork.gfx_first, 0);
2538 dumpTreeInfo(artwork.snd_first, 0);
2539 dumpTreeInfo(artwork.mus_first, 0);
2543 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2544 LevelDirTree *level_node)
2546 /* recursively check all level directories for artwork sub-directories */
2550 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2551 ARTWORK_DIRECTORY((*artwork_node)->type));
2554 if (!level_node->parent_link)
2555 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2556 level_node->subdir, level_node->name);
2559 if (!level_node->parent_link)
2561 TreeInfo *topnode_last = *artwork_node;
2563 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2564 (*artwork_node)->type);
2566 if (topnode_last != *artwork_node)
2568 free((*artwork_node)->identifier);
2569 free((*artwork_node)->name);
2570 free((*artwork_node)->name_sorting);
2572 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2573 (*artwork_node)->name = getStringCopy(level_node->name);
2574 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2576 (*artwork_node)->sort_priority = level_node->sort_priority;
2577 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2583 if (level_node->node_group != NULL)
2584 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2586 level_node = level_node->next;
2590 void LoadLevelArtworkInfo()
2592 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2594 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2595 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2596 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2598 /* needed for reloading level artwork not known at ealier stage */
2600 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2602 artwork.gfx_current =
2603 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2604 if (artwork.gfx_current == NULL)
2605 artwork.gfx_current =
2606 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2607 if (artwork.gfx_current == NULL)
2608 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2611 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2613 artwork.snd_current =
2614 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2615 if (artwork.snd_current == NULL)
2616 artwork.snd_current =
2617 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2618 if (artwork.snd_current == NULL)
2619 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2622 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2624 artwork.mus_current =
2625 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2626 if (artwork.mus_current == NULL)
2627 artwork.mus_current =
2628 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2629 if (artwork.mus_current == NULL)
2630 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2633 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2634 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2635 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2638 dumpTreeInfo(artwork.gfx_first, 0);
2639 dumpTreeInfo(artwork.snd_first, 0);
2640 dumpTreeInfo(artwork.mus_first, 0);
2644 static void SaveUserLevelInfo()
2646 LevelDirTree *level_info;
2651 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2653 if (!(file = fopen(filename, MODE_WRITE)))
2655 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2660 level_info = newTreeInfo();
2662 /* always start with reliable default values */
2663 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2666 setString(&level_info->name, getLoginName());
2667 setString(&level_info->author, getRealName());
2668 level_info->levels = 100;
2669 level_info->first_level = 1;
2671 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2672 level_info->readonly = FALSE;
2673 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2674 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2675 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2678 ldi.name = getStringCopy(getLoginName());
2679 ldi.author = getStringCopy(getRealName());
2681 ldi.first_level = 1;
2682 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2683 ldi.readonly = FALSE;
2684 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2685 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2686 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2689 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2691 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2692 getCookie("LEVELINFO")));
2695 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2698 if (i == LEVELINFO_TOKEN_NAME ||
2699 i == LEVELINFO_TOKEN_AUTHOR ||
2700 i == LEVELINFO_TOKEN_LEVELS ||
2701 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2702 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2704 /* just to make things nicer :) */
2705 if (i == LEVELINFO_TOKEN_AUTHOR)
2706 fprintf(file, "\n");
2708 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2709 i != LEVELINFO_TOKEN_NAME_SORTING &&
2710 i != LEVELINFO_TOKEN_IMPORTED_FROM &&
2711 i != LEVELINFO_TOKEN_IMPORTED_BY &&
2712 i != LEVELINFO_TOKEN_FILENAME &&
2713 i != LEVELINFO_TOKEN_FILETYPE)
2714 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2718 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2722 SetFilePermissions(filename, PERMS_PRIVATE);
2724 freeTreeInfo(level_info);
2728 char *getSetupValue(int type, void *value)
2730 static char value_string[MAX_LINE_LEN];
2738 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2742 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2746 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2750 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2754 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2758 sprintf(value_string, "%d", *(int *)value);
2762 strcpy(value_string, *(char **)value);
2766 value_string[0] = '\0';
2770 return value_string;
2773 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2777 static char token_string[MAX_LINE_LEN];
2778 int token_type = token_info[token_nr].type;
2779 void *setup_value = token_info[token_nr].value;
2780 char *token_text = token_info[token_nr].text;
2781 char *value_string = getSetupValue(token_type, setup_value);
2783 /* build complete token string */
2784 sprintf(token_string, "%s%s", prefix, token_text);
2786 /* build setup entry line */
2787 line = getFormattedSetupEntry(token_string, value_string);
2789 if (token_type == TYPE_KEY_X11)
2791 Key key = *(Key *)setup_value;
2792 char *keyname = getKeyNameFromKey(key);
2794 /* add comment, if useful */
2795 if (strcmp(keyname, "(undefined)") != 0 &&
2796 strcmp(keyname, "(unknown)") != 0)
2798 /* add at least one whitespace */
2800 for (i = strlen(line); i < token_comment_position; i++)
2804 strcat(line, keyname);
2811 void LoadLevelSetup_LastSeries()
2813 /* ----------------------------------------------------------------------- */
2814 /* ~/.<program>/levelsetup.conf */
2815 /* ----------------------------------------------------------------------- */
2817 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2818 SetupFileHash *level_setup_hash = NULL;
2820 /* always start with reliable default values */
2821 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2823 if ((level_setup_hash = loadSetupFileHash(filename)))
2825 char *last_level_series =
2826 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2828 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2830 if (leveldir_current == NULL)
2831 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2833 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2835 freeSetupFileHash(level_setup_hash);
2838 Error(ERR_WARN, "using default setup values");
2843 void SaveLevelSetup_LastSeries()
2845 /* ----------------------------------------------------------------------- */
2846 /* ~/.<program>/levelsetup.conf */
2847 /* ----------------------------------------------------------------------- */
2849 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2850 char *level_subdir = leveldir_current->subdir;
2853 InitUserDataDirectory();
2855 if (!(file = fopen(filename, MODE_WRITE)))
2857 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2862 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2863 getCookie("LEVELSETUP")));
2864 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2869 SetFilePermissions(filename, PERMS_PRIVATE);
2874 static void checkSeriesInfo()
2876 static char *level_directory = NULL;
2878 struct dirent *dir_entry;
2880 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2882 level_directory = getPath2((leveldir_current->in_user_dir ?
2883 getUserLevelDir(NULL) :
2884 options.level_directory),
2885 leveldir_current->fullpath);
2887 if ((dir = opendir(level_directory)) == NULL)
2889 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2893 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2895 if (strlen(dir_entry->d_name) > 4 &&
2896 dir_entry->d_name[3] == '.' &&
2897 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2899 char levelnum_str[4];
2902 strncpy(levelnum_str, dir_entry->d_name, 3);
2903 levelnum_str[3] = '\0';
2905 levelnum_value = atoi(levelnum_str);
2908 if (levelnum_value < leveldir_current->first_level)
2910 Error(ERR_WARN, "additional level %d found", levelnum_value);
2911 leveldir_current->first_level = levelnum_value;
2913 else if (levelnum_value > leveldir_current->last_level)
2915 Error(ERR_WARN, "additional level %d found", levelnum_value);
2916 leveldir_current->last_level = levelnum_value;
2925 void LoadLevelSetup_SeriesInfo()
2928 SetupFileHash *level_setup_hash = NULL;
2929 char *level_subdir = leveldir_current->subdir;
2931 /* always start with reliable default values */
2932 level_nr = leveldir_current->first_level;
2934 checkSeriesInfo(leveldir_current);
2936 /* ----------------------------------------------------------------------- */
2937 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2938 /* ----------------------------------------------------------------------- */
2940 level_subdir = leveldir_current->subdir;
2942 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2944 if ((level_setup_hash = loadSetupFileHash(filename)))
2948 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2952 level_nr = atoi(token_value);
2954 if (level_nr < leveldir_current->first_level)
2955 level_nr = leveldir_current->first_level;
2956 if (level_nr > leveldir_current->last_level)
2957 level_nr = leveldir_current->last_level;
2960 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2964 int level_nr = atoi(token_value);
2966 if (level_nr < leveldir_current->first_level)
2967 level_nr = leveldir_current->first_level;
2968 if (level_nr > leveldir_current->last_level + 1)
2969 level_nr = leveldir_current->last_level;
2971 if (leveldir_current->user_defined || !leveldir_current->handicap)
2972 level_nr = leveldir_current->last_level;
2974 leveldir_current->handicap_level = level_nr;
2977 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2979 freeSetupFileHash(level_setup_hash);
2982 Error(ERR_WARN, "using default setup values");
2987 void SaveLevelSetup_SeriesInfo()
2990 char *level_subdir = leveldir_current->subdir;
2991 char *level_nr_str = int2str(level_nr, 0);
2992 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2995 /* ----------------------------------------------------------------------- */
2996 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2997 /* ----------------------------------------------------------------------- */
2999 InitLevelSetupDirectory(level_subdir);
3001 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3003 if (!(file = fopen(filename, MODE_WRITE)))
3005 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3010 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3011 getCookie("LEVELSETUP")));
3012 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3014 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3015 handicap_level_str));
3019 SetFilePermissions(filename, PERMS_PRIVATE);