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_GREEN : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
49 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
52 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
53 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
54 IS_LEVELCLASS_BD(n) ? 2 : \
55 IS_LEVELCLASS_EM(n) ? 3 : \
56 IS_LEVELCLASS_SP(n) ? 4 : \
57 IS_LEVELCLASS_DX(n) ? 5 : \
58 IS_LEVELCLASS_CONTRIB(n) ? 6 : \
59 IS_LEVELCLASS_PRIVATE(n) ? 7 : \
62 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
63 IS_ARTWORKCLASS_CONTRIB(n) ? FC_YELLOW : \
64 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
65 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
68 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
69 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
70 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
71 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
74 #define TOKEN_VALUE_POSITION 40
75 #define TOKEN_COMMENT_POSITION 60
77 #define MAX_COOKIE_LEN 256
80 /* ------------------------------------------------------------------------- */
82 /* ------------------------------------------------------------------------- */
84 static char *getLevelClassDescription(TreeInfo *ldi)
86 int position = ldi->sort_priority / 100;
88 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
89 return levelclass_desc[position];
91 return "Unknown Level Class";
94 static char *getUserLevelDir(char *level_subdir)
96 static char *userlevel_dir = NULL;
97 char *data_dir = getUserDataDir();
98 char *userlevel_subdir = LEVELS_DIRECTORY;
100 checked_free(userlevel_dir);
102 if (level_subdir != NULL)
103 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
105 userlevel_dir = getPath2(data_dir, userlevel_subdir);
107 return userlevel_dir;
110 static char *getScoreDir(char *level_subdir)
112 static char *score_dir = NULL;
113 char *data_dir = getCommonDataDir();
114 char *score_subdir = SCORES_DIRECTORY;
116 checked_free(score_dir);
118 if (level_subdir != NULL)
119 score_dir = getPath3(data_dir, score_subdir, level_subdir);
121 score_dir = getPath2(data_dir, score_subdir);
126 static char *getLevelSetupDir(char *level_subdir)
128 static char *levelsetup_dir = NULL;
129 char *data_dir = getUserDataDir();
130 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
132 checked_free(levelsetup_dir);
134 if (level_subdir != NULL)
135 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
137 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
139 return levelsetup_dir;
142 static char *getLevelDirFromTreeInfo(TreeInfo *node)
144 static char *level_dir = NULL;
147 return options.level_directory;
149 checked_free(level_dir);
151 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
152 options.level_directory), node->fullpath);
157 char *getCurrentLevelDir()
159 return getLevelDirFromTreeInfo(leveldir_current);
162 static char *getTapeDir(char *level_subdir)
164 static char *tape_dir = NULL;
165 char *data_dir = getUserDataDir();
166 char *tape_subdir = TAPES_DIRECTORY;
168 checked_free(tape_dir);
170 if (level_subdir != NULL)
171 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
173 tape_dir = getPath2(data_dir, tape_subdir);
178 static char *getSolutionTapeDir()
180 static char *tape_dir = NULL;
181 char *data_dir = getCurrentLevelDir();
182 char *tape_subdir = TAPES_DIRECTORY;
184 checked_free(tape_dir);
186 tape_dir = getPath2(data_dir, tape_subdir);
191 static char *getDefaultGraphicsDir(char *graphics_subdir)
193 static char *graphics_dir = NULL;
195 if (graphics_subdir == NULL)
196 return options.graphics_directory;
198 checked_free(graphics_dir);
200 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
205 static char *getDefaultSoundsDir(char *sounds_subdir)
207 static char *sounds_dir = NULL;
209 if (sounds_subdir == NULL)
210 return options.sounds_directory;
212 checked_free(sounds_dir);
214 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
219 static char *getDefaultMusicDir(char *music_subdir)
221 static char *music_dir = NULL;
223 if (music_subdir == NULL)
224 return options.music_directory;
226 checked_free(music_dir);
228 music_dir = getPath2(options.music_directory, music_subdir);
233 static char *getDefaultArtworkSet(int type)
235 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
236 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
237 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
240 static char *getDefaultArtworkDir(int type)
242 return (type == TREE_TYPE_GRAPHICS_DIR ?
243 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
244 type == TREE_TYPE_SOUNDS_DIR ?
245 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
246 type == TREE_TYPE_MUSIC_DIR ?
247 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
250 static char *getUserGraphicsDir()
252 static char *usergraphics_dir = NULL;
254 if (usergraphics_dir == NULL)
255 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
257 return usergraphics_dir;
260 static char *getUserSoundsDir()
262 static char *usersounds_dir = NULL;
264 if (usersounds_dir == NULL)
265 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
267 return usersounds_dir;
270 static char *getUserMusicDir()
272 static char *usermusic_dir = NULL;
274 if (usermusic_dir == NULL)
275 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
277 return usermusic_dir;
280 static char *getSetupArtworkDir(TreeInfo *ti)
282 static char *artwork_dir = NULL;
284 checked_free(artwork_dir);
286 artwork_dir = getPath2(ti->basepath, ti->fullpath);
291 char *setLevelArtworkDir(TreeInfo *ti)
293 char **artwork_path_ptr, **artwork_set_ptr;
294 TreeInfo *level_artwork;
296 if (ti == NULL || leveldir_current == NULL)
299 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
300 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
302 checked_free(*artwork_path_ptr);
304 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
305 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
308 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
309 normally result in using the artwork configured in the setup menu. But
310 if an artwork subdirectory exists (which might contain custom artwork
311 or an artwork configuration file), this level artwork must be treated
312 as relative to the default "classic" artwork, not to the artwork that
313 is currently configured in the setup menu. */
315 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
317 checked_free(*artwork_set_ptr);
321 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
322 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
326 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
327 *artwork_set_ptr = NULL;
333 return *artwork_set_ptr;
336 inline static char *getLevelArtworkSet(int type)
338 if (leveldir_current == NULL)
341 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
344 inline static char *getLevelArtworkDir(int type)
346 if (leveldir_current == NULL)
347 return UNDEFINED_FILENAME;
349 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
352 char *getTapeFilename(int nr)
354 static char *filename = NULL;
355 char basename[MAX_FILENAME_LEN];
357 checked_free(filename);
359 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
360 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
365 char *getSolutionTapeFilename(int nr)
367 static char *filename = NULL;
368 char basename[MAX_FILENAME_LEN];
370 checked_free(filename);
372 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
373 filename = getPath2(getSolutionTapeDir(), basename);
378 char *getScoreFilename(int nr)
380 static char *filename = NULL;
381 char basename[MAX_FILENAME_LEN];
383 checked_free(filename);
385 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
386 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
391 char *getSetupFilename()
393 static char *filename = NULL;
395 checked_free(filename);
397 filename = getPath2(getSetupDir(), SETUP_FILENAME);
402 char *getEditorSetupFilename()
404 static char *filename = NULL;
406 checked_free(filename);
407 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
409 if (fileExists(filename))
412 checked_free(filename);
413 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
418 char *getHelpAnimFilename()
420 static char *filename = NULL;
422 checked_free(filename);
424 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
429 char *getHelpTextFilename()
431 static char *filename = NULL;
433 checked_free(filename);
435 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
440 char *getLevelSetInfoFilename()
442 static char *filename = NULL;
457 for (i = 0; basenames[i] != NULL; i++)
459 checked_free(filename);
460 filename = getPath2(getCurrentLevelDir(), basenames[i]);
462 if (fileExists(filename))
469 static char *getCorrectedArtworkBasename(char *basename)
471 char *basename_corrected = basename;
473 #if defined(PLATFORM_MSDOS)
474 if (program.filename_prefix != NULL)
476 int prefix_len = strlen(program.filename_prefix);
478 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
479 basename_corrected = &basename[prefix_len];
481 /* if corrected filename is still longer than standard MS-DOS filename
482 size (8 characters + 1 dot + 3 characters file extension), shorten
483 filename by writing file extension after 8th basename character */
484 if (strlen(basename_corrected) > 8 + 1 + 3)
486 static char *msdos_filename = NULL;
488 checked_free(msdos_filename);
490 msdos_filename = getStringCopy(basename_corrected);
491 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
493 basename_corrected = msdos_filename;
498 return basename_corrected;
501 char *getCustomImageFilename(char *basename)
503 static char *filename = NULL;
504 boolean skip_setup_artwork = FALSE;
506 checked_free(filename);
508 basename = getCorrectedArtworkBasename(basename);
510 if (!setup.override_level_graphics)
512 /* 1st try: look for special artwork in current level series directory */
513 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
514 if (fileExists(filename))
519 /* check if there is special artwork configured in level series config */
520 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
522 /* 2nd try: look for special artwork configured in level series config */
523 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
524 if (fileExists(filename))
529 /* take missing artwork configured in level set config from default */
530 skip_setup_artwork = TRUE;
534 if (!skip_setup_artwork)
536 /* 3rd try: look for special artwork in configured artwork directory */
537 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
538 if (fileExists(filename))
544 /* 4th try: look for default artwork in new default artwork directory */
545 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
546 if (fileExists(filename))
551 /* 5th try: look for default artwork in old default artwork directory */
552 filename = getPath2(options.graphics_directory, basename);
553 if (fileExists(filename))
556 return NULL; /* cannot find specified artwork file anywhere */
559 char *getCustomSoundFilename(char *basename)
561 static char *filename = NULL;
562 boolean skip_setup_artwork = FALSE;
564 checked_free(filename);
566 basename = getCorrectedArtworkBasename(basename);
568 if (!setup.override_level_sounds)
570 /* 1st try: look for special artwork in current level series directory */
571 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
572 if (fileExists(filename))
577 /* check if there is special artwork configured in level series config */
578 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
580 /* 2nd try: look for special artwork configured in level series config */
581 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
582 if (fileExists(filename))
587 /* take missing artwork configured in level set config from default */
588 skip_setup_artwork = TRUE;
592 if (!skip_setup_artwork)
594 /* 3rd try: look for special artwork in configured artwork directory */
595 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
596 if (fileExists(filename))
602 /* 4th try: look for default artwork in new default artwork directory */
603 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
604 if (fileExists(filename))
609 /* 5th try: look for default artwork in old default artwork directory */
610 filename = getPath2(options.sounds_directory, basename);
611 if (fileExists(filename))
614 return NULL; /* cannot find specified artwork file anywhere */
617 char *getCustomMusicFilename(char *basename)
619 static char *filename = NULL;
620 boolean skip_setup_artwork = FALSE;
622 checked_free(filename);
624 basename = getCorrectedArtworkBasename(basename);
626 if (!setup.override_level_music)
628 /* 1st try: look for special artwork in current level series directory */
629 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
630 if (fileExists(filename))
635 /* check if there is special artwork configured in level series config */
636 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
638 /* 2nd try: look for special artwork configured in level series config */
639 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
640 if (fileExists(filename))
645 /* take missing artwork configured in level set config from default */
646 skip_setup_artwork = TRUE;
650 if (!skip_setup_artwork)
652 /* 3rd try: look for special artwork in configured artwork directory */
653 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
654 if (fileExists(filename))
660 /* 4th try: look for default artwork in new default artwork directory */
661 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
662 if (fileExists(filename))
667 /* 5th try: look for default artwork in old default artwork directory */
668 filename = getPath2(options.music_directory, basename);
669 if (fileExists(filename))
672 return NULL; /* cannot find specified artwork file anywhere */
675 char *getCustomArtworkFilename(char *basename, int type)
677 if (type == ARTWORK_TYPE_GRAPHICS)
678 return getCustomImageFilename(basename);
679 else if (type == ARTWORK_TYPE_SOUNDS)
680 return getCustomSoundFilename(basename);
681 else if (type == ARTWORK_TYPE_MUSIC)
682 return getCustomMusicFilename(basename);
684 return UNDEFINED_FILENAME;
687 char *getCustomArtworkConfigFilename(int type)
689 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
692 char *getCustomArtworkLevelConfigFilename(int type)
694 static char *filename = NULL;
696 checked_free(filename);
698 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
703 char *getCustomMusicDirectory(void)
705 static char *directory = NULL;
706 boolean skip_setup_artwork = FALSE;
708 checked_free(directory);
710 if (!setup.override_level_music)
712 /* 1st try: look for special artwork in current level series directory */
713 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
714 if (fileExists(directory))
719 /* check if there is special artwork configured in level series config */
720 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
722 /* 2nd try: look for special artwork configured in level series config */
723 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
724 if (fileExists(directory))
729 /* take missing artwork configured in level set config from default */
730 skip_setup_artwork = TRUE;
734 if (!skip_setup_artwork)
736 /* 3rd try: look for special artwork in configured artwork directory */
737 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
738 if (fileExists(directory))
744 /* 4th try: look for default artwork in new default artwork directory */
745 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
746 if (fileExists(directory))
751 /* 5th try: look for default artwork in old default artwork directory */
752 directory = getStringCopy(options.music_directory);
753 if (fileExists(directory))
756 return NULL; /* cannot find specified artwork file anywhere */
759 void InitTapeDirectory(char *level_subdir)
761 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
762 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
763 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
766 void InitScoreDirectory(char *level_subdir)
768 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
769 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
770 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
773 static void SaveUserLevelInfo();
775 void InitUserLevelDirectory(char *level_subdir)
777 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
779 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
780 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
781 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
787 void InitLevelSetupDirectory(char *level_subdir)
789 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
790 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
791 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
795 /* ------------------------------------------------------------------------- */
796 /* some functions to handle lists of level directories */
797 /* ------------------------------------------------------------------------- */
799 TreeInfo *newTreeInfo()
801 return checked_calloc(sizeof(TreeInfo));
804 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
806 node_new->next = *node_first;
807 *node_first = node_new;
810 int numTreeInfo(TreeInfo *node)
823 boolean validLevelSeries(TreeInfo *node)
825 return (node != NULL && !node->node_group && !node->parent_link);
828 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
833 if (node->node_group) /* enter level group (step down into tree) */
834 return getFirstValidTreeInfoEntry(node->node_group);
835 else if (node->parent_link) /* skip start entry of level group */
837 if (node->next) /* get first real level series entry */
838 return getFirstValidTreeInfoEntry(node->next);
839 else /* leave empty level group and go on */
840 return getFirstValidTreeInfoEntry(node->node_parent->next);
842 else /* this seems to be a regular level series */
846 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
851 if (node->node_parent == NULL) /* top level group */
852 return *node->node_top;
853 else /* sub level group */
854 return node->node_parent->node_group;
857 int numTreeInfoInGroup(TreeInfo *node)
859 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
862 int posTreeInfo(TreeInfo *node)
864 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
869 if (node_cmp == node)
873 node_cmp = node_cmp->next;
879 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
881 TreeInfo *node_default = node;
896 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
898 if (identifier == NULL)
903 if (node->node_group)
905 TreeInfo *node_group;
907 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
912 else if (!node->parent_link)
914 if (strcmp(identifier, node->identifier) == 0)
924 void dumpTreeInfo(TreeInfo *node, int depth)
928 printf("Dumping TreeInfo:\n");
932 for (i = 0; i < (depth + 1) * 3; i++)
936 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
937 node->subdir, node->fullpath, node->basepath, node->user_defined);
939 printf("subdir == '%s' (%s) [%s] (%d)\n",
940 node->subdir, node->name, node->identifier, node->sort_priority);
943 if (node->node_group != NULL)
944 dumpTreeInfo(node->node_group, depth + 1);
950 void sortTreeInfo(TreeInfo **node_first,
951 int (*compare_function)(const void *, const void *))
953 int num_nodes = numTreeInfo(*node_first);
954 TreeInfo **sort_array;
955 TreeInfo *node = *node_first;
961 /* allocate array for sorting structure pointers */
962 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
964 /* writing structure pointers to sorting array */
965 while (i < num_nodes && node) /* double boundary check... */
967 sort_array[i] = node;
973 /* sorting the structure pointers in the sorting array */
974 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
977 /* update the linkage of list elements with the sorted node array */
978 for (i = 0; i < num_nodes - 1; i++)
979 sort_array[i]->next = sort_array[i + 1];
980 sort_array[num_nodes - 1]->next = NULL;
982 /* update the linkage of the main list anchor pointer */
983 *node_first = sort_array[0];
987 /* now recursively sort the level group structures */
991 if (node->node_group != NULL)
992 sortTreeInfo(&node->node_group, compare_function);
999 /* ========================================================================= */
1000 /* some stuff from "files.c" */
1001 /* ========================================================================= */
1003 #if defined(PLATFORM_WIN32)
1005 #define S_IRGRP S_IRUSR
1008 #define S_IROTH S_IRUSR
1011 #define S_IWGRP S_IWUSR
1014 #define S_IWOTH S_IWUSR
1017 #define S_IXGRP S_IXUSR
1020 #define S_IXOTH S_IXUSR
1023 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1028 #endif /* PLATFORM_WIN32 */
1030 /* file permissions for newly written files */
1031 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1032 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1033 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1035 #define MODE_W_PRIVATE (S_IWUSR)
1036 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1037 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1039 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1040 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1042 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1043 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1045 char *getUserDataDir(void)
1047 static char *userdata_dir = NULL;
1049 if (userdata_dir == NULL)
1050 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1052 return userdata_dir;
1055 char *getCommonDataDir(void)
1057 static char *common_data_dir = NULL;
1059 #if defined(PLATFORM_WIN32)
1060 if (common_data_dir == NULL)
1062 char *dir = checked_malloc(MAX_PATH + 1);
1064 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1065 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1066 common_data_dir = getPath2(dir, program.userdata_directory);
1068 common_data_dir = options.rw_base_directory;
1071 if (common_data_dir == NULL)
1072 common_data_dir = options.rw_base_directory;
1075 return common_data_dir;
1080 return getUserDataDir();
1083 static mode_t posix_umask(mode_t mask)
1085 #if defined(PLATFORM_UNIX)
1092 static int posix_mkdir(const char *pathname, mode_t mode)
1094 #if defined(PLATFORM_WIN32)
1095 return mkdir(pathname);
1097 return mkdir(pathname, mode);
1101 void createDirectory(char *dir, char *text, int permission_class)
1103 /* leave "other" permissions in umask untouched, but ensure group parts
1104 of USERDATA_DIR_MODE are not masked */
1105 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1106 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1107 mode_t normal_umask = posix_umask(0);
1108 mode_t group_umask = ~(dir_mode & S_IRWXG);
1109 posix_umask(normal_umask & group_umask);
1111 if (access(dir, F_OK) != 0)
1112 if (posix_mkdir(dir, dir_mode) != 0)
1113 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1115 posix_umask(normal_umask); /* reset normal umask */
1118 void InitUserDataDirectory()
1120 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1123 void SetFilePermissions(char *filename, int permission_class)
1125 chmod(filename, (permission_class == PERMS_PRIVATE ?
1126 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1129 char *getCookie(char *file_type)
1131 static char cookie[MAX_COOKIE_LEN + 1];
1133 if (strlen(program.cookie_prefix) + 1 +
1134 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1135 return "[COOKIE ERROR]"; /* should never happen */
1137 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1138 program.cookie_prefix, file_type,
1139 program.version_major, program.version_minor);
1144 int getFileVersionFromCookieString(const char *cookie)
1146 const char *ptr_cookie1, *ptr_cookie2;
1147 const char *pattern1 = "_FILE_VERSION_";
1148 const char *pattern2 = "?.?";
1149 const int len_cookie = strlen(cookie);
1150 const int len_pattern1 = strlen(pattern1);
1151 const int len_pattern2 = strlen(pattern2);
1152 const int len_pattern = len_pattern1 + len_pattern2;
1153 int version_major, version_minor;
1155 if (len_cookie <= len_pattern)
1158 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1159 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1161 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1164 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1165 ptr_cookie2[1] != '.' ||
1166 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1169 version_major = ptr_cookie2[0] - '0';
1170 version_minor = ptr_cookie2[2] - '0';
1172 return VERSION_IDENT(version_major, version_minor, 0, 0);
1175 boolean checkCookieString(const char *cookie, const char *template)
1177 const char *pattern = "_FILE_VERSION_?.?";
1178 const int len_cookie = strlen(cookie);
1179 const int len_template = strlen(template);
1180 const int len_pattern = strlen(pattern);
1182 if (len_cookie != len_template)
1185 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1191 /* ------------------------------------------------------------------------- */
1192 /* setup file list and hash handling functions */
1193 /* ------------------------------------------------------------------------- */
1195 char *getFormattedSetupEntry(char *token, char *value)
1198 static char entry[MAX_LINE_LEN];
1200 /* if value is an empty string, just return token without value */
1204 /* start with the token and some spaces to format output line */
1205 sprintf(entry, "%s:", token);
1206 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1209 /* continue with the token's value */
1210 strcat(entry, value);
1215 SetupFileList *newSetupFileList(char *token, char *value)
1217 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1219 new->token = getStringCopy(token);
1220 new->value = getStringCopy(value);
1227 void freeSetupFileList(SetupFileList *list)
1232 checked_free(list->token);
1233 checked_free(list->value);
1236 freeSetupFileList(list->next);
1241 char *getListEntry(SetupFileList *list, char *token)
1246 if (strcmp(list->token, token) == 0)
1249 return getListEntry(list->next, token);
1252 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1257 if (strcmp(list->token, token) == 0)
1259 checked_free(list->value);
1261 list->value = getStringCopy(value);
1265 else if (list->next == NULL)
1266 return (list->next = newSetupFileList(token, value));
1268 return setListEntry(list->next, token, value);
1271 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1276 if (list->next == NULL)
1277 return (list->next = newSetupFileList(token, value));
1279 return addListEntry(list->next, token, value);
1283 static void printSetupFileList(SetupFileList *list)
1288 printf("token: '%s'\n", list->token);
1289 printf("value: '%s'\n", list->value);
1291 printSetupFileList(list->next);
1296 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1297 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1298 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1299 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1301 #define insert_hash_entry hashtable_insert
1302 #define search_hash_entry hashtable_search
1303 #define change_hash_entry hashtable_change
1304 #define remove_hash_entry hashtable_remove
1307 static unsigned int get_hash_from_key(void *key)
1312 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1313 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1314 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1315 it works better than many other constants, prime or not) has never been
1316 adequately explained.
1318 If you just want to have a good hash function, and cannot wait, djb2
1319 is one of the best string hash functions i know. It has excellent
1320 distribution and speed on many different sets of keys and table sizes.
1321 You are not likely to do better with one of the "well known" functions
1322 such as PJW, K&R, etc.
1324 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1327 char *str = (char *)key;
1328 unsigned int hash = 5381;
1331 while ((c = *str++))
1332 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1337 static int keys_are_equal(void *key1, void *key2)
1339 return (strcmp((char *)key1, (char *)key2) == 0);
1342 SetupFileHash *newSetupFileHash()
1344 SetupFileHash *new_hash =
1345 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1347 if (new_hash == NULL)
1348 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1353 void freeSetupFileHash(SetupFileHash *hash)
1358 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1361 char *getHashEntry(SetupFileHash *hash, char *token)
1366 return search_hash_entry(hash, token);
1369 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1376 value_copy = getStringCopy(value);
1378 /* change value; if it does not exist, insert it as new */
1379 if (!change_hash_entry(hash, token, value_copy))
1380 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1381 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1384 char *removeHashEntry(SetupFileHash *hash, char *token)
1389 return remove_hash_entry(hash, token);
1394 static void printSetupFileHash(SetupFileHash *hash)
1396 BEGIN_HASH_ITERATION(hash, itr)
1398 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1399 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1401 END_HASH_ITERATION(hash, itr)
1406 static void *loadSetupFileData(char *filename, boolean use_hash)
1408 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1409 char *token, *value, *line_ptr;
1410 void *setup_file_data, *insert_ptr = NULL;
1411 boolean read_continued_line = FALSE;
1415 setup_file_data = newSetupFileHash();
1417 insert_ptr = setup_file_data = newSetupFileList("", "");
1419 if (!(file = fopen(filename, MODE_READ)))
1421 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1427 /* read next line of input file */
1428 if (!fgets(line, MAX_LINE_LEN, file))
1431 /* cut trailing newline or carriage return */
1432 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1433 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1436 if (read_continued_line)
1438 /* cut leading whitespaces from input line */
1439 for (line_ptr = line; *line_ptr; line_ptr++)
1440 if (*line_ptr != ' ' && *line_ptr != '\t')
1443 /* append new line to existing line, if there is enough space */
1444 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1445 strcat(previous_line, line_ptr);
1447 strcpy(line, previous_line); /* copy storage buffer to line */
1449 read_continued_line = FALSE;
1452 /* if the last character is '\', continue at next line */
1453 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1455 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1456 strcpy(previous_line, line); /* copy line to storage buffer */
1458 read_continued_line = TRUE;
1463 /* cut trailing comment from input line */
1464 for (line_ptr = line; *line_ptr; line_ptr++)
1466 if (*line_ptr == '#')
1473 /* cut trailing whitespaces from input line */
1474 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1475 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1478 /* ignore empty lines */
1482 /* cut leading whitespaces from token */
1483 for (token = line; *token; token++)
1484 if (*token != ' ' && *token != '\t')
1487 /* start with empty value as reliable default */
1490 /* find end of token to determine start of value */
1491 for (line_ptr = token; *line_ptr; line_ptr++)
1493 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1495 *line_ptr = '\0'; /* terminate token string */
1496 value = line_ptr + 1; /* set beginning of value */
1502 /* cut leading whitespaces from value */
1503 for (; *value; value++)
1504 if (*value != ' ' && *value != '\t')
1509 value = "true"; /* treat tokens without value as "true" */
1515 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1517 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1525 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1526 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1530 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1531 SetupFileList *first_valid_list_entry = setup_file_list->next;
1533 /* free empty list header */
1534 setup_file_list->next = NULL;
1535 freeSetupFileList(setup_file_list);
1536 setup_file_data = first_valid_list_entry;
1538 if (first_valid_list_entry == NULL)
1539 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1542 return setup_file_data;
1545 SetupFileList *loadSetupFileList(char *filename)
1547 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1550 SetupFileHash *loadSetupFileHash(char *filename)
1552 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1555 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1558 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1561 Error(ERR_WARN, "configuration file has no file identifier");
1562 else if (!checkCookieString(value, identifier))
1563 Error(ERR_WARN, "configuration file has wrong file identifier");
1567 /* ========================================================================= */
1568 /* setup file stuff */
1569 /* ========================================================================= */
1571 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1572 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1573 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1575 /* level directory info */
1576 #define LEVELINFO_TOKEN_IDENTIFIER 0
1577 #define LEVELINFO_TOKEN_NAME 1
1578 #define LEVELINFO_TOKEN_NAME_SORTING 2
1579 #define LEVELINFO_TOKEN_AUTHOR 3
1580 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1581 #define LEVELINFO_TOKEN_LEVELS 5
1582 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1583 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1584 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1585 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1586 #define LEVELINFO_TOKEN_READONLY 10
1587 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1588 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1589 #define LEVELINFO_TOKEN_MUSIC_SET 13
1590 #define LEVELINFO_TOKEN_FILENAME 14
1591 #define LEVELINFO_TOKEN_FILETYPE 15
1592 #define LEVELINFO_TOKEN_HANDICAP 16
1594 #define NUM_LEVELINFO_TOKENS 17
1596 static LevelDirTree ldi;
1598 static struct TokenInfo levelinfo_tokens[] =
1600 /* level directory info */
1601 { TYPE_STRING, &ldi.identifier, "identifier" },
1602 { TYPE_STRING, &ldi.name, "name" },
1603 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1604 { TYPE_STRING, &ldi.author, "author" },
1605 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1606 { TYPE_INTEGER, &ldi.levels, "levels" },
1607 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1608 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1609 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1610 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1611 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1612 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1613 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1614 { TYPE_STRING, &ldi.music_set, "music_set" },
1615 { TYPE_STRING, &ldi.level_filename, "filename" },
1616 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1617 { TYPE_BOOLEAN, &ldi.handicap, "handicap" }
1620 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1624 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1625 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1626 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1627 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1630 ldi->node_parent = NULL;
1631 ldi->node_group = NULL;
1635 ldi->cl_cursor = -1;
1638 ldi->fullpath = NULL;
1639 ldi->basepath = NULL;
1640 ldi->identifier = NULL;
1641 ldi->name = getStringCopy(ANONYMOUS_NAME);
1642 ldi->name_sorting = NULL;
1643 ldi->author = getStringCopy(ANONYMOUS_NAME);
1645 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1646 ldi->latest_engine = FALSE; /* default: get from level */
1647 ldi->parent_link = FALSE;
1648 ldi->user_defined = FALSE;
1650 ldi->class_desc = NULL;
1652 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1654 ldi->imported_from = NULL;
1656 ldi->graphics_set = NULL;
1657 ldi->sounds_set = NULL;
1658 ldi->music_set = NULL;
1659 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1660 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1661 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1663 ldi->level_filename = NULL;
1664 ldi->level_filetype = NULL;
1667 ldi->first_level = 0;
1668 ldi->last_level = 0;
1669 ldi->level_group = FALSE;
1670 ldi->handicap_level = 0;
1671 ldi->readonly = TRUE;
1672 ldi->handicap = TRUE;
1676 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1680 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1682 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1688 /* copy all values from the parent structure */
1690 ldi->type = parent->type;
1692 ldi->node_top = parent->node_top;
1693 ldi->node_parent = parent;
1694 ldi->node_group = NULL;
1698 ldi->cl_cursor = -1;
1701 ldi->fullpath = NULL;
1702 ldi->basepath = NULL;
1703 ldi->identifier = NULL;
1704 ldi->name = getStringCopy(ANONYMOUS_NAME);
1705 ldi->name_sorting = NULL;
1706 ldi->author = getStringCopy(parent->author);
1708 ldi->sort_priority = parent->sort_priority;
1709 ldi->latest_engine = parent->latest_engine;
1710 ldi->parent_link = FALSE;
1711 ldi->user_defined = parent->user_defined;
1712 ldi->color = parent->color;
1713 ldi->class_desc = getStringCopy(parent->class_desc);
1715 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1717 ldi->imported_from = getStringCopy(parent->imported_from);
1719 ldi->graphics_set = NULL;
1720 ldi->sounds_set = NULL;
1721 ldi->music_set = NULL;
1722 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1723 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1724 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1726 ldi->level_filename = NULL;
1727 ldi->level_filetype = NULL;
1730 ldi->first_level = 0;
1731 ldi->last_level = 0;
1732 ldi->level_group = FALSE;
1733 ldi->handicap_level = 0;
1734 ldi->readonly = TRUE;
1735 ldi->handicap = TRUE;
1740 /* first copy all values from the parent structure ... */
1743 /* ... then set all fields to default that cannot be inherited from parent.
1744 This is especially important for all those fields that can be set from
1745 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1746 calls 'free()' for all already set token values which requires that no
1747 other structure's pointer may point to them!
1751 ldi->fullpath = NULL;
1752 ldi->basepath = NULL;
1753 ldi->identifier = NULL;
1754 ldi->name = getStringCopy(ANONYMOUS_NAME);
1755 ldi->name_sorting = NULL;
1756 ldi->author = getStringCopy(parent->author);
1758 ldi->imported_from = getStringCopy(parent->imported_from);
1759 ldi->class_desc = getStringCopy(parent->class_desc);
1761 ldi->graphics_set = NULL;
1762 ldi->sounds_set = NULL;
1763 ldi->music_set = NULL;
1764 ldi->graphics_path = NULL;
1765 ldi->sounds_path = NULL;
1766 ldi->music_path = NULL;
1768 ldi->level_group = FALSE;
1769 ldi->parent_link = FALSE;
1771 ldi->node_top = parent->node_top;
1772 ldi->node_parent = parent;
1773 ldi->node_group = NULL;
1779 static void freeTreeInfo(TreeInfo *ldi)
1781 checked_free(ldi->subdir);
1782 checked_free(ldi->fullpath);
1783 checked_free(ldi->basepath);
1784 checked_free(ldi->identifier);
1786 checked_free(ldi->name);
1787 checked_free(ldi->name_sorting);
1788 checked_free(ldi->author);
1790 checked_free(ldi->class_desc);
1792 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1794 checked_free(ldi->imported_from);
1796 checked_free(ldi->graphics_set);
1797 checked_free(ldi->sounds_set);
1798 checked_free(ldi->music_set);
1800 checked_free(ldi->graphics_path);
1801 checked_free(ldi->sounds_path);
1802 checked_free(ldi->music_path);
1804 checked_free(ldi->level_filename);
1805 checked_free(ldi->level_filetype);
1809 void setSetupInfo(struct TokenInfo *token_info,
1810 int token_nr, char *token_value)
1812 int token_type = token_info[token_nr].type;
1813 void *setup_value = token_info[token_nr].value;
1815 if (token_value == NULL)
1818 /* set setup field to corresponding token value */
1823 *(boolean *)setup_value = get_boolean_from_string(token_value);
1827 *(Key *)setup_value = getKeyFromKeyName(token_value);
1831 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1835 *(int *)setup_value = get_integer_from_string(token_value);
1839 checked_free(*(char **)setup_value);
1840 *(char **)setup_value = getStringCopy(token_value);
1848 static int compareTreeInfoEntries(const void *object1, const void *object2)
1850 const TreeInfo *entry1 = *((TreeInfo **)object1);
1851 const TreeInfo *entry2 = *((TreeInfo **)object2);
1852 int class_sorting1, class_sorting2;
1855 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1857 class_sorting1 = LEVELSORTING(entry1);
1858 class_sorting2 = LEVELSORTING(entry2);
1862 class_sorting1 = ARTWORKSORTING(entry1);
1863 class_sorting2 = ARTWORKSORTING(entry2);
1866 if (entry1->parent_link || entry2->parent_link)
1867 compare_result = (entry1->parent_link ? -1 : +1);
1868 else if (entry1->sort_priority == entry2->sort_priority)
1870 char *name1 = getStringToLower(entry1->name_sorting);
1871 char *name2 = getStringToLower(entry2->name_sorting);
1873 compare_result = strcmp(name1, name2);
1878 else if (class_sorting1 == class_sorting2)
1879 compare_result = entry1->sort_priority - entry2->sort_priority;
1881 compare_result = class_sorting1 - class_sorting2;
1883 return compare_result;
1886 static void createParentTreeInfoNode(TreeInfo *node_parent)
1890 if (node_parent == NULL)
1893 ti_new = newTreeInfo();
1894 setTreeInfoToDefaults(ti_new, node_parent->type);
1896 ti_new->node_parent = node_parent;
1897 ti_new->parent_link = TRUE;
1900 setString(&ti_new->identifier, node_parent->identifier);
1901 setString(&ti_new->name, ".. (parent directory)");
1902 setString(&ti_new->name_sorting, ti_new->name);
1904 setString(&ti_new->subdir, "..");
1905 setString(&ti_new->fullpath, node_parent->fullpath);
1907 ti_new->sort_priority = node_parent->sort_priority;
1908 ti_new->latest_engine = node_parent->latest_engine;
1910 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1912 ti_new->identifier = getStringCopy(node_parent->identifier);
1913 ti_new->name = ".. (parent directory)";
1914 ti_new->name_sorting = getStringCopy(ti_new->name);
1916 ti_new->subdir = "..";
1917 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1919 ti_new->sort_priority = node_parent->sort_priority;
1920 ti_new->latest_engine = node_parent->latest_engine;
1922 ti_new->class_desc = getLevelClassDescription(ti_new);
1925 pushTreeInfo(&node_parent->node_group, ti_new);
1928 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1929 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1931 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1932 TreeInfo *node_parent,
1933 char *level_directory,
1934 char *directory_name)
1936 char *directory_path = getPath2(level_directory, directory_name);
1937 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1938 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1939 LevelDirTree *leveldir_new = NULL;
1942 if (setup_file_hash == NULL)
1944 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1946 free(directory_path);
1952 leveldir_new = newTreeInfo();
1955 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1957 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1959 leveldir_new->subdir = getStringCopy(directory_name);
1961 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1963 /* set all structure fields according to the token/value pairs */
1964 ldi = *leveldir_new;
1965 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1966 setSetupInfo(levelinfo_tokens, i,
1967 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1968 *leveldir_new = ldi;
1971 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1972 setString(&leveldir_new->name, leveldir_new->subdir);
1974 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1976 free(leveldir_new->name);
1977 leveldir_new->name = getStringCopy(leveldir_new->subdir);
1981 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1983 if (leveldir_new->identifier == NULL)
1984 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
1986 if (leveldir_new->name_sorting == NULL)
1987 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1989 if (node_parent == NULL) /* top level group */
1991 leveldir_new->basepath = getStringCopy(level_directory);
1992 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
1994 else /* sub level group */
1996 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1997 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2000 if (leveldir_new->levels < 1)
2001 leveldir_new->levels = 1;
2003 leveldir_new->last_level =
2004 leveldir_new->first_level + leveldir_new->levels - 1;
2007 leveldir_new->user_defined =
2008 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2010 leveldir_new->user_defined =
2011 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2014 leveldir_new->color = LEVELCOLOR(leveldir_new);
2016 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2018 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2021 leveldir_new->handicap_level = /* set handicap to default value */
2022 (leveldir_new->user_defined || !leveldir_new->handicap ?
2023 leveldir_new->last_level : leveldir_new->first_level);
2025 pushTreeInfo(node_first, leveldir_new);
2027 freeSetupFileHash(setup_file_hash);
2029 if (leveldir_new->level_group)
2031 /* create node to link back to current level directory */
2032 createParentTreeInfoNode(leveldir_new);
2034 /* step into sub-directory and look for more level series */
2035 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2036 leveldir_new, directory_path);
2039 free(directory_path);
2045 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2046 TreeInfo *node_parent,
2047 char *level_directory)
2050 struct dirent *dir_entry;
2051 boolean valid_entry_found = FALSE;
2053 if ((dir = opendir(level_directory)) == NULL)
2055 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2059 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2061 struct stat file_status;
2062 char *directory_name = dir_entry->d_name;
2063 char *directory_path = getPath2(level_directory, directory_name);
2065 /* skip entries for current and parent directory */
2066 if (strcmp(directory_name, ".") == 0 ||
2067 strcmp(directory_name, "..") == 0)
2069 free(directory_path);
2073 /* find out if directory entry is itself a directory */
2074 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2075 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2077 free(directory_path);
2081 free(directory_path);
2083 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2084 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2085 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2088 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2095 if (!valid_entry_found)
2097 /* check if this directory directly contains a file "levelinfo.conf" */
2098 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2099 level_directory, ".");
2102 if (!valid_entry_found)
2103 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2107 void LoadLevelInfo()
2109 InitUserLevelDirectory(getLoginName());
2111 DrawInitText("Loading level series:", 120, FC_GREEN);
2113 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2114 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2116 /* before sorting, the first entries will be from the user directory */
2117 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2119 if (leveldir_first == NULL)
2120 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2122 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2125 dumpTreeInfo(leveldir_first, 0);
2129 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2130 TreeInfo *node_parent,
2131 char *base_directory,
2132 char *directory_name, int type)
2134 char *directory_path = getPath2(base_directory, directory_name);
2135 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2136 SetupFileHash *setup_file_hash = NULL;
2137 TreeInfo *artwork_new = NULL;
2140 if (access(filename, F_OK) == 0) /* file exists */
2141 setup_file_hash = loadSetupFileHash(filename);
2143 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2146 struct dirent *dir_entry;
2147 boolean valid_file_found = FALSE;
2149 if ((dir = opendir(directory_path)) != NULL)
2151 while ((dir_entry = readdir(dir)) != NULL)
2153 char *entry_name = dir_entry->d_name;
2155 if (FileIsArtworkType(entry_name, type))
2157 valid_file_found = TRUE;
2165 if (!valid_file_found)
2167 if (strcmp(directory_name, ".") != 0)
2168 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2170 free(directory_path);
2177 artwork_new = newTreeInfo();
2180 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2182 setTreeInfoToDefaults(artwork_new, type);
2184 artwork_new->subdir = getStringCopy(directory_name);
2186 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2189 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2192 /* set all structure fields according to the token/value pairs */
2194 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2195 setSetupInfo(levelinfo_tokens, i,
2196 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2200 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2201 setString(&artwork_new->name, artwork_new->subdir);
2203 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2205 free(artwork_new->name);
2206 artwork_new->name = getStringCopy(artwork_new->subdir);
2211 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2214 if (artwork_new->identifier == NULL)
2215 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2217 if (artwork_new->name_sorting == NULL)
2218 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2221 if (node_parent == NULL) /* top level group */
2223 artwork_new->basepath = getStringCopy(base_directory);
2224 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2226 else /* sub level group */
2228 artwork_new->basepath = getStringCopy(node_parent->basepath);
2229 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2233 artwork_new->user_defined =
2234 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2236 artwork_new->user_defined =
2237 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2240 /* (may use ".sort_priority" from "setup_file_hash" above) */
2241 artwork_new->color = ARTWORKCOLOR(artwork_new);
2243 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2245 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2248 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2251 if (artwork_new->name != NULL)
2253 free(artwork_new->name);
2254 artwork_new->name = NULL;
2259 if (artwork_new->identifier != NULL)
2261 free(artwork_new->identifier);
2262 artwork_new->identifier = NULL;
2266 if (strcmp(artwork_new->subdir, ".") == 0)
2268 if (artwork_new->user_defined)
2271 setString(&artwork_new->identifier, "private");
2273 artwork_new->identifier = getStringCopy("private");
2275 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2280 setString(&artwork_new->identifier, "classic");
2282 artwork_new->identifier = getStringCopy("classic");
2284 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2287 /* set to new values after changing ".sort_priority" */
2288 artwork_new->color = ARTWORKCOLOR(artwork_new);
2290 setString(&artwork_new->class_desc,
2291 getLevelClassDescription(artwork_new));
2293 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2299 setString(&artwork_new->identifier, artwork_new->subdir);
2301 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2306 setString(&artwork_new->name, artwork_new->identifier);
2307 setString(&artwork_new->name_sorting, artwork_new->name);
2309 artwork_new->name = getStringCopy(artwork_new->identifier);
2310 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2314 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2316 pushTreeInfo(node_first, artwork_new);
2318 freeSetupFileHash(setup_file_hash);
2320 free(directory_path);
2326 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2327 TreeInfo *node_parent,
2328 char *base_directory, int type)
2331 struct dirent *dir_entry;
2332 boolean valid_entry_found = FALSE;
2334 if ((dir = opendir(base_directory)) == NULL)
2336 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2337 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2341 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2343 struct stat file_status;
2344 char *directory_name = dir_entry->d_name;
2345 char *directory_path = getPath2(base_directory, directory_name);
2347 /* skip entries for current and parent directory */
2348 if (strcmp(directory_name, ".") == 0 ||
2349 strcmp(directory_name, "..") == 0)
2351 free(directory_path);
2355 /* find out if directory entry is itself a directory */
2356 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2357 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2359 free(directory_path);
2363 free(directory_path);
2365 /* check if this directory contains artwork with or without config file */
2366 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2368 directory_name, type);
2373 /* check if this directory directly contains artwork itself */
2374 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2375 base_directory, ".",
2377 if (!valid_entry_found)
2378 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2382 static TreeInfo *getDummyArtworkInfo(int type)
2384 /* this is only needed when there is completely no artwork available */
2385 TreeInfo *artwork_new = newTreeInfo();
2387 setTreeInfoToDefaults(artwork_new, type);
2390 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2391 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2392 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2394 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2395 setString(&artwork_new->name, UNDEFINED_FILENAME);
2396 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2398 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2399 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2400 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2402 checked_free(artwork_new->name);
2404 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2405 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2406 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2412 void LoadArtworkInfo()
2414 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2416 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2417 options.graphics_directory,
2418 TREE_TYPE_GRAPHICS_DIR);
2419 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2420 getUserGraphicsDir(),
2421 TREE_TYPE_GRAPHICS_DIR);
2423 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2424 options.sounds_directory,
2425 TREE_TYPE_SOUNDS_DIR);
2426 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2428 TREE_TYPE_SOUNDS_DIR);
2430 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2431 options.music_directory,
2432 TREE_TYPE_MUSIC_DIR);
2433 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2435 TREE_TYPE_MUSIC_DIR);
2437 if (artwork.gfx_first == NULL)
2438 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2439 if (artwork.snd_first == NULL)
2440 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2441 if (artwork.mus_first == NULL)
2442 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2444 /* before sorting, the first entries will be from the user directory */
2445 artwork.gfx_current =
2446 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2447 if (artwork.gfx_current == NULL)
2448 artwork.gfx_current =
2449 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2450 if (artwork.gfx_current == NULL)
2451 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2453 artwork.snd_current =
2454 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2455 if (artwork.snd_current == NULL)
2456 artwork.snd_current =
2457 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2458 if (artwork.snd_current == NULL)
2459 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2461 artwork.mus_current =
2462 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2463 if (artwork.mus_current == NULL)
2464 artwork.mus_current =
2465 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2466 if (artwork.mus_current == NULL)
2467 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2469 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2470 artwork.snd_current_identifier = artwork.snd_current->identifier;
2471 artwork.mus_current_identifier = artwork.mus_current->identifier;
2474 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2475 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2476 printf("music set == %s\n\n", artwork.mus_current_identifier);
2479 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2480 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2481 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2484 dumpTreeInfo(artwork.gfx_first, 0);
2485 dumpTreeInfo(artwork.snd_first, 0);
2486 dumpTreeInfo(artwork.mus_first, 0);
2490 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2491 LevelDirTree *level_node)
2493 /* recursively check all level directories for artwork sub-directories */
2497 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2498 ARTWORK_DIRECTORY((*artwork_node)->type));
2501 if (!level_node->parent_link)
2502 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2503 level_node->subdir, level_node->name);
2506 if (!level_node->parent_link)
2508 TreeInfo *topnode_last = *artwork_node;
2510 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2511 (*artwork_node)->type);
2513 if (topnode_last != *artwork_node)
2515 free((*artwork_node)->identifier);
2516 free((*artwork_node)->name);
2517 free((*artwork_node)->name_sorting);
2519 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2520 (*artwork_node)->name = getStringCopy(level_node->name);
2521 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2523 (*artwork_node)->sort_priority = level_node->sort_priority;
2524 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2530 if (level_node->node_group != NULL)
2531 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2533 level_node = level_node->next;
2537 void LoadLevelArtworkInfo()
2539 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2541 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2542 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2543 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2545 /* needed for reloading level artwork not known at ealier stage */
2547 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2549 artwork.gfx_current =
2550 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2551 if (artwork.gfx_current == NULL)
2552 artwork.gfx_current =
2553 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2554 if (artwork.gfx_current == NULL)
2555 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2558 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2560 artwork.snd_current =
2561 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2562 if (artwork.snd_current == NULL)
2563 artwork.snd_current =
2564 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2565 if (artwork.snd_current == NULL)
2566 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2569 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2571 artwork.mus_current =
2572 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2573 if (artwork.mus_current == NULL)
2574 artwork.mus_current =
2575 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2576 if (artwork.mus_current == NULL)
2577 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2580 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2581 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2582 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2585 dumpTreeInfo(artwork.gfx_first, 0);
2586 dumpTreeInfo(artwork.snd_first, 0);
2587 dumpTreeInfo(artwork.mus_first, 0);
2591 static void SaveUserLevelInfo()
2593 LevelDirTree *level_info;
2598 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2600 if (!(file = fopen(filename, MODE_WRITE)))
2602 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2607 level_info = newTreeInfo();
2609 /* always start with reliable default values */
2610 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2613 setString(&level_info->name, getLoginName());
2614 setString(&level_info->author, getRealName());
2615 level_info->levels = 100;
2616 level_info->first_level = 1;
2617 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2618 level_info->readonly = FALSE;
2619 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2620 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2621 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2623 ldi.name = getStringCopy(getLoginName());
2624 ldi.author = getStringCopy(getRealName());
2626 ldi.first_level = 1;
2627 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2628 ldi.readonly = FALSE;
2629 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2630 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2631 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2634 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2635 getCookie("LEVELINFO")));
2638 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2639 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2640 i != LEVELINFO_TOKEN_NAME_SORTING &&
2641 i != LEVELINFO_TOKEN_IMPORTED_FROM &&
2642 i != LEVELINFO_TOKEN_FILENAME &&
2643 i != LEVELINFO_TOKEN_FILETYPE)
2644 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2648 SetFilePermissions(filename, PERMS_PRIVATE);
2650 freeTreeInfo(level_info);
2654 char *getSetupValue(int type, void *value)
2656 static char value_string[MAX_LINE_LEN];
2664 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2668 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2672 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2676 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2680 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2684 sprintf(value_string, "%d", *(int *)value);
2688 strcpy(value_string, *(char **)value);
2692 value_string[0] = '\0';
2696 return value_string;
2699 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2703 static char token_string[MAX_LINE_LEN];
2704 int token_type = token_info[token_nr].type;
2705 void *setup_value = token_info[token_nr].value;
2706 char *token_text = token_info[token_nr].text;
2707 char *value_string = getSetupValue(token_type, setup_value);
2709 /* build complete token string */
2710 sprintf(token_string, "%s%s", prefix, token_text);
2712 /* build setup entry line */
2713 line = getFormattedSetupEntry(token_string, value_string);
2715 if (token_type == TYPE_KEY_X11)
2717 Key key = *(Key *)setup_value;
2718 char *keyname = getKeyNameFromKey(key);
2720 /* add comment, if useful */
2721 if (strcmp(keyname, "(undefined)") != 0 &&
2722 strcmp(keyname, "(unknown)") != 0)
2724 /* add at least one whitespace */
2726 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2730 strcat(line, keyname);
2737 void LoadLevelSetup_LastSeries()
2739 /* ----------------------------------------------------------------------- */
2740 /* ~/.<program>/levelsetup.conf */
2741 /* ----------------------------------------------------------------------- */
2743 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2744 SetupFileHash *level_setup_hash = NULL;
2746 /* always start with reliable default values */
2747 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2749 if ((level_setup_hash = loadSetupFileHash(filename)))
2751 char *last_level_series =
2752 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2754 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2756 if (leveldir_current == NULL)
2757 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2759 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2761 freeSetupFileHash(level_setup_hash);
2764 Error(ERR_WARN, "using default setup values");
2769 void SaveLevelSetup_LastSeries()
2771 /* ----------------------------------------------------------------------- */
2772 /* ~/.<program>/levelsetup.conf */
2773 /* ----------------------------------------------------------------------- */
2775 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2776 char *level_subdir = leveldir_current->subdir;
2779 InitUserDataDirectory();
2781 if (!(file = fopen(filename, MODE_WRITE)))
2783 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2788 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2789 getCookie("LEVELSETUP")));
2790 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2795 SetFilePermissions(filename, PERMS_PRIVATE);
2800 static void checkSeriesInfo()
2802 static char *level_directory = NULL;
2804 struct dirent *dir_entry;
2806 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2808 level_directory = getPath2((leveldir_current->user_defined ?
2809 getUserLevelDir(NULL) :
2810 options.level_directory),
2811 leveldir_current->fullpath);
2813 if ((dir = opendir(level_directory)) == NULL)
2815 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2819 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2821 if (strlen(dir_entry->d_name) > 4 &&
2822 dir_entry->d_name[3] == '.' &&
2823 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2825 char levelnum_str[4];
2828 strncpy(levelnum_str, dir_entry->d_name, 3);
2829 levelnum_str[3] = '\0';
2831 levelnum_value = atoi(levelnum_str);
2834 if (levelnum_value < leveldir_current->first_level)
2836 Error(ERR_WARN, "additional level %d found", levelnum_value);
2837 leveldir_current->first_level = levelnum_value;
2839 else if (levelnum_value > leveldir_current->last_level)
2841 Error(ERR_WARN, "additional level %d found", levelnum_value);
2842 leveldir_current->last_level = levelnum_value;
2851 void LoadLevelSetup_SeriesInfo()
2854 SetupFileHash *level_setup_hash = NULL;
2855 char *level_subdir = leveldir_current->subdir;
2857 /* always start with reliable default values */
2858 level_nr = leveldir_current->first_level;
2860 checkSeriesInfo(leveldir_current);
2862 /* ----------------------------------------------------------------------- */
2863 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2864 /* ----------------------------------------------------------------------- */
2866 level_subdir = leveldir_current->subdir;
2868 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2870 if ((level_setup_hash = loadSetupFileHash(filename)))
2874 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2878 level_nr = atoi(token_value);
2880 if (level_nr < leveldir_current->first_level)
2881 level_nr = leveldir_current->first_level;
2882 if (level_nr > leveldir_current->last_level)
2883 level_nr = leveldir_current->last_level;
2886 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2890 int level_nr = atoi(token_value);
2892 if (level_nr < leveldir_current->first_level)
2893 level_nr = leveldir_current->first_level;
2894 if (level_nr > leveldir_current->last_level + 1)
2895 level_nr = leveldir_current->last_level;
2897 if (leveldir_current->user_defined || !leveldir_current->handicap)
2898 level_nr = leveldir_current->last_level;
2900 leveldir_current->handicap_level = level_nr;
2903 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2905 freeSetupFileHash(level_setup_hash);
2908 Error(ERR_WARN, "using default setup values");
2913 void SaveLevelSetup_SeriesInfo()
2916 char *level_subdir = leveldir_current->subdir;
2917 char *level_nr_str = int2str(level_nr, 0);
2918 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2921 /* ----------------------------------------------------------------------- */
2922 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2923 /* ----------------------------------------------------------------------- */
2925 InitLevelSetupDirectory(level_subdir);
2927 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2929 if (!(file = fopen(filename, MODE_WRITE)))
2931 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2936 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2937 getCookie("LEVELSETUP")));
2938 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2940 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2941 handicap_level_str));
2945 SetFilePermissions(filename, PERMS_PRIVATE);