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 40
77 #define TOKEN_COMMENT_POSITION 60
79 #define MAX_COOKIE_LEN 256
82 /* ------------------------------------------------------------------------- */
84 /* ------------------------------------------------------------------------- */
86 static char *getLevelClassDescription(TreeInfo *ldi)
88 int position = ldi->sort_priority / 100;
90 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
91 return levelclass_desc[position];
93 return "Unknown Level Class";
96 static char *getUserLevelDir(char *level_subdir)
98 static char *userlevel_dir = NULL;
99 char *data_dir = getUserDataDir();
100 char *userlevel_subdir = LEVELS_DIRECTORY;
102 checked_free(userlevel_dir);
104 if (level_subdir != NULL)
105 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
107 userlevel_dir = getPath2(data_dir, userlevel_subdir);
109 return userlevel_dir;
112 static char *getScoreDir(char *level_subdir)
114 static char *score_dir = NULL;
115 char *data_dir = getCommonDataDir();
116 char *score_subdir = SCORES_DIRECTORY;
118 checked_free(score_dir);
120 if (level_subdir != NULL)
121 score_dir = getPath3(data_dir, score_subdir, level_subdir);
123 score_dir = getPath2(data_dir, score_subdir);
128 static char *getLevelSetupDir(char *level_subdir)
130 static char *levelsetup_dir = NULL;
131 char *data_dir = getUserDataDir();
132 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
134 checked_free(levelsetup_dir);
136 if (level_subdir != NULL)
137 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
139 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
141 return levelsetup_dir;
144 static char *getLevelDirFromTreeInfo(TreeInfo *node)
146 static char *level_dir = NULL;
149 return options.level_directory;
151 checked_free(level_dir);
153 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
154 options.level_directory), node->fullpath);
159 char *getCurrentLevelDir()
161 return getLevelDirFromTreeInfo(leveldir_current);
164 static char *getTapeDir(char *level_subdir)
166 static char *tape_dir = NULL;
167 char *data_dir = getUserDataDir();
168 char *tape_subdir = TAPES_DIRECTORY;
170 checked_free(tape_dir);
172 if (level_subdir != NULL)
173 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
175 tape_dir = getPath2(data_dir, tape_subdir);
180 static char *getSolutionTapeDir()
182 static char *tape_dir = NULL;
183 char *data_dir = getCurrentLevelDir();
184 char *tape_subdir = TAPES_DIRECTORY;
186 checked_free(tape_dir);
188 tape_dir = getPath2(data_dir, tape_subdir);
193 static char *getDefaultGraphicsDir(char *graphics_subdir)
195 static char *graphics_dir = NULL;
197 if (graphics_subdir == NULL)
198 return options.graphics_directory;
200 checked_free(graphics_dir);
202 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
207 static char *getDefaultSoundsDir(char *sounds_subdir)
209 static char *sounds_dir = NULL;
211 if (sounds_subdir == NULL)
212 return options.sounds_directory;
214 checked_free(sounds_dir);
216 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
221 static char *getDefaultMusicDir(char *music_subdir)
223 static char *music_dir = NULL;
225 if (music_subdir == NULL)
226 return options.music_directory;
228 checked_free(music_dir);
230 music_dir = getPath2(options.music_directory, music_subdir);
235 static char *getDefaultArtworkSet(int type)
237 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
238 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
239 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
242 static char *getDefaultArtworkDir(int type)
244 return (type == TREE_TYPE_GRAPHICS_DIR ?
245 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
246 type == TREE_TYPE_SOUNDS_DIR ?
247 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
248 type == TREE_TYPE_MUSIC_DIR ?
249 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
252 static char *getUserGraphicsDir()
254 static char *usergraphics_dir = NULL;
256 if (usergraphics_dir == NULL)
257 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
259 return usergraphics_dir;
262 static char *getUserSoundsDir()
264 static char *usersounds_dir = NULL;
266 if (usersounds_dir == NULL)
267 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
269 return usersounds_dir;
272 static char *getUserMusicDir()
274 static char *usermusic_dir = NULL;
276 if (usermusic_dir == NULL)
277 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
279 return usermusic_dir;
282 static char *getSetupArtworkDir(TreeInfo *ti)
284 static char *artwork_dir = NULL;
286 checked_free(artwork_dir);
288 artwork_dir = getPath2(ti->basepath, ti->fullpath);
293 char *setLevelArtworkDir(TreeInfo *ti)
295 char **artwork_path_ptr, **artwork_set_ptr;
296 TreeInfo *level_artwork;
298 if (ti == NULL || leveldir_current == NULL)
301 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
302 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
304 checked_free(*artwork_path_ptr);
306 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
307 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
310 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
311 normally result in using the artwork configured in the setup menu. But
312 if an artwork subdirectory exists (which might contain custom artwork
313 or an artwork configuration file), this level artwork must be treated
314 as relative to the default "classic" artwork, not to the artwork that
315 is currently configured in the setup menu. */
317 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
319 checked_free(*artwork_set_ptr);
323 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
324 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
328 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
329 *artwork_set_ptr = NULL;
335 return *artwork_set_ptr;
338 inline static char *getLevelArtworkSet(int type)
340 if (leveldir_current == NULL)
343 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
346 inline static char *getLevelArtworkDir(int type)
348 if (leveldir_current == NULL)
349 return UNDEFINED_FILENAME;
351 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
354 char *getTapeFilename(int nr)
356 static char *filename = NULL;
357 char basename[MAX_FILENAME_LEN];
359 checked_free(filename);
361 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
362 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
367 char *getSolutionTapeFilename(int nr)
369 static char *filename = NULL;
370 char basename[MAX_FILENAME_LEN];
372 checked_free(filename);
374 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
375 filename = getPath2(getSolutionTapeDir(), basename);
380 char *getScoreFilename(int nr)
382 static char *filename = NULL;
383 char basename[MAX_FILENAME_LEN];
385 checked_free(filename);
387 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
388 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
393 char *getSetupFilename()
395 static char *filename = NULL;
397 checked_free(filename);
399 filename = getPath2(getSetupDir(), SETUP_FILENAME);
404 char *getEditorSetupFilename()
406 static char *filename = NULL;
408 checked_free(filename);
409 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
411 if (fileExists(filename))
414 checked_free(filename);
415 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
420 char *getHelpAnimFilename()
422 static char *filename = NULL;
424 checked_free(filename);
426 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
431 char *getHelpTextFilename()
433 static char *filename = NULL;
435 checked_free(filename);
437 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
442 char *getLevelSetInfoFilename()
444 static char *filename = NULL;
459 for (i = 0; basenames[i] != NULL; i++)
461 checked_free(filename);
462 filename = getPath2(getCurrentLevelDir(), basenames[i]);
464 if (fileExists(filename))
471 static char *getCorrectedArtworkBasename(char *basename)
473 char *basename_corrected = basename;
475 #if defined(PLATFORM_MSDOS)
476 if (program.filename_prefix != NULL)
478 int prefix_len = strlen(program.filename_prefix);
480 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
481 basename_corrected = &basename[prefix_len];
483 /* if corrected filename is still longer than standard MS-DOS filename
484 size (8 characters + 1 dot + 3 characters file extension), shorten
485 filename by writing file extension after 8th basename character */
486 if (strlen(basename_corrected) > 8 + 1 + 3)
488 static char *msdos_filename = NULL;
490 checked_free(msdos_filename);
492 msdos_filename = getStringCopy(basename_corrected);
493 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
495 basename_corrected = msdos_filename;
500 return basename_corrected;
503 char *getCustomImageFilename(char *basename)
505 static char *filename = NULL;
506 boolean skip_setup_artwork = FALSE;
508 checked_free(filename);
510 basename = getCorrectedArtworkBasename(basename);
512 if (!setup.override_level_graphics)
514 /* 1st try: look for special artwork in current level series directory */
515 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
516 if (fileExists(filename))
521 /* check if there is special artwork configured in level series config */
522 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
524 /* 2nd try: look for special artwork configured in level series config */
525 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
526 if (fileExists(filename))
531 /* take missing artwork configured in level set config from default */
532 skip_setup_artwork = TRUE;
536 if (!skip_setup_artwork)
538 /* 3rd try: look for special artwork in configured artwork directory */
539 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
540 if (fileExists(filename))
546 /* 4th try: look for default artwork in new default artwork directory */
547 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
548 if (fileExists(filename))
553 /* 5th try: look for default artwork in old default artwork directory */
554 filename = getPath2(options.graphics_directory, basename);
555 if (fileExists(filename))
558 return NULL; /* cannot find specified artwork file anywhere */
561 char *getCustomSoundFilename(char *basename)
563 static char *filename = NULL;
564 boolean skip_setup_artwork = FALSE;
566 checked_free(filename);
568 basename = getCorrectedArtworkBasename(basename);
570 if (!setup.override_level_sounds)
572 /* 1st try: look for special artwork in current level series directory */
573 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
574 if (fileExists(filename))
579 /* check if there is special artwork configured in level series config */
580 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
582 /* 2nd try: look for special artwork configured in level series config */
583 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
584 if (fileExists(filename))
589 /* take missing artwork configured in level set config from default */
590 skip_setup_artwork = TRUE;
594 if (!skip_setup_artwork)
596 /* 3rd try: look for special artwork in configured artwork directory */
597 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
598 if (fileExists(filename))
604 /* 4th try: look for default artwork in new default artwork directory */
605 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
606 if (fileExists(filename))
611 /* 5th try: look for default artwork in old default artwork directory */
612 filename = getPath2(options.sounds_directory, basename);
613 if (fileExists(filename))
616 return NULL; /* cannot find specified artwork file anywhere */
619 char *getCustomMusicFilename(char *basename)
621 static char *filename = NULL;
622 boolean skip_setup_artwork = FALSE;
624 checked_free(filename);
626 basename = getCorrectedArtworkBasename(basename);
628 if (!setup.override_level_music)
630 /* 1st try: look for special artwork in current level series directory */
631 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
632 if (fileExists(filename))
637 /* check if there is special artwork configured in level series config */
638 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
640 /* 2nd try: look for special artwork configured in level series config */
641 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
642 if (fileExists(filename))
647 /* take missing artwork configured in level set config from default */
648 skip_setup_artwork = TRUE;
652 if (!skip_setup_artwork)
654 /* 3rd try: look for special artwork in configured artwork directory */
655 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
656 if (fileExists(filename))
662 /* 4th try: look for default artwork in new default artwork directory */
663 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
664 if (fileExists(filename))
669 /* 5th try: look for default artwork in old default artwork directory */
670 filename = getPath2(options.music_directory, basename);
671 if (fileExists(filename))
674 return NULL; /* cannot find specified artwork file anywhere */
677 char *getCustomArtworkFilename(char *basename, int type)
679 if (type == ARTWORK_TYPE_GRAPHICS)
680 return getCustomImageFilename(basename);
681 else if (type == ARTWORK_TYPE_SOUNDS)
682 return getCustomSoundFilename(basename);
683 else if (type == ARTWORK_TYPE_MUSIC)
684 return getCustomMusicFilename(basename);
686 return UNDEFINED_FILENAME;
689 char *getCustomArtworkConfigFilename(int type)
691 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
694 char *getCustomArtworkLevelConfigFilename(int type)
696 static char *filename = NULL;
698 checked_free(filename);
700 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
705 char *getCustomMusicDirectory(void)
707 static char *directory = NULL;
708 boolean skip_setup_artwork = FALSE;
710 checked_free(directory);
712 if (!setup.override_level_music)
714 /* 1st try: look for special artwork in current level series directory */
715 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
716 if (fileExists(directory))
721 /* check if there is special artwork configured in level series config */
722 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
724 /* 2nd try: look for special artwork configured in level series config */
725 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
726 if (fileExists(directory))
731 /* take missing artwork configured in level set config from default */
732 skip_setup_artwork = TRUE;
736 if (!skip_setup_artwork)
738 /* 3rd try: look for special artwork in configured artwork directory */
739 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
740 if (fileExists(directory))
746 /* 4th try: look for default artwork in new default artwork directory */
747 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
748 if (fileExists(directory))
753 /* 5th try: look for default artwork in old default artwork directory */
754 directory = getStringCopy(options.music_directory);
755 if (fileExists(directory))
758 return NULL; /* cannot find specified artwork file anywhere */
761 void InitTapeDirectory(char *level_subdir)
763 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
764 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
765 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
768 void InitScoreDirectory(char *level_subdir)
770 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
771 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
772 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
775 static void SaveUserLevelInfo();
777 void InitUserLevelDirectory(char *level_subdir)
779 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
781 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
782 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
783 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
789 void InitLevelSetupDirectory(char *level_subdir)
791 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
792 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
793 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
797 /* ------------------------------------------------------------------------- */
798 /* some functions to handle lists of level directories */
799 /* ------------------------------------------------------------------------- */
801 TreeInfo *newTreeInfo()
803 return checked_calloc(sizeof(TreeInfo));
806 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
808 node_new->next = *node_first;
809 *node_first = node_new;
812 int numTreeInfo(TreeInfo *node)
825 boolean validLevelSeries(TreeInfo *node)
827 return (node != NULL && !node->node_group && !node->parent_link);
830 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
835 if (node->node_group) /* enter level group (step down into tree) */
836 return getFirstValidTreeInfoEntry(node->node_group);
837 else if (node->parent_link) /* skip start entry of level group */
839 if (node->next) /* get first real level series entry */
840 return getFirstValidTreeInfoEntry(node->next);
841 else /* leave empty level group and go on */
842 return getFirstValidTreeInfoEntry(node->node_parent->next);
844 else /* this seems to be a regular level series */
848 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
853 if (node->node_parent == NULL) /* top level group */
854 return *node->node_top;
855 else /* sub level group */
856 return node->node_parent->node_group;
859 int numTreeInfoInGroup(TreeInfo *node)
861 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
864 int posTreeInfo(TreeInfo *node)
866 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
871 if (node_cmp == node)
875 node_cmp = node_cmp->next;
881 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
883 TreeInfo *node_default = node;
898 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
900 if (identifier == NULL)
905 if (node->node_group)
907 TreeInfo *node_group;
909 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
914 else if (!node->parent_link)
916 if (strcmp(identifier, node->identifier) == 0)
926 void dumpTreeInfo(TreeInfo *node, int depth)
930 printf("Dumping TreeInfo:\n");
934 for (i = 0; i < (depth + 1) * 3; i++)
938 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
939 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
941 printf("subdir == '%s' (%s) [%s] (%d)\n",
942 node->subdir, node->name, node->identifier, node->sort_priority);
945 if (node->node_group != NULL)
946 dumpTreeInfo(node->node_group, depth + 1);
952 void sortTreeInfo(TreeInfo **node_first,
953 int (*compare_function)(const void *, const void *))
955 int num_nodes = numTreeInfo(*node_first);
956 TreeInfo **sort_array;
957 TreeInfo *node = *node_first;
963 /* allocate array for sorting structure pointers */
964 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
966 /* writing structure pointers to sorting array */
967 while (i < num_nodes && node) /* double boundary check... */
969 sort_array[i] = node;
975 /* sorting the structure pointers in the sorting array */
976 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
979 /* update the linkage of list elements with the sorted node array */
980 for (i = 0; i < num_nodes - 1; i++)
981 sort_array[i]->next = sort_array[i + 1];
982 sort_array[num_nodes - 1]->next = NULL;
984 /* update the linkage of the main list anchor pointer */
985 *node_first = sort_array[0];
989 /* now recursively sort the level group structures */
993 if (node->node_group != NULL)
994 sortTreeInfo(&node->node_group, compare_function);
1001 /* ========================================================================= */
1002 /* some stuff from "files.c" */
1003 /* ========================================================================= */
1005 #if defined(PLATFORM_WIN32)
1007 #define S_IRGRP S_IRUSR
1010 #define S_IROTH S_IRUSR
1013 #define S_IWGRP S_IWUSR
1016 #define S_IWOTH S_IWUSR
1019 #define S_IXGRP S_IXUSR
1022 #define S_IXOTH S_IXUSR
1025 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1030 #endif /* PLATFORM_WIN32 */
1032 /* file permissions for newly written files */
1033 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1034 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1035 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1037 #define MODE_W_PRIVATE (S_IWUSR)
1038 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1039 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1041 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1042 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1044 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1045 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1047 char *getUserDataDir(void)
1049 static char *userdata_dir = NULL;
1051 if (userdata_dir == NULL)
1052 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1054 return userdata_dir;
1057 char *getCommonDataDir(void)
1059 static char *common_data_dir = NULL;
1061 #if defined(PLATFORM_WIN32)
1062 if (common_data_dir == NULL)
1064 char *dir = checked_malloc(MAX_PATH + 1);
1066 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1067 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1068 common_data_dir = getPath2(dir, program.userdata_directory);
1070 common_data_dir = options.rw_base_directory;
1073 if (common_data_dir == NULL)
1074 common_data_dir = options.rw_base_directory;
1077 return common_data_dir;
1082 return getUserDataDir();
1085 static mode_t posix_umask(mode_t mask)
1087 #if defined(PLATFORM_UNIX)
1094 static int posix_mkdir(const char *pathname, mode_t mode)
1096 #if defined(PLATFORM_WIN32)
1097 return mkdir(pathname);
1099 return mkdir(pathname, mode);
1103 void createDirectory(char *dir, char *text, int permission_class)
1105 /* leave "other" permissions in umask untouched, but ensure group parts
1106 of USERDATA_DIR_MODE are not masked */
1107 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1108 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1109 mode_t normal_umask = posix_umask(0);
1110 mode_t group_umask = ~(dir_mode & S_IRWXG);
1111 posix_umask(normal_umask & group_umask);
1113 if (access(dir, F_OK) != 0)
1114 if (posix_mkdir(dir, dir_mode) != 0)
1115 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1117 posix_umask(normal_umask); /* reset normal umask */
1120 void InitUserDataDirectory()
1122 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1125 void SetFilePermissions(char *filename, int permission_class)
1127 chmod(filename, (permission_class == PERMS_PRIVATE ?
1128 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1131 char *getCookie(char *file_type)
1133 static char cookie[MAX_COOKIE_LEN + 1];
1135 if (strlen(program.cookie_prefix) + 1 +
1136 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1137 return "[COOKIE ERROR]"; /* should never happen */
1139 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1140 program.cookie_prefix, file_type,
1141 program.version_major, program.version_minor);
1146 int getFileVersionFromCookieString(const char *cookie)
1148 const char *ptr_cookie1, *ptr_cookie2;
1149 const char *pattern1 = "_FILE_VERSION_";
1150 const char *pattern2 = "?.?";
1151 const int len_cookie = strlen(cookie);
1152 const int len_pattern1 = strlen(pattern1);
1153 const int len_pattern2 = strlen(pattern2);
1154 const int len_pattern = len_pattern1 + len_pattern2;
1155 int version_major, version_minor;
1157 if (len_cookie <= len_pattern)
1160 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1161 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1163 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1166 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1167 ptr_cookie2[1] != '.' ||
1168 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1171 version_major = ptr_cookie2[0] - '0';
1172 version_minor = ptr_cookie2[2] - '0';
1174 return VERSION_IDENT(version_major, version_minor, 0, 0);
1177 boolean checkCookieString(const char *cookie, const char *template)
1179 const char *pattern = "_FILE_VERSION_?.?";
1180 const int len_cookie = strlen(cookie);
1181 const int len_template = strlen(template);
1182 const int len_pattern = strlen(pattern);
1184 if (len_cookie != len_template)
1187 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1193 /* ------------------------------------------------------------------------- */
1194 /* setup file list and hash handling functions */
1195 /* ------------------------------------------------------------------------- */
1197 char *getFormattedSetupEntry(char *token, char *value)
1200 static char entry[MAX_LINE_LEN];
1202 /* if value is an empty string, just return token without value */
1206 /* start with the token and some spaces to format output line */
1207 sprintf(entry, "%s:", token);
1208 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1211 /* continue with the token's value */
1212 strcat(entry, value);
1217 SetupFileList *newSetupFileList(char *token, char *value)
1219 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1221 new->token = getStringCopy(token);
1222 new->value = getStringCopy(value);
1229 void freeSetupFileList(SetupFileList *list)
1234 checked_free(list->token);
1235 checked_free(list->value);
1238 freeSetupFileList(list->next);
1243 char *getListEntry(SetupFileList *list, char *token)
1248 if (strcmp(list->token, token) == 0)
1251 return getListEntry(list->next, token);
1254 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1259 if (strcmp(list->token, token) == 0)
1261 checked_free(list->value);
1263 list->value = getStringCopy(value);
1267 else if (list->next == NULL)
1268 return (list->next = newSetupFileList(token, value));
1270 return setListEntry(list->next, token, value);
1273 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1278 if (list->next == NULL)
1279 return (list->next = newSetupFileList(token, value));
1281 return addListEntry(list->next, token, value);
1285 static void printSetupFileList(SetupFileList *list)
1290 printf("token: '%s'\n", list->token);
1291 printf("value: '%s'\n", list->value);
1293 printSetupFileList(list->next);
1298 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1299 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1300 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1301 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1303 #define insert_hash_entry hashtable_insert
1304 #define search_hash_entry hashtable_search
1305 #define change_hash_entry hashtable_change
1306 #define remove_hash_entry hashtable_remove
1309 static unsigned int get_hash_from_key(void *key)
1314 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1315 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1316 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1317 it works better than many other constants, prime or not) has never been
1318 adequately explained.
1320 If you just want to have a good hash function, and cannot wait, djb2
1321 is one of the best string hash functions i know. It has excellent
1322 distribution and speed on many different sets of keys and table sizes.
1323 You are not likely to do better with one of the "well known" functions
1324 such as PJW, K&R, etc.
1326 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1329 char *str = (char *)key;
1330 unsigned int hash = 5381;
1333 while ((c = *str++))
1334 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1339 static int keys_are_equal(void *key1, void *key2)
1341 return (strcmp((char *)key1, (char *)key2) == 0);
1344 SetupFileHash *newSetupFileHash()
1346 SetupFileHash *new_hash =
1347 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1349 if (new_hash == NULL)
1350 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1355 void freeSetupFileHash(SetupFileHash *hash)
1360 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1363 char *getHashEntry(SetupFileHash *hash, char *token)
1368 return search_hash_entry(hash, token);
1371 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1378 value_copy = getStringCopy(value);
1380 /* change value; if it does not exist, insert it as new */
1381 if (!change_hash_entry(hash, token, value_copy))
1382 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1383 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1386 char *removeHashEntry(SetupFileHash *hash, char *token)
1391 return remove_hash_entry(hash, token);
1396 static void printSetupFileHash(SetupFileHash *hash)
1398 BEGIN_HASH_ITERATION(hash, itr)
1400 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1401 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1403 END_HASH_ITERATION(hash, itr)
1408 static void *loadSetupFileData(char *filename, boolean use_hash)
1410 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1411 char *token, *value, *line_ptr;
1412 void *setup_file_data, *insert_ptr = NULL;
1413 boolean read_continued_line = FALSE;
1417 setup_file_data = newSetupFileHash();
1419 insert_ptr = setup_file_data = newSetupFileList("", "");
1421 if (!(file = fopen(filename, MODE_READ)))
1423 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1429 /* read next line of input file */
1430 if (!fgets(line, MAX_LINE_LEN, file))
1433 /* cut trailing newline or carriage return */
1434 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1435 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1438 if (read_continued_line)
1440 /* cut leading whitespaces from input line */
1441 for (line_ptr = line; *line_ptr; line_ptr++)
1442 if (*line_ptr != ' ' && *line_ptr != '\t')
1445 /* append new line to existing line, if there is enough space */
1446 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1447 strcat(previous_line, line_ptr);
1449 strcpy(line, previous_line); /* copy storage buffer to line */
1451 read_continued_line = FALSE;
1454 /* if the last character is '\', continue at next line */
1455 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1457 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1458 strcpy(previous_line, line); /* copy line to storage buffer */
1460 read_continued_line = TRUE;
1465 /* cut trailing comment from input line */
1466 for (line_ptr = line; *line_ptr; line_ptr++)
1468 if (*line_ptr == '#')
1475 /* cut trailing whitespaces from input line */
1476 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1477 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1480 /* ignore empty lines */
1484 /* cut leading whitespaces from token */
1485 for (token = line; *token; token++)
1486 if (*token != ' ' && *token != '\t')
1489 /* start with empty value as reliable default */
1492 /* find end of token to determine start of value */
1493 for (line_ptr = token; *line_ptr; line_ptr++)
1495 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1497 *line_ptr = '\0'; /* terminate token string */
1498 value = line_ptr + 1; /* set beginning of value */
1504 /* cut leading whitespaces from value */
1505 for (; *value; value++)
1506 if (*value != ' ' && *value != '\t')
1511 value = "true"; /* treat tokens without value as "true" */
1517 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1519 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1527 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1528 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1532 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1533 SetupFileList *first_valid_list_entry = setup_file_list->next;
1535 /* free empty list header */
1536 setup_file_list->next = NULL;
1537 freeSetupFileList(setup_file_list);
1538 setup_file_data = first_valid_list_entry;
1540 if (first_valid_list_entry == NULL)
1541 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1544 return setup_file_data;
1547 SetupFileList *loadSetupFileList(char *filename)
1549 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1552 SetupFileHash *loadSetupFileHash(char *filename)
1554 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1557 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1560 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1563 Error(ERR_WARN, "configuration file has no file identifier");
1564 else if (!checkCookieString(value, identifier))
1565 Error(ERR_WARN, "configuration file has wrong file identifier");
1569 /* ========================================================================= */
1570 /* setup file stuff */
1571 /* ========================================================================= */
1573 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1574 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1575 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1577 /* level directory info */
1578 #define LEVELINFO_TOKEN_IDENTIFIER 0
1579 #define LEVELINFO_TOKEN_NAME 1
1580 #define LEVELINFO_TOKEN_NAME_SORTING 2
1581 #define LEVELINFO_TOKEN_AUTHOR 3
1582 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1583 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1584 #define LEVELINFO_TOKEN_LEVELS 6
1585 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1586 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1587 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1588 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1589 #define LEVELINFO_TOKEN_READONLY 11
1590 #define LEVELINFO_TOKEN_GRAPHICS_SET 12
1591 #define LEVELINFO_TOKEN_SOUNDS_SET 13
1592 #define LEVELINFO_TOKEN_MUSIC_SET 14
1593 #define LEVELINFO_TOKEN_FILENAME 15
1594 #define LEVELINFO_TOKEN_FILETYPE 16
1595 #define LEVELINFO_TOKEN_HANDICAP 17
1596 #define LEVELINFO_TOKEN_SKIP_LEVELS 18
1598 #define NUM_LEVELINFO_TOKENS 19
1600 static LevelDirTree ldi;
1602 static struct TokenInfo levelinfo_tokens[] =
1604 /* level directory info */
1605 { TYPE_STRING, &ldi.identifier, "identifier" },
1606 { TYPE_STRING, &ldi.name, "name" },
1607 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1608 { TYPE_STRING, &ldi.author, "author" },
1609 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1610 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1611 { TYPE_INTEGER, &ldi.levels, "levels" },
1612 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1613 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1614 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1615 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1616 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1617 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1618 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1619 { TYPE_STRING, &ldi.music_set, "music_set" },
1620 { TYPE_STRING, &ldi.level_filename, "filename" },
1621 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1622 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1623 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1626 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1630 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1631 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1632 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1633 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1636 ldi->node_parent = NULL;
1637 ldi->node_group = NULL;
1641 ldi->cl_cursor = -1;
1644 ldi->fullpath = NULL;
1645 ldi->basepath = NULL;
1646 ldi->identifier = NULL;
1647 ldi->name = getStringCopy(ANONYMOUS_NAME);
1648 ldi->name_sorting = NULL;
1649 ldi->author = getStringCopy(ANONYMOUS_NAME);
1651 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1652 ldi->latest_engine = FALSE; /* default: get from level */
1653 ldi->parent_link = FALSE;
1654 ldi->in_user_dir = FALSE;
1655 ldi->user_defined = FALSE;
1657 ldi->class_desc = NULL;
1659 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1661 ldi->imported_from = NULL;
1662 ldi->imported_by = NULL;
1664 ldi->graphics_set = NULL;
1665 ldi->sounds_set = NULL;
1666 ldi->music_set = NULL;
1667 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1668 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1669 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1671 ldi->level_filename = NULL;
1672 ldi->level_filetype = NULL;
1675 ldi->first_level = 0;
1676 ldi->last_level = 0;
1677 ldi->level_group = FALSE;
1678 ldi->handicap_level = 0;
1679 ldi->readonly = TRUE;
1680 ldi->handicap = TRUE;
1681 ldi->skip_levels = FALSE;
1685 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1689 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1691 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1697 /* copy all values from the parent structure */
1699 ldi->type = parent->type;
1701 ldi->node_top = parent->node_top;
1702 ldi->node_parent = parent;
1703 ldi->node_group = NULL;
1707 ldi->cl_cursor = -1;
1710 ldi->fullpath = NULL;
1711 ldi->basepath = NULL;
1712 ldi->identifier = NULL;
1713 ldi->name = getStringCopy(ANONYMOUS_NAME);
1714 ldi->name_sorting = NULL;
1715 ldi->author = getStringCopy(parent->author);
1717 ldi->sort_priority = parent->sort_priority;
1718 ldi->latest_engine = parent->latest_engine;
1719 ldi->parent_link = FALSE;
1720 ldi->in_user_dir = parent->in_user_dir;
1721 ldi->user_defined = parent->user_defined;
1722 ldi->color = parent->color;
1723 ldi->class_desc = getStringCopy(parent->class_desc);
1725 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1727 ldi->imported_from = getStringCopy(parent->imported_from);
1728 ldi->imported_by = getStringCopy(parent->imported_by);
1730 ldi->graphics_set = NULL;
1731 ldi->sounds_set = NULL;
1732 ldi->music_set = NULL;
1733 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1734 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1735 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1737 ldi->level_filename = NULL;
1738 ldi->level_filetype = NULL;
1741 ldi->first_level = 0;
1742 ldi->last_level = 0;
1743 ldi->level_group = FALSE;
1744 ldi->handicap_level = 0;
1745 ldi->readonly = TRUE;
1746 ldi->handicap = TRUE;
1747 ldi->skip_levels = FALSE;
1752 /* first copy all values from the parent structure ... */
1755 /* ... then set all fields to default that cannot be inherited from parent.
1756 This is especially important for all those fields that can be set from
1757 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1758 calls 'free()' for all already set token values which requires that no
1759 other structure's pointer may point to them!
1763 ldi->fullpath = NULL;
1764 ldi->basepath = NULL;
1765 ldi->identifier = NULL;
1766 ldi->name = getStringCopy(ANONYMOUS_NAME);
1767 ldi->name_sorting = NULL;
1768 ldi->author = getStringCopy(parent->author);
1770 ldi->imported_from = getStringCopy(parent->imported_from);
1771 ldi->imported_by = getStringCopy(parent->imported_by);
1772 ldi->class_desc = getStringCopy(parent->class_desc);
1774 ldi->graphics_set = NULL;
1775 ldi->sounds_set = NULL;
1776 ldi->music_set = NULL;
1777 ldi->graphics_path = NULL;
1778 ldi->sounds_path = NULL;
1779 ldi->music_path = NULL;
1781 ldi->level_group = FALSE;
1782 ldi->parent_link = FALSE;
1784 ldi->node_top = parent->node_top;
1785 ldi->node_parent = parent;
1786 ldi->node_group = NULL;
1792 static void freeTreeInfo(TreeInfo *ldi)
1794 checked_free(ldi->subdir);
1795 checked_free(ldi->fullpath);
1796 checked_free(ldi->basepath);
1797 checked_free(ldi->identifier);
1799 checked_free(ldi->name);
1800 checked_free(ldi->name_sorting);
1801 checked_free(ldi->author);
1803 checked_free(ldi->class_desc);
1805 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1807 checked_free(ldi->imported_from);
1808 checked_free(ldi->imported_by);
1810 checked_free(ldi->graphics_set);
1811 checked_free(ldi->sounds_set);
1812 checked_free(ldi->music_set);
1814 checked_free(ldi->graphics_path);
1815 checked_free(ldi->sounds_path);
1816 checked_free(ldi->music_path);
1818 checked_free(ldi->level_filename);
1819 checked_free(ldi->level_filetype);
1823 void setSetupInfo(struct TokenInfo *token_info,
1824 int token_nr, char *token_value)
1826 int token_type = token_info[token_nr].type;
1827 void *setup_value = token_info[token_nr].value;
1829 if (token_value == NULL)
1832 /* set setup field to corresponding token value */
1837 *(boolean *)setup_value = get_boolean_from_string(token_value);
1841 *(Key *)setup_value = getKeyFromKeyName(token_value);
1845 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1849 *(int *)setup_value = get_integer_from_string(token_value);
1853 checked_free(*(char **)setup_value);
1854 *(char **)setup_value = getStringCopy(token_value);
1862 static int compareTreeInfoEntries(const void *object1, const void *object2)
1864 const TreeInfo *entry1 = *((TreeInfo **)object1);
1865 const TreeInfo *entry2 = *((TreeInfo **)object2);
1866 int class_sorting1, class_sorting2;
1869 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1871 class_sorting1 = LEVELSORTING(entry1);
1872 class_sorting2 = LEVELSORTING(entry2);
1876 class_sorting1 = ARTWORKSORTING(entry1);
1877 class_sorting2 = ARTWORKSORTING(entry2);
1880 if (entry1->parent_link || entry2->parent_link)
1881 compare_result = (entry1->parent_link ? -1 : +1);
1882 else if (entry1->sort_priority == entry2->sort_priority)
1884 char *name1 = getStringToLower(entry1->name_sorting);
1885 char *name2 = getStringToLower(entry2->name_sorting);
1887 compare_result = strcmp(name1, name2);
1892 else if (class_sorting1 == class_sorting2)
1893 compare_result = entry1->sort_priority - entry2->sort_priority;
1895 compare_result = class_sorting1 - class_sorting2;
1897 return compare_result;
1900 static void createParentTreeInfoNode(TreeInfo *node_parent)
1904 if (node_parent == NULL)
1907 ti_new = newTreeInfo();
1908 setTreeInfoToDefaults(ti_new, node_parent->type);
1910 ti_new->node_parent = node_parent;
1911 ti_new->parent_link = TRUE;
1914 setString(&ti_new->identifier, node_parent->identifier);
1915 setString(&ti_new->name, ".. (parent directory)");
1916 setString(&ti_new->name_sorting, ti_new->name);
1918 setString(&ti_new->subdir, "..");
1919 setString(&ti_new->fullpath, node_parent->fullpath);
1921 ti_new->sort_priority = node_parent->sort_priority;
1922 ti_new->latest_engine = node_parent->latest_engine;
1924 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1926 ti_new->identifier = getStringCopy(node_parent->identifier);
1927 ti_new->name = ".. (parent directory)";
1928 ti_new->name_sorting = getStringCopy(ti_new->name);
1930 ti_new->subdir = "..";
1931 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1933 ti_new->sort_priority = node_parent->sort_priority;
1934 ti_new->latest_engine = node_parent->latest_engine;
1936 ti_new->class_desc = getLevelClassDescription(ti_new);
1939 pushTreeInfo(&node_parent->node_group, ti_new);
1942 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1943 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1945 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1946 TreeInfo *node_parent,
1947 char *level_directory,
1948 char *directory_name)
1950 char *directory_path = getPath2(level_directory, directory_name);
1951 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1952 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1953 LevelDirTree *leveldir_new = NULL;
1956 if (setup_file_hash == NULL)
1958 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1960 free(directory_path);
1966 leveldir_new = newTreeInfo();
1969 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1971 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1973 leveldir_new->subdir = getStringCopy(directory_name);
1975 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1977 /* set all structure fields according to the token/value pairs */
1978 ldi = *leveldir_new;
1979 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1980 setSetupInfo(levelinfo_tokens, i,
1981 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1982 *leveldir_new = ldi;
1985 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1986 setString(&leveldir_new->name, leveldir_new->subdir);
1988 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1990 free(leveldir_new->name);
1991 leveldir_new->name = getStringCopy(leveldir_new->subdir);
1995 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1997 if (leveldir_new->identifier == NULL)
1998 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2000 if (leveldir_new->name_sorting == NULL)
2001 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2003 if (node_parent == NULL) /* top level group */
2005 leveldir_new->basepath = getStringCopy(level_directory);
2006 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2008 else /* sub level group */
2010 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2011 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2014 if (leveldir_new->levels < 1)
2015 leveldir_new->levels = 1;
2017 leveldir_new->last_level =
2018 leveldir_new->first_level + leveldir_new->levels - 1;
2021 leveldir_new->in_user_dir =
2022 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2024 leveldir_new->in_user_dir =
2025 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2028 leveldir_new->user_defined =
2029 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2031 leveldir_new->color = LEVELCOLOR(leveldir_new);
2033 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2035 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2038 leveldir_new->handicap_level = /* set handicap to default value */
2039 (leveldir_new->user_defined || !leveldir_new->handicap ?
2040 leveldir_new->last_level : leveldir_new->first_level);
2042 pushTreeInfo(node_first, leveldir_new);
2044 freeSetupFileHash(setup_file_hash);
2046 if (leveldir_new->level_group)
2048 /* create node to link back to current level directory */
2049 createParentTreeInfoNode(leveldir_new);
2051 /* step into sub-directory and look for more level series */
2052 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2053 leveldir_new, directory_path);
2056 free(directory_path);
2062 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2063 TreeInfo *node_parent,
2064 char *level_directory)
2067 struct dirent *dir_entry;
2068 boolean valid_entry_found = FALSE;
2070 if ((dir = opendir(level_directory)) == NULL)
2072 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2076 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2078 struct stat file_status;
2079 char *directory_name = dir_entry->d_name;
2080 char *directory_path = getPath2(level_directory, directory_name);
2082 /* skip entries for current and parent directory */
2083 if (strcmp(directory_name, ".") == 0 ||
2084 strcmp(directory_name, "..") == 0)
2086 free(directory_path);
2090 /* find out if directory entry is itself a directory */
2091 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2092 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2094 free(directory_path);
2098 free(directory_path);
2100 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2101 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2102 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2105 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2112 if (!valid_entry_found)
2114 /* check if this directory directly contains a file "levelinfo.conf" */
2115 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2116 level_directory, ".");
2119 if (!valid_entry_found)
2120 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2124 void LoadLevelInfo()
2126 InitUserLevelDirectory(getLoginName());
2128 DrawInitText("Loading level series:", 120, FC_GREEN);
2130 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2131 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2133 /* before sorting, the first entries will be from the user directory */
2134 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2136 if (leveldir_first == NULL)
2137 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2139 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2142 dumpTreeInfo(leveldir_first, 0);
2146 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2147 TreeInfo *node_parent,
2148 char *base_directory,
2149 char *directory_name, int type)
2151 char *directory_path = getPath2(base_directory, directory_name);
2152 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2153 SetupFileHash *setup_file_hash = NULL;
2154 TreeInfo *artwork_new = NULL;
2157 if (access(filename, F_OK) == 0) /* file exists */
2158 setup_file_hash = loadSetupFileHash(filename);
2160 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2163 struct dirent *dir_entry;
2164 boolean valid_file_found = FALSE;
2166 if ((dir = opendir(directory_path)) != NULL)
2168 while ((dir_entry = readdir(dir)) != NULL)
2170 char *entry_name = dir_entry->d_name;
2172 if (FileIsArtworkType(entry_name, type))
2174 valid_file_found = TRUE;
2182 if (!valid_file_found)
2184 if (strcmp(directory_name, ".") != 0)
2185 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2187 free(directory_path);
2194 artwork_new = newTreeInfo();
2197 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2199 setTreeInfoToDefaults(artwork_new, type);
2201 artwork_new->subdir = getStringCopy(directory_name);
2203 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2206 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2209 /* set all structure fields according to the token/value pairs */
2211 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2212 setSetupInfo(levelinfo_tokens, i,
2213 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2217 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2218 setString(&artwork_new->name, artwork_new->subdir);
2220 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2222 free(artwork_new->name);
2223 artwork_new->name = getStringCopy(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);
2250 artwork_new->in_user_dir =
2251 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2253 artwork_new->in_user_dir =
2254 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2257 /* (may use ".sort_priority" from "setup_file_hash" above) */
2258 artwork_new->color = ARTWORKCOLOR(artwork_new);
2260 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2262 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2265 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2268 if (artwork_new->name != NULL)
2270 free(artwork_new->name);
2271 artwork_new->name = NULL;
2276 if (artwork_new->identifier != NULL)
2278 free(artwork_new->identifier);
2279 artwork_new->identifier = NULL;
2283 if (strcmp(artwork_new->subdir, ".") == 0)
2285 if (artwork_new->user_defined)
2288 setString(&artwork_new->identifier, "private");
2290 artwork_new->identifier = getStringCopy("private");
2292 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2297 setString(&artwork_new->identifier, "classic");
2299 artwork_new->identifier = getStringCopy("classic");
2301 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2304 /* set to new values after changing ".sort_priority" */
2305 artwork_new->color = ARTWORKCOLOR(artwork_new);
2307 setString(&artwork_new->class_desc,
2308 getLevelClassDescription(artwork_new));
2310 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2316 setString(&artwork_new->identifier, artwork_new->subdir);
2318 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2323 setString(&artwork_new->name, artwork_new->identifier);
2324 setString(&artwork_new->name_sorting, artwork_new->name);
2326 artwork_new->name = getStringCopy(artwork_new->identifier);
2327 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2331 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2333 pushTreeInfo(node_first, artwork_new);
2335 freeSetupFileHash(setup_file_hash);
2337 free(directory_path);
2343 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2344 TreeInfo *node_parent,
2345 char *base_directory, int type)
2348 struct dirent *dir_entry;
2349 boolean valid_entry_found = FALSE;
2351 if ((dir = opendir(base_directory)) == NULL)
2353 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2354 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2358 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2360 struct stat file_status;
2361 char *directory_name = dir_entry->d_name;
2362 char *directory_path = getPath2(base_directory, directory_name);
2364 /* skip entries for current and parent directory */
2365 if (strcmp(directory_name, ".") == 0 ||
2366 strcmp(directory_name, "..") == 0)
2368 free(directory_path);
2372 /* find out if directory entry is itself a directory */
2373 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2374 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2376 free(directory_path);
2380 free(directory_path);
2382 /* check if this directory contains artwork with or without config file */
2383 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2385 directory_name, type);
2390 /* check if this directory directly contains artwork itself */
2391 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2392 base_directory, ".",
2394 if (!valid_entry_found)
2395 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2399 static TreeInfo *getDummyArtworkInfo(int type)
2401 /* this is only needed when there is completely no artwork available */
2402 TreeInfo *artwork_new = newTreeInfo();
2404 setTreeInfoToDefaults(artwork_new, type);
2407 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2408 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2409 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2411 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2412 setString(&artwork_new->name, UNDEFINED_FILENAME);
2413 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2415 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2416 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2417 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2419 checked_free(artwork_new->name);
2421 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2422 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2423 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2429 void LoadArtworkInfo()
2431 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2433 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2434 options.graphics_directory,
2435 TREE_TYPE_GRAPHICS_DIR);
2436 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2437 getUserGraphicsDir(),
2438 TREE_TYPE_GRAPHICS_DIR);
2440 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2441 options.sounds_directory,
2442 TREE_TYPE_SOUNDS_DIR);
2443 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2445 TREE_TYPE_SOUNDS_DIR);
2447 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2448 options.music_directory,
2449 TREE_TYPE_MUSIC_DIR);
2450 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2452 TREE_TYPE_MUSIC_DIR);
2454 if (artwork.gfx_first == NULL)
2455 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2456 if (artwork.snd_first == NULL)
2457 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2458 if (artwork.mus_first == NULL)
2459 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2461 /* before sorting, the first entries will be from the user directory */
2462 artwork.gfx_current =
2463 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2464 if (artwork.gfx_current == NULL)
2465 artwork.gfx_current =
2466 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2467 if (artwork.gfx_current == NULL)
2468 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2470 artwork.snd_current =
2471 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2472 if (artwork.snd_current == NULL)
2473 artwork.snd_current =
2474 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2475 if (artwork.snd_current == NULL)
2476 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2478 artwork.mus_current =
2479 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2480 if (artwork.mus_current == NULL)
2481 artwork.mus_current =
2482 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2483 if (artwork.mus_current == NULL)
2484 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2486 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2487 artwork.snd_current_identifier = artwork.snd_current->identifier;
2488 artwork.mus_current_identifier = artwork.mus_current->identifier;
2491 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2492 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2493 printf("music set == %s\n\n", artwork.mus_current_identifier);
2496 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2497 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2498 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2501 dumpTreeInfo(artwork.gfx_first, 0);
2502 dumpTreeInfo(artwork.snd_first, 0);
2503 dumpTreeInfo(artwork.mus_first, 0);
2507 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2508 LevelDirTree *level_node)
2510 /* recursively check all level directories for artwork sub-directories */
2514 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2515 ARTWORK_DIRECTORY((*artwork_node)->type));
2518 if (!level_node->parent_link)
2519 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2520 level_node->subdir, level_node->name);
2523 if (!level_node->parent_link)
2525 TreeInfo *topnode_last = *artwork_node;
2527 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2528 (*artwork_node)->type);
2530 if (topnode_last != *artwork_node)
2532 free((*artwork_node)->identifier);
2533 free((*artwork_node)->name);
2534 free((*artwork_node)->name_sorting);
2536 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2537 (*artwork_node)->name = getStringCopy(level_node->name);
2538 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2540 (*artwork_node)->sort_priority = level_node->sort_priority;
2541 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2547 if (level_node->node_group != NULL)
2548 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2550 level_node = level_node->next;
2554 void LoadLevelArtworkInfo()
2556 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2558 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2559 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2560 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2562 /* needed for reloading level artwork not known at ealier stage */
2564 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2566 artwork.gfx_current =
2567 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2568 if (artwork.gfx_current == NULL)
2569 artwork.gfx_current =
2570 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2571 if (artwork.gfx_current == NULL)
2572 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2575 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2577 artwork.snd_current =
2578 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2579 if (artwork.snd_current == NULL)
2580 artwork.snd_current =
2581 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2582 if (artwork.snd_current == NULL)
2583 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2586 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2588 artwork.mus_current =
2589 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2590 if (artwork.mus_current == NULL)
2591 artwork.mus_current =
2592 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2593 if (artwork.mus_current == NULL)
2594 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2597 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2598 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2599 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2602 dumpTreeInfo(artwork.gfx_first, 0);
2603 dumpTreeInfo(artwork.snd_first, 0);
2604 dumpTreeInfo(artwork.mus_first, 0);
2608 static void SaveUserLevelInfo()
2610 LevelDirTree *level_info;
2615 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2617 if (!(file = fopen(filename, MODE_WRITE)))
2619 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2624 level_info = newTreeInfo();
2626 /* always start with reliable default values */
2627 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2630 setString(&level_info->name, getLoginName());
2631 setString(&level_info->author, getRealName());
2632 level_info->levels = 100;
2633 level_info->first_level = 1;
2634 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2635 level_info->readonly = FALSE;
2636 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2637 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2638 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2640 ldi.name = getStringCopy(getLoginName());
2641 ldi.author = getStringCopy(getRealName());
2643 ldi.first_level = 1;
2644 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2645 ldi.readonly = FALSE;
2646 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2647 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2648 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2651 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2652 getCookie("LEVELINFO")));
2655 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2656 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2657 i != LEVELINFO_TOKEN_NAME_SORTING &&
2658 i != LEVELINFO_TOKEN_IMPORTED_FROM &&
2659 i != LEVELINFO_TOKEN_IMPORTED_BY &&
2660 i != LEVELINFO_TOKEN_FILENAME &&
2661 i != LEVELINFO_TOKEN_FILETYPE)
2662 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2666 SetFilePermissions(filename, PERMS_PRIVATE);
2668 freeTreeInfo(level_info);
2672 char *getSetupValue(int type, void *value)
2674 static char value_string[MAX_LINE_LEN];
2682 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2686 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2690 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2694 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2698 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2702 sprintf(value_string, "%d", *(int *)value);
2706 strcpy(value_string, *(char **)value);
2710 value_string[0] = '\0';
2714 return value_string;
2717 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2721 static char token_string[MAX_LINE_LEN];
2722 int token_type = token_info[token_nr].type;
2723 void *setup_value = token_info[token_nr].value;
2724 char *token_text = token_info[token_nr].text;
2725 char *value_string = getSetupValue(token_type, setup_value);
2727 /* build complete token string */
2728 sprintf(token_string, "%s%s", prefix, token_text);
2730 /* build setup entry line */
2731 line = getFormattedSetupEntry(token_string, value_string);
2733 if (token_type == TYPE_KEY_X11)
2735 Key key = *(Key *)setup_value;
2736 char *keyname = getKeyNameFromKey(key);
2738 /* add comment, if useful */
2739 if (strcmp(keyname, "(undefined)") != 0 &&
2740 strcmp(keyname, "(unknown)") != 0)
2742 /* add at least one whitespace */
2744 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2748 strcat(line, keyname);
2755 void LoadLevelSetup_LastSeries()
2757 /* ----------------------------------------------------------------------- */
2758 /* ~/.<program>/levelsetup.conf */
2759 /* ----------------------------------------------------------------------- */
2761 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2762 SetupFileHash *level_setup_hash = NULL;
2764 /* always start with reliable default values */
2765 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2767 if ((level_setup_hash = loadSetupFileHash(filename)))
2769 char *last_level_series =
2770 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2772 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2774 if (leveldir_current == NULL)
2775 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2777 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2779 freeSetupFileHash(level_setup_hash);
2782 Error(ERR_WARN, "using default setup values");
2787 void SaveLevelSetup_LastSeries()
2789 /* ----------------------------------------------------------------------- */
2790 /* ~/.<program>/levelsetup.conf */
2791 /* ----------------------------------------------------------------------- */
2793 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2794 char *level_subdir = leveldir_current->subdir;
2797 InitUserDataDirectory();
2799 if (!(file = fopen(filename, MODE_WRITE)))
2801 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2806 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2807 getCookie("LEVELSETUP")));
2808 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2813 SetFilePermissions(filename, PERMS_PRIVATE);
2818 static void checkSeriesInfo()
2820 static char *level_directory = NULL;
2822 struct dirent *dir_entry;
2824 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2826 level_directory = getPath2((leveldir_current->in_user_dir ?
2827 getUserLevelDir(NULL) :
2828 options.level_directory),
2829 leveldir_current->fullpath);
2831 if ((dir = opendir(level_directory)) == NULL)
2833 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2837 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2839 if (strlen(dir_entry->d_name) > 4 &&
2840 dir_entry->d_name[3] == '.' &&
2841 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2843 char levelnum_str[4];
2846 strncpy(levelnum_str, dir_entry->d_name, 3);
2847 levelnum_str[3] = '\0';
2849 levelnum_value = atoi(levelnum_str);
2852 if (levelnum_value < leveldir_current->first_level)
2854 Error(ERR_WARN, "additional level %d found", levelnum_value);
2855 leveldir_current->first_level = levelnum_value;
2857 else if (levelnum_value > leveldir_current->last_level)
2859 Error(ERR_WARN, "additional level %d found", levelnum_value);
2860 leveldir_current->last_level = levelnum_value;
2869 void LoadLevelSetup_SeriesInfo()
2872 SetupFileHash *level_setup_hash = NULL;
2873 char *level_subdir = leveldir_current->subdir;
2875 /* always start with reliable default values */
2876 level_nr = leveldir_current->first_level;
2878 checkSeriesInfo(leveldir_current);
2880 /* ----------------------------------------------------------------------- */
2881 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2882 /* ----------------------------------------------------------------------- */
2884 level_subdir = leveldir_current->subdir;
2886 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2888 if ((level_setup_hash = loadSetupFileHash(filename)))
2892 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2896 level_nr = atoi(token_value);
2898 if (level_nr < leveldir_current->first_level)
2899 level_nr = leveldir_current->first_level;
2900 if (level_nr > leveldir_current->last_level)
2901 level_nr = leveldir_current->last_level;
2904 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2908 int level_nr = atoi(token_value);
2910 if (level_nr < leveldir_current->first_level)
2911 level_nr = leveldir_current->first_level;
2912 if (level_nr > leveldir_current->last_level + 1)
2913 level_nr = leveldir_current->last_level;
2915 if (leveldir_current->user_defined || !leveldir_current->handicap)
2916 level_nr = leveldir_current->last_level;
2918 leveldir_current->handicap_level = level_nr;
2921 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2923 freeSetupFileHash(level_setup_hash);
2926 Error(ERR_WARN, "using default setup values");
2931 void SaveLevelSetup_SeriesInfo()
2934 char *level_subdir = leveldir_current->subdir;
2935 char *level_nr_str = int2str(level_nr, 0);
2936 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2939 /* ----------------------------------------------------------------------- */
2940 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2941 /* ----------------------------------------------------------------------- */
2943 InitLevelSetupDirectory(level_subdir);
2945 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2947 if (!(file = fopen(filename, MODE_WRITE)))
2949 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2954 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2955 getCookie("LEVELSETUP")));
2956 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2958 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2959 handicap_level_str));
2963 SetFilePermissions(filename, PERMS_PRIVATE);