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->user_defined ? 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->user_defined);
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
1597 #define NUM_LEVELINFO_TOKENS 18
1599 static LevelDirTree ldi;
1601 static struct TokenInfo levelinfo_tokens[] =
1603 /* level directory info */
1604 { TYPE_STRING, &ldi.identifier, "identifier" },
1605 { TYPE_STRING, &ldi.name, "name" },
1606 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1607 { TYPE_STRING, &ldi.author, "author" },
1608 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1609 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1610 { TYPE_INTEGER, &ldi.levels, "levels" },
1611 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1612 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1613 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1614 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1615 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1616 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1617 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1618 { TYPE_STRING, &ldi.music_set, "music_set" },
1619 { TYPE_STRING, &ldi.level_filename, "filename" },
1620 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1621 { TYPE_BOOLEAN, &ldi.handicap, "handicap" }
1624 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1628 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1629 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1630 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1631 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1634 ldi->node_parent = NULL;
1635 ldi->node_group = NULL;
1639 ldi->cl_cursor = -1;
1642 ldi->fullpath = NULL;
1643 ldi->basepath = NULL;
1644 ldi->identifier = NULL;
1645 ldi->name = getStringCopy(ANONYMOUS_NAME);
1646 ldi->name_sorting = NULL;
1647 ldi->author = getStringCopy(ANONYMOUS_NAME);
1649 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1650 ldi->latest_engine = FALSE; /* default: get from level */
1651 ldi->parent_link = FALSE;
1652 ldi->user_defined = FALSE;
1654 ldi->class_desc = NULL;
1656 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1658 ldi->imported_from = NULL;
1659 ldi->imported_by = NULL;
1661 ldi->graphics_set = NULL;
1662 ldi->sounds_set = NULL;
1663 ldi->music_set = NULL;
1664 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1665 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1666 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1668 ldi->level_filename = NULL;
1669 ldi->level_filetype = NULL;
1672 ldi->first_level = 0;
1673 ldi->last_level = 0;
1674 ldi->level_group = FALSE;
1675 ldi->handicap_level = 0;
1676 ldi->readonly = TRUE;
1677 ldi->handicap = TRUE;
1681 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1685 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1687 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1693 /* copy all values from the parent structure */
1695 ldi->type = parent->type;
1697 ldi->node_top = parent->node_top;
1698 ldi->node_parent = parent;
1699 ldi->node_group = NULL;
1703 ldi->cl_cursor = -1;
1706 ldi->fullpath = NULL;
1707 ldi->basepath = NULL;
1708 ldi->identifier = NULL;
1709 ldi->name = getStringCopy(ANONYMOUS_NAME);
1710 ldi->name_sorting = NULL;
1711 ldi->author = getStringCopy(parent->author);
1713 ldi->sort_priority = parent->sort_priority;
1714 ldi->latest_engine = parent->latest_engine;
1715 ldi->parent_link = FALSE;
1716 ldi->user_defined = parent->user_defined;
1717 ldi->color = parent->color;
1718 ldi->class_desc = getStringCopy(parent->class_desc);
1720 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1722 ldi->imported_from = getStringCopy(parent->imported_from);
1723 ldi->imported_by = getStringCopy(parent->imported_by);
1725 ldi->graphics_set = NULL;
1726 ldi->sounds_set = NULL;
1727 ldi->music_set = NULL;
1728 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1729 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1730 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1732 ldi->level_filename = NULL;
1733 ldi->level_filetype = NULL;
1736 ldi->first_level = 0;
1737 ldi->last_level = 0;
1738 ldi->level_group = FALSE;
1739 ldi->handicap_level = 0;
1740 ldi->readonly = TRUE;
1741 ldi->handicap = TRUE;
1746 /* first copy all values from the parent structure ... */
1749 /* ... then set all fields to default that cannot be inherited from parent.
1750 This is especially important for all those fields that can be set from
1751 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1752 calls 'free()' for all already set token values which requires that no
1753 other structure's pointer may point to them!
1757 ldi->fullpath = NULL;
1758 ldi->basepath = NULL;
1759 ldi->identifier = NULL;
1760 ldi->name = getStringCopy(ANONYMOUS_NAME);
1761 ldi->name_sorting = NULL;
1762 ldi->author = getStringCopy(parent->author);
1764 ldi->imported_from = getStringCopy(parent->imported_from);
1765 ldi->imported_by = getStringCopy(parent->imported_by);
1766 ldi->class_desc = getStringCopy(parent->class_desc);
1768 ldi->graphics_set = NULL;
1769 ldi->sounds_set = NULL;
1770 ldi->music_set = NULL;
1771 ldi->graphics_path = NULL;
1772 ldi->sounds_path = NULL;
1773 ldi->music_path = NULL;
1775 ldi->level_group = FALSE;
1776 ldi->parent_link = FALSE;
1778 ldi->node_top = parent->node_top;
1779 ldi->node_parent = parent;
1780 ldi->node_group = NULL;
1786 static void freeTreeInfo(TreeInfo *ldi)
1788 checked_free(ldi->subdir);
1789 checked_free(ldi->fullpath);
1790 checked_free(ldi->basepath);
1791 checked_free(ldi->identifier);
1793 checked_free(ldi->name);
1794 checked_free(ldi->name_sorting);
1795 checked_free(ldi->author);
1797 checked_free(ldi->class_desc);
1799 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1801 checked_free(ldi->imported_from);
1802 checked_free(ldi->imported_by);
1804 checked_free(ldi->graphics_set);
1805 checked_free(ldi->sounds_set);
1806 checked_free(ldi->music_set);
1808 checked_free(ldi->graphics_path);
1809 checked_free(ldi->sounds_path);
1810 checked_free(ldi->music_path);
1812 checked_free(ldi->level_filename);
1813 checked_free(ldi->level_filetype);
1817 void setSetupInfo(struct TokenInfo *token_info,
1818 int token_nr, char *token_value)
1820 int token_type = token_info[token_nr].type;
1821 void *setup_value = token_info[token_nr].value;
1823 if (token_value == NULL)
1826 /* set setup field to corresponding token value */
1831 *(boolean *)setup_value = get_boolean_from_string(token_value);
1835 *(Key *)setup_value = getKeyFromKeyName(token_value);
1839 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1843 *(int *)setup_value = get_integer_from_string(token_value);
1847 checked_free(*(char **)setup_value);
1848 *(char **)setup_value = getStringCopy(token_value);
1856 static int compareTreeInfoEntries(const void *object1, const void *object2)
1858 const TreeInfo *entry1 = *((TreeInfo **)object1);
1859 const TreeInfo *entry2 = *((TreeInfo **)object2);
1860 int class_sorting1, class_sorting2;
1863 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1865 class_sorting1 = LEVELSORTING(entry1);
1866 class_sorting2 = LEVELSORTING(entry2);
1870 class_sorting1 = ARTWORKSORTING(entry1);
1871 class_sorting2 = ARTWORKSORTING(entry2);
1874 if (entry1->parent_link || entry2->parent_link)
1875 compare_result = (entry1->parent_link ? -1 : +1);
1876 else if (entry1->sort_priority == entry2->sort_priority)
1878 char *name1 = getStringToLower(entry1->name_sorting);
1879 char *name2 = getStringToLower(entry2->name_sorting);
1881 compare_result = strcmp(name1, name2);
1886 else if (class_sorting1 == class_sorting2)
1887 compare_result = entry1->sort_priority - entry2->sort_priority;
1889 compare_result = class_sorting1 - class_sorting2;
1891 return compare_result;
1894 static void createParentTreeInfoNode(TreeInfo *node_parent)
1898 if (node_parent == NULL)
1901 ti_new = newTreeInfo();
1902 setTreeInfoToDefaults(ti_new, node_parent->type);
1904 ti_new->node_parent = node_parent;
1905 ti_new->parent_link = TRUE;
1908 setString(&ti_new->identifier, node_parent->identifier);
1909 setString(&ti_new->name, ".. (parent directory)");
1910 setString(&ti_new->name_sorting, ti_new->name);
1912 setString(&ti_new->subdir, "..");
1913 setString(&ti_new->fullpath, node_parent->fullpath);
1915 ti_new->sort_priority = node_parent->sort_priority;
1916 ti_new->latest_engine = node_parent->latest_engine;
1918 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1920 ti_new->identifier = getStringCopy(node_parent->identifier);
1921 ti_new->name = ".. (parent directory)";
1922 ti_new->name_sorting = getStringCopy(ti_new->name);
1924 ti_new->subdir = "..";
1925 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1927 ti_new->sort_priority = node_parent->sort_priority;
1928 ti_new->latest_engine = node_parent->latest_engine;
1930 ti_new->class_desc = getLevelClassDescription(ti_new);
1933 pushTreeInfo(&node_parent->node_group, ti_new);
1936 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1937 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1939 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1940 TreeInfo *node_parent,
1941 char *level_directory,
1942 char *directory_name)
1944 char *directory_path = getPath2(level_directory, directory_name);
1945 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1946 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1947 LevelDirTree *leveldir_new = NULL;
1950 if (setup_file_hash == NULL)
1952 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1954 free(directory_path);
1960 leveldir_new = newTreeInfo();
1963 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1965 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1967 leveldir_new->subdir = getStringCopy(directory_name);
1969 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1971 /* set all structure fields according to the token/value pairs */
1972 ldi = *leveldir_new;
1973 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1974 setSetupInfo(levelinfo_tokens, i,
1975 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1976 *leveldir_new = ldi;
1979 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1980 setString(&leveldir_new->name, leveldir_new->subdir);
1982 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1984 free(leveldir_new->name);
1985 leveldir_new->name = getStringCopy(leveldir_new->subdir);
1989 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1991 if (leveldir_new->identifier == NULL)
1992 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
1994 if (leveldir_new->name_sorting == NULL)
1995 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1997 if (node_parent == NULL) /* top level group */
1999 leveldir_new->basepath = getStringCopy(level_directory);
2000 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2002 else /* sub level group */
2004 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2005 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2008 if (leveldir_new->levels < 1)
2009 leveldir_new->levels = 1;
2011 leveldir_new->last_level =
2012 leveldir_new->first_level + leveldir_new->levels - 1;
2015 leveldir_new->user_defined =
2016 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2018 leveldir_new->user_defined =
2019 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2022 leveldir_new->color = LEVELCOLOR(leveldir_new);
2024 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2026 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2029 leveldir_new->handicap_level = /* set handicap to default value */
2030 (leveldir_new->user_defined || !leveldir_new->handicap ?
2031 leveldir_new->last_level : leveldir_new->first_level);
2033 pushTreeInfo(node_first, leveldir_new);
2035 freeSetupFileHash(setup_file_hash);
2037 if (leveldir_new->level_group)
2039 /* create node to link back to current level directory */
2040 createParentTreeInfoNode(leveldir_new);
2042 /* step into sub-directory and look for more level series */
2043 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2044 leveldir_new, directory_path);
2047 free(directory_path);
2053 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2054 TreeInfo *node_parent,
2055 char *level_directory)
2058 struct dirent *dir_entry;
2059 boolean valid_entry_found = FALSE;
2061 if ((dir = opendir(level_directory)) == NULL)
2063 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2067 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2069 struct stat file_status;
2070 char *directory_name = dir_entry->d_name;
2071 char *directory_path = getPath2(level_directory, directory_name);
2073 /* skip entries for current and parent directory */
2074 if (strcmp(directory_name, ".") == 0 ||
2075 strcmp(directory_name, "..") == 0)
2077 free(directory_path);
2081 /* find out if directory entry is itself a directory */
2082 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2083 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2085 free(directory_path);
2089 free(directory_path);
2091 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2092 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2093 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2096 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2103 if (!valid_entry_found)
2105 /* check if this directory directly contains a file "levelinfo.conf" */
2106 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2107 level_directory, ".");
2110 if (!valid_entry_found)
2111 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2115 void LoadLevelInfo()
2117 InitUserLevelDirectory(getLoginName());
2119 DrawInitText("Loading level series:", 120, FC_GREEN);
2121 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2122 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2124 /* before sorting, the first entries will be from the user directory */
2125 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2127 if (leveldir_first == NULL)
2128 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2130 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2133 dumpTreeInfo(leveldir_first, 0);
2137 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2138 TreeInfo *node_parent,
2139 char *base_directory,
2140 char *directory_name, int type)
2142 char *directory_path = getPath2(base_directory, directory_name);
2143 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2144 SetupFileHash *setup_file_hash = NULL;
2145 TreeInfo *artwork_new = NULL;
2148 if (access(filename, F_OK) == 0) /* file exists */
2149 setup_file_hash = loadSetupFileHash(filename);
2151 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2154 struct dirent *dir_entry;
2155 boolean valid_file_found = FALSE;
2157 if ((dir = opendir(directory_path)) != NULL)
2159 while ((dir_entry = readdir(dir)) != NULL)
2161 char *entry_name = dir_entry->d_name;
2163 if (FileIsArtworkType(entry_name, type))
2165 valid_file_found = TRUE;
2173 if (!valid_file_found)
2175 if (strcmp(directory_name, ".") != 0)
2176 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2178 free(directory_path);
2185 artwork_new = newTreeInfo();
2188 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2190 setTreeInfoToDefaults(artwork_new, type);
2192 artwork_new->subdir = getStringCopy(directory_name);
2194 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2197 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2200 /* set all structure fields according to the token/value pairs */
2202 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2203 setSetupInfo(levelinfo_tokens, i,
2204 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2208 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2209 setString(&artwork_new->name, artwork_new->subdir);
2211 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2213 free(artwork_new->name);
2214 artwork_new->name = getStringCopy(artwork_new->subdir);
2219 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2222 if (artwork_new->identifier == NULL)
2223 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2225 if (artwork_new->name_sorting == NULL)
2226 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2229 if (node_parent == NULL) /* top level group */
2231 artwork_new->basepath = getStringCopy(base_directory);
2232 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2234 else /* sub level group */
2236 artwork_new->basepath = getStringCopy(node_parent->basepath);
2237 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2241 artwork_new->user_defined =
2242 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2244 artwork_new->user_defined =
2245 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2248 /* (may use ".sort_priority" from "setup_file_hash" above) */
2249 artwork_new->color = ARTWORKCOLOR(artwork_new);
2251 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2253 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2256 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2259 if (artwork_new->name != NULL)
2261 free(artwork_new->name);
2262 artwork_new->name = NULL;
2267 if (artwork_new->identifier != NULL)
2269 free(artwork_new->identifier);
2270 artwork_new->identifier = NULL;
2274 if (strcmp(artwork_new->subdir, ".") == 0)
2276 if (artwork_new->user_defined)
2279 setString(&artwork_new->identifier, "private");
2281 artwork_new->identifier = getStringCopy("private");
2283 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2288 setString(&artwork_new->identifier, "classic");
2290 artwork_new->identifier = getStringCopy("classic");
2292 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2295 /* set to new values after changing ".sort_priority" */
2296 artwork_new->color = ARTWORKCOLOR(artwork_new);
2298 setString(&artwork_new->class_desc,
2299 getLevelClassDescription(artwork_new));
2301 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2307 setString(&artwork_new->identifier, artwork_new->subdir);
2309 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2314 setString(&artwork_new->name, artwork_new->identifier);
2315 setString(&artwork_new->name_sorting, artwork_new->name);
2317 artwork_new->name = getStringCopy(artwork_new->identifier);
2318 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2322 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2324 pushTreeInfo(node_first, artwork_new);
2326 freeSetupFileHash(setup_file_hash);
2328 free(directory_path);
2334 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2335 TreeInfo *node_parent,
2336 char *base_directory, int type)
2339 struct dirent *dir_entry;
2340 boolean valid_entry_found = FALSE;
2342 if ((dir = opendir(base_directory)) == NULL)
2344 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2345 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2349 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2351 struct stat file_status;
2352 char *directory_name = dir_entry->d_name;
2353 char *directory_path = getPath2(base_directory, directory_name);
2355 /* skip entries for current and parent directory */
2356 if (strcmp(directory_name, ".") == 0 ||
2357 strcmp(directory_name, "..") == 0)
2359 free(directory_path);
2363 /* find out if directory entry is itself a directory */
2364 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2365 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2367 free(directory_path);
2371 free(directory_path);
2373 /* check if this directory contains artwork with or without config file */
2374 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2376 directory_name, type);
2381 /* check if this directory directly contains artwork itself */
2382 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2383 base_directory, ".",
2385 if (!valid_entry_found)
2386 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2390 static TreeInfo *getDummyArtworkInfo(int type)
2392 /* this is only needed when there is completely no artwork available */
2393 TreeInfo *artwork_new = newTreeInfo();
2395 setTreeInfoToDefaults(artwork_new, type);
2398 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2399 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2400 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2402 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2403 setString(&artwork_new->name, UNDEFINED_FILENAME);
2404 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2406 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2407 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2408 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2410 checked_free(artwork_new->name);
2412 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2413 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2414 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2420 void LoadArtworkInfo()
2422 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2424 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2425 options.graphics_directory,
2426 TREE_TYPE_GRAPHICS_DIR);
2427 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2428 getUserGraphicsDir(),
2429 TREE_TYPE_GRAPHICS_DIR);
2431 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2432 options.sounds_directory,
2433 TREE_TYPE_SOUNDS_DIR);
2434 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2436 TREE_TYPE_SOUNDS_DIR);
2438 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2439 options.music_directory,
2440 TREE_TYPE_MUSIC_DIR);
2441 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2443 TREE_TYPE_MUSIC_DIR);
2445 if (artwork.gfx_first == NULL)
2446 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2447 if (artwork.snd_first == NULL)
2448 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2449 if (artwork.mus_first == NULL)
2450 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2452 /* before sorting, the first entries will be from the user directory */
2453 artwork.gfx_current =
2454 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2455 if (artwork.gfx_current == NULL)
2456 artwork.gfx_current =
2457 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2458 if (artwork.gfx_current == NULL)
2459 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2461 artwork.snd_current =
2462 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2463 if (artwork.snd_current == NULL)
2464 artwork.snd_current =
2465 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2466 if (artwork.snd_current == NULL)
2467 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2469 artwork.mus_current =
2470 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2471 if (artwork.mus_current == NULL)
2472 artwork.mus_current =
2473 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2474 if (artwork.mus_current == NULL)
2475 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2477 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2478 artwork.snd_current_identifier = artwork.snd_current->identifier;
2479 artwork.mus_current_identifier = artwork.mus_current->identifier;
2482 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2483 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2484 printf("music set == %s\n\n", artwork.mus_current_identifier);
2487 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2488 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2489 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2492 dumpTreeInfo(artwork.gfx_first, 0);
2493 dumpTreeInfo(artwork.snd_first, 0);
2494 dumpTreeInfo(artwork.mus_first, 0);
2498 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2499 LevelDirTree *level_node)
2501 /* recursively check all level directories for artwork sub-directories */
2505 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2506 ARTWORK_DIRECTORY((*artwork_node)->type));
2509 if (!level_node->parent_link)
2510 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2511 level_node->subdir, level_node->name);
2514 if (!level_node->parent_link)
2516 TreeInfo *topnode_last = *artwork_node;
2518 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2519 (*artwork_node)->type);
2521 if (topnode_last != *artwork_node)
2523 free((*artwork_node)->identifier);
2524 free((*artwork_node)->name);
2525 free((*artwork_node)->name_sorting);
2527 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2528 (*artwork_node)->name = getStringCopy(level_node->name);
2529 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2531 (*artwork_node)->sort_priority = level_node->sort_priority;
2532 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2538 if (level_node->node_group != NULL)
2539 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2541 level_node = level_node->next;
2545 void LoadLevelArtworkInfo()
2547 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2549 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2550 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2551 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2553 /* needed for reloading level artwork not known at ealier stage */
2555 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2557 artwork.gfx_current =
2558 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2559 if (artwork.gfx_current == NULL)
2560 artwork.gfx_current =
2561 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2562 if (artwork.gfx_current == NULL)
2563 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2566 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2568 artwork.snd_current =
2569 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2570 if (artwork.snd_current == NULL)
2571 artwork.snd_current =
2572 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2573 if (artwork.snd_current == NULL)
2574 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2577 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2579 artwork.mus_current =
2580 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2581 if (artwork.mus_current == NULL)
2582 artwork.mus_current =
2583 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2584 if (artwork.mus_current == NULL)
2585 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2588 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2589 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2590 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2593 dumpTreeInfo(artwork.gfx_first, 0);
2594 dumpTreeInfo(artwork.snd_first, 0);
2595 dumpTreeInfo(artwork.mus_first, 0);
2599 static void SaveUserLevelInfo()
2601 LevelDirTree *level_info;
2606 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2608 if (!(file = fopen(filename, MODE_WRITE)))
2610 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2615 level_info = newTreeInfo();
2617 /* always start with reliable default values */
2618 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2621 setString(&level_info->name, getLoginName());
2622 setString(&level_info->author, getRealName());
2623 level_info->levels = 100;
2624 level_info->first_level = 1;
2625 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2626 level_info->readonly = FALSE;
2627 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2628 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2629 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2631 ldi.name = getStringCopy(getLoginName());
2632 ldi.author = getStringCopy(getRealName());
2634 ldi.first_level = 1;
2635 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2636 ldi.readonly = FALSE;
2637 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2638 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2639 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2642 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2643 getCookie("LEVELINFO")));
2646 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2647 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2648 i != LEVELINFO_TOKEN_NAME_SORTING &&
2649 i != LEVELINFO_TOKEN_IMPORTED_FROM &&
2650 i != LEVELINFO_TOKEN_IMPORTED_BY &&
2651 i != LEVELINFO_TOKEN_FILENAME &&
2652 i != LEVELINFO_TOKEN_FILETYPE)
2653 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2657 SetFilePermissions(filename, PERMS_PRIVATE);
2659 freeTreeInfo(level_info);
2663 char *getSetupValue(int type, void *value)
2665 static char value_string[MAX_LINE_LEN];
2673 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2677 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2681 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2685 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2689 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2693 sprintf(value_string, "%d", *(int *)value);
2697 strcpy(value_string, *(char **)value);
2701 value_string[0] = '\0';
2705 return value_string;
2708 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2712 static char token_string[MAX_LINE_LEN];
2713 int token_type = token_info[token_nr].type;
2714 void *setup_value = token_info[token_nr].value;
2715 char *token_text = token_info[token_nr].text;
2716 char *value_string = getSetupValue(token_type, setup_value);
2718 /* build complete token string */
2719 sprintf(token_string, "%s%s", prefix, token_text);
2721 /* build setup entry line */
2722 line = getFormattedSetupEntry(token_string, value_string);
2724 if (token_type == TYPE_KEY_X11)
2726 Key key = *(Key *)setup_value;
2727 char *keyname = getKeyNameFromKey(key);
2729 /* add comment, if useful */
2730 if (strcmp(keyname, "(undefined)") != 0 &&
2731 strcmp(keyname, "(unknown)") != 0)
2733 /* add at least one whitespace */
2735 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2739 strcat(line, keyname);
2746 void LoadLevelSetup_LastSeries()
2748 /* ----------------------------------------------------------------------- */
2749 /* ~/.<program>/levelsetup.conf */
2750 /* ----------------------------------------------------------------------- */
2752 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2753 SetupFileHash *level_setup_hash = NULL;
2755 /* always start with reliable default values */
2756 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2758 if ((level_setup_hash = loadSetupFileHash(filename)))
2760 char *last_level_series =
2761 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2763 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2765 if (leveldir_current == NULL)
2766 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2768 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2770 freeSetupFileHash(level_setup_hash);
2773 Error(ERR_WARN, "using default setup values");
2778 void SaveLevelSetup_LastSeries()
2780 /* ----------------------------------------------------------------------- */
2781 /* ~/.<program>/levelsetup.conf */
2782 /* ----------------------------------------------------------------------- */
2784 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2785 char *level_subdir = leveldir_current->subdir;
2788 InitUserDataDirectory();
2790 if (!(file = fopen(filename, MODE_WRITE)))
2792 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2797 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2798 getCookie("LEVELSETUP")));
2799 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2804 SetFilePermissions(filename, PERMS_PRIVATE);
2809 static void checkSeriesInfo()
2811 static char *level_directory = NULL;
2813 struct dirent *dir_entry;
2815 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2817 level_directory = getPath2((leveldir_current->user_defined ?
2818 getUserLevelDir(NULL) :
2819 options.level_directory),
2820 leveldir_current->fullpath);
2822 if ((dir = opendir(level_directory)) == NULL)
2824 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2828 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2830 if (strlen(dir_entry->d_name) > 4 &&
2831 dir_entry->d_name[3] == '.' &&
2832 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2834 char levelnum_str[4];
2837 strncpy(levelnum_str, dir_entry->d_name, 3);
2838 levelnum_str[3] = '\0';
2840 levelnum_value = atoi(levelnum_str);
2843 if (levelnum_value < leveldir_current->first_level)
2845 Error(ERR_WARN, "additional level %d found", levelnum_value);
2846 leveldir_current->first_level = levelnum_value;
2848 else if (levelnum_value > leveldir_current->last_level)
2850 Error(ERR_WARN, "additional level %d found", levelnum_value);
2851 leveldir_current->last_level = levelnum_value;
2860 void LoadLevelSetup_SeriesInfo()
2863 SetupFileHash *level_setup_hash = NULL;
2864 char *level_subdir = leveldir_current->subdir;
2866 /* always start with reliable default values */
2867 level_nr = leveldir_current->first_level;
2869 checkSeriesInfo(leveldir_current);
2871 /* ----------------------------------------------------------------------- */
2872 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2873 /* ----------------------------------------------------------------------- */
2875 level_subdir = leveldir_current->subdir;
2877 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2879 if ((level_setup_hash = loadSetupFileHash(filename)))
2883 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2887 level_nr = atoi(token_value);
2889 if (level_nr < leveldir_current->first_level)
2890 level_nr = leveldir_current->first_level;
2891 if (level_nr > leveldir_current->last_level)
2892 level_nr = leveldir_current->last_level;
2895 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2899 int level_nr = atoi(token_value);
2901 if (level_nr < leveldir_current->first_level)
2902 level_nr = leveldir_current->first_level;
2903 if (level_nr > leveldir_current->last_level + 1)
2904 level_nr = leveldir_current->last_level;
2906 if (leveldir_current->user_defined || !leveldir_current->handicap)
2907 level_nr = leveldir_current->last_level;
2909 leveldir_current->handicap_level = level_nr;
2912 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2914 freeSetupFileHash(level_setup_hash);
2917 Error(ERR_WARN, "using default setup values");
2922 void SaveLevelSetup_SeriesInfo()
2925 char *level_subdir = leveldir_current->subdir;
2926 char *level_nr_str = int2str(level_nr, 0);
2927 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2930 /* ----------------------------------------------------------------------- */
2931 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2932 /* ----------------------------------------------------------------------- */
2934 InitLevelSetupDirectory(level_subdir);
2936 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2938 if (!(file = fopen(filename, MODE_WRITE)))
2940 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2945 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2946 getCookie("LEVELSETUP")));
2947 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2949 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2950 handicap_level_str));
2954 SetFilePermissions(filename, PERMS_PRIVATE);