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);
408 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
413 char *getHelpAnimFilename()
415 static char *filename = NULL;
417 checked_free(filename);
419 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
424 char *getHelpTextFilename()
426 static char *filename = NULL;
428 checked_free(filename);
430 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
435 char *getLevelSetInfoFilename()
437 static char *filename = NULL;
452 for (i = 0; basenames[i] != NULL; i++)
454 checked_free(filename);
456 filename = getPath2(getCurrentLevelDir(), basenames[i]);
457 if (fileExists(filename))
464 static char *getCorrectedArtworkBasename(char *basename)
466 char *basename_corrected = basename;
468 #if defined(PLATFORM_MSDOS)
469 if (program.filename_prefix != NULL)
471 int prefix_len = strlen(program.filename_prefix);
473 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
474 basename_corrected = &basename[prefix_len];
476 /* if corrected filename is still longer than standard MS-DOS filename
477 size (8 characters + 1 dot + 3 characters file extension), shorten
478 filename by writing file extension after 8th basename character */
479 if (strlen(basename_corrected) > 8 + 1 + 3)
481 static char *msdos_filename = NULL;
483 checked_free(msdos_filename);
485 msdos_filename = getStringCopy(basename_corrected);
486 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
488 basename_corrected = msdos_filename;
493 return basename_corrected;
496 char *getCustomImageFilename(char *basename)
498 static char *filename = NULL;
499 boolean skip_setup_artwork = FALSE;
501 checked_free(filename);
503 basename = getCorrectedArtworkBasename(basename);
505 if (!setup.override_level_graphics)
507 /* 1st try: look for special artwork in current level series directory */
508 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
509 if (fileExists(filename))
514 /* check if there is special artwork configured in level series config */
515 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
517 /* 2nd try: look for special artwork configured in level series config */
518 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
519 if (fileExists(filename))
524 /* take missing artwork configured in level set config from default */
525 skip_setup_artwork = TRUE;
529 if (!skip_setup_artwork)
531 /* 3rd try: look for special artwork in configured artwork directory */
532 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
533 if (fileExists(filename))
539 /* 4th try: look for default artwork in new default artwork directory */
540 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
541 if (fileExists(filename))
546 /* 5th try: look for default artwork in old default artwork directory */
547 filename = getPath2(options.graphics_directory, basename);
548 if (fileExists(filename))
551 return NULL; /* cannot find specified artwork file anywhere */
554 char *getCustomSoundFilename(char *basename)
556 static char *filename = NULL;
557 boolean skip_setup_artwork = FALSE;
559 checked_free(filename);
561 basename = getCorrectedArtworkBasename(basename);
563 if (!setup.override_level_sounds)
565 /* 1st try: look for special artwork in current level series directory */
566 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
567 if (fileExists(filename))
572 /* check if there is special artwork configured in level series config */
573 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
575 /* 2nd try: look for special artwork configured in level series config */
576 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
577 if (fileExists(filename))
582 /* take missing artwork configured in level set config from default */
583 skip_setup_artwork = TRUE;
587 if (!skip_setup_artwork)
589 /* 3rd try: look for special artwork in configured artwork directory */
590 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
591 if (fileExists(filename))
597 /* 4th try: look for default artwork in new default artwork directory */
598 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
599 if (fileExists(filename))
604 /* 5th try: look for default artwork in old default artwork directory */
605 filename = getPath2(options.sounds_directory, basename);
606 if (fileExists(filename))
609 return NULL; /* cannot find specified artwork file anywhere */
612 char *getCustomMusicFilename(char *basename)
614 static char *filename = NULL;
615 boolean skip_setup_artwork = FALSE;
617 checked_free(filename);
619 basename = getCorrectedArtworkBasename(basename);
621 if (!setup.override_level_music)
623 /* 1st try: look for special artwork in current level series directory */
624 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
625 if (fileExists(filename))
630 /* check if there is special artwork configured in level series config */
631 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
633 /* 2nd try: look for special artwork configured in level series config */
634 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
635 if (fileExists(filename))
640 /* take missing artwork configured in level set config from default */
641 skip_setup_artwork = TRUE;
645 if (!skip_setup_artwork)
647 /* 3rd try: look for special artwork in configured artwork directory */
648 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
649 if (fileExists(filename))
655 /* 4th try: look for default artwork in new default artwork directory */
656 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
657 if (fileExists(filename))
662 /* 5th try: look for default artwork in old default artwork directory */
663 filename = getPath2(options.music_directory, basename);
664 if (fileExists(filename))
667 return NULL; /* cannot find specified artwork file anywhere */
670 char *getCustomArtworkFilename(char *basename, int type)
672 if (type == ARTWORK_TYPE_GRAPHICS)
673 return getCustomImageFilename(basename);
674 else if (type == ARTWORK_TYPE_SOUNDS)
675 return getCustomSoundFilename(basename);
676 else if (type == ARTWORK_TYPE_MUSIC)
677 return getCustomMusicFilename(basename);
679 return UNDEFINED_FILENAME;
682 char *getCustomArtworkConfigFilename(int type)
684 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
687 char *getCustomArtworkLevelConfigFilename(int type)
689 static char *filename = NULL;
691 checked_free(filename);
693 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
698 char *getCustomMusicDirectory(void)
700 static char *directory = NULL;
701 boolean skip_setup_artwork = FALSE;
703 checked_free(directory);
705 if (!setup.override_level_music)
707 /* 1st try: look for special artwork in current level series directory */
708 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
709 if (fileExists(directory))
714 /* check if there is special artwork configured in level series config */
715 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
717 /* 2nd try: look for special artwork configured in level series config */
718 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
719 if (fileExists(directory))
724 /* take missing artwork configured in level set config from default */
725 skip_setup_artwork = TRUE;
729 if (!skip_setup_artwork)
731 /* 3rd try: look for special artwork in configured artwork directory */
732 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
733 if (fileExists(directory))
739 /* 4th try: look for default artwork in new default artwork directory */
740 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
741 if (fileExists(directory))
746 /* 5th try: look for default artwork in old default artwork directory */
747 directory = getStringCopy(options.music_directory);
748 if (fileExists(directory))
751 return NULL; /* cannot find specified artwork file anywhere */
754 void InitTapeDirectory(char *level_subdir)
756 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
757 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
758 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
761 void InitScoreDirectory(char *level_subdir)
763 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
764 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
765 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
768 static void SaveUserLevelInfo();
770 void InitUserLevelDirectory(char *level_subdir)
772 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
774 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
775 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
776 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
782 void InitLevelSetupDirectory(char *level_subdir)
784 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
785 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
786 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
790 /* ------------------------------------------------------------------------- */
791 /* some functions to handle lists of level directories */
792 /* ------------------------------------------------------------------------- */
794 TreeInfo *newTreeInfo()
796 return checked_calloc(sizeof(TreeInfo));
799 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
801 node_new->next = *node_first;
802 *node_first = node_new;
805 int numTreeInfo(TreeInfo *node)
818 boolean validLevelSeries(TreeInfo *node)
820 return (node != NULL && !node->node_group && !node->parent_link);
823 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
828 if (node->node_group) /* enter level group (step down into tree) */
829 return getFirstValidTreeInfoEntry(node->node_group);
830 else if (node->parent_link) /* skip start entry of level group */
832 if (node->next) /* get first real level series entry */
833 return getFirstValidTreeInfoEntry(node->next);
834 else /* leave empty level group and go on */
835 return getFirstValidTreeInfoEntry(node->node_parent->next);
837 else /* this seems to be a regular level series */
841 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
846 if (node->node_parent == NULL) /* top level group */
847 return *node->node_top;
848 else /* sub level group */
849 return node->node_parent->node_group;
852 int numTreeInfoInGroup(TreeInfo *node)
854 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
857 int posTreeInfo(TreeInfo *node)
859 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
864 if (node_cmp == node)
868 node_cmp = node_cmp->next;
874 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
876 TreeInfo *node_default = node;
891 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
893 if (identifier == NULL)
898 if (node->node_group)
900 TreeInfo *node_group;
902 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
907 else if (!node->parent_link)
909 if (strcmp(identifier, node->identifier) == 0)
919 void dumpTreeInfo(TreeInfo *node, int depth)
923 printf("Dumping TreeInfo:\n");
927 for (i = 0; i < (depth + 1) * 3; i++)
931 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
932 node->subdir, node->fullpath, node->basepath, node->user_defined);
934 printf("subdir == '%s' (%s) [%s] (%d)\n",
935 node->subdir, node->name, node->identifier, node->sort_priority);
938 if (node->node_group != NULL)
939 dumpTreeInfo(node->node_group, depth + 1);
945 void sortTreeInfo(TreeInfo **node_first,
946 int (*compare_function)(const void *, const void *))
948 int num_nodes = numTreeInfo(*node_first);
949 TreeInfo **sort_array;
950 TreeInfo *node = *node_first;
956 /* allocate array for sorting structure pointers */
957 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
959 /* writing structure pointers to sorting array */
960 while (i < num_nodes && node) /* double boundary check... */
962 sort_array[i] = node;
968 /* sorting the structure pointers in the sorting array */
969 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
972 /* update the linkage of list elements with the sorted node array */
973 for (i = 0; i < num_nodes - 1; i++)
974 sort_array[i]->next = sort_array[i + 1];
975 sort_array[num_nodes - 1]->next = NULL;
977 /* update the linkage of the main list anchor pointer */
978 *node_first = sort_array[0];
982 /* now recursively sort the level group structures */
986 if (node->node_group != NULL)
987 sortTreeInfo(&node->node_group, compare_function);
994 /* ========================================================================= */
995 /* some stuff from "files.c" */
996 /* ========================================================================= */
998 #if defined(PLATFORM_WIN32)
1000 #define S_IRGRP S_IRUSR
1003 #define S_IROTH S_IRUSR
1006 #define S_IWGRP S_IWUSR
1009 #define S_IWOTH S_IWUSR
1012 #define S_IXGRP S_IXUSR
1015 #define S_IXOTH S_IXUSR
1018 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1023 #endif /* PLATFORM_WIN32 */
1025 /* file permissions for newly written files */
1026 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1027 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1028 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1030 #define MODE_W_PRIVATE (S_IWUSR)
1031 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1032 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1034 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1035 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1037 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1038 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1040 char *getUserDataDir(void)
1042 static char *userdata_dir = NULL;
1044 if (userdata_dir == NULL)
1045 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1047 return userdata_dir;
1050 char *getCommonDataDir(void)
1052 static char *common_data_dir = NULL;
1054 #if defined(PLATFORM_WIN32)
1055 if (common_data_dir == NULL)
1057 char *dir = checked_malloc(MAX_PATH + 1);
1059 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1060 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1061 common_data_dir = getPath2(dir, program.userdata_directory);
1063 common_data_dir = options.rw_base_directory;
1066 if (common_data_dir == NULL)
1067 common_data_dir = options.rw_base_directory;
1070 return common_data_dir;
1075 return getUserDataDir();
1078 static mode_t posix_umask(mode_t mask)
1080 #if defined(PLATFORM_UNIX)
1087 static int posix_mkdir(const char *pathname, mode_t mode)
1089 #if defined(PLATFORM_WIN32)
1090 return mkdir(pathname);
1092 return mkdir(pathname, mode);
1096 void createDirectory(char *dir, char *text, int permission_class)
1098 /* leave "other" permissions in umask untouched, but ensure group parts
1099 of USERDATA_DIR_MODE are not masked */
1100 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1101 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1102 mode_t normal_umask = posix_umask(0);
1103 mode_t group_umask = ~(dir_mode & S_IRWXG);
1104 posix_umask(normal_umask & group_umask);
1106 if (access(dir, F_OK) != 0)
1107 if (posix_mkdir(dir, dir_mode) != 0)
1108 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1110 posix_umask(normal_umask); /* reset normal umask */
1113 void InitUserDataDirectory()
1115 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1118 void SetFilePermissions(char *filename, int permission_class)
1120 chmod(filename, (permission_class == PERMS_PRIVATE ?
1121 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1124 char *getCookie(char *file_type)
1126 static char cookie[MAX_COOKIE_LEN + 1];
1128 if (strlen(program.cookie_prefix) + 1 +
1129 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1130 return "[COOKIE ERROR]"; /* should never happen */
1132 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1133 program.cookie_prefix, file_type,
1134 program.version_major, program.version_minor);
1139 int getFileVersionFromCookieString(const char *cookie)
1141 const char *ptr_cookie1, *ptr_cookie2;
1142 const char *pattern1 = "_FILE_VERSION_";
1143 const char *pattern2 = "?.?";
1144 const int len_cookie = strlen(cookie);
1145 const int len_pattern1 = strlen(pattern1);
1146 const int len_pattern2 = strlen(pattern2);
1147 const int len_pattern = len_pattern1 + len_pattern2;
1148 int version_major, version_minor;
1150 if (len_cookie <= len_pattern)
1153 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1154 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1156 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1159 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1160 ptr_cookie2[1] != '.' ||
1161 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1164 version_major = ptr_cookie2[0] - '0';
1165 version_minor = ptr_cookie2[2] - '0';
1167 return VERSION_IDENT(version_major, version_minor, 0, 0);
1170 boolean checkCookieString(const char *cookie, const char *template)
1172 const char *pattern = "_FILE_VERSION_?.?";
1173 const int len_cookie = strlen(cookie);
1174 const int len_template = strlen(template);
1175 const int len_pattern = strlen(pattern);
1177 if (len_cookie != len_template)
1180 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1186 /* ------------------------------------------------------------------------- */
1187 /* setup file list and hash handling functions */
1188 /* ------------------------------------------------------------------------- */
1190 char *getFormattedSetupEntry(char *token, char *value)
1193 static char entry[MAX_LINE_LEN];
1195 /* if value is an empty string, just return token without value */
1199 /* start with the token and some spaces to format output line */
1200 sprintf(entry, "%s:", token);
1201 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1204 /* continue with the token's value */
1205 strcat(entry, value);
1210 SetupFileList *newSetupFileList(char *token, char *value)
1212 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1214 new->token = getStringCopy(token);
1215 new->value = getStringCopy(value);
1222 void freeSetupFileList(SetupFileList *list)
1227 checked_free(list->token);
1228 checked_free(list->value);
1231 freeSetupFileList(list->next);
1236 char *getListEntry(SetupFileList *list, char *token)
1241 if (strcmp(list->token, token) == 0)
1244 return getListEntry(list->next, token);
1247 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1252 if (strcmp(list->token, token) == 0)
1254 checked_free(list->value);
1256 list->value = getStringCopy(value);
1260 else if (list->next == NULL)
1261 return (list->next = newSetupFileList(token, value));
1263 return setListEntry(list->next, token, value);
1266 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1271 if (list->next == NULL)
1272 return (list->next = newSetupFileList(token, value));
1274 return addListEntry(list->next, token, value);
1278 static void printSetupFileList(SetupFileList *list)
1283 printf("token: '%s'\n", list->token);
1284 printf("value: '%s'\n", list->value);
1286 printSetupFileList(list->next);
1291 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1292 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1293 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1294 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1296 #define insert_hash_entry hashtable_insert
1297 #define search_hash_entry hashtable_search
1298 #define change_hash_entry hashtable_change
1299 #define remove_hash_entry hashtable_remove
1302 static unsigned int get_hash_from_key(void *key)
1307 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1308 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1309 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1310 it works better than many other constants, prime or not) has never been
1311 adequately explained.
1313 If you just want to have a good hash function, and cannot wait, djb2
1314 is one of the best string hash functions i know. It has excellent
1315 distribution and speed on many different sets of keys and table sizes.
1316 You are not likely to do better with one of the "well known" functions
1317 such as PJW, K&R, etc.
1319 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1322 char *str = (char *)key;
1323 unsigned int hash = 5381;
1326 while ((c = *str++))
1327 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1332 static int keys_are_equal(void *key1, void *key2)
1334 return (strcmp((char *)key1, (char *)key2) == 0);
1337 SetupFileHash *newSetupFileHash()
1339 SetupFileHash *new_hash =
1340 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1342 if (new_hash == NULL)
1343 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1348 void freeSetupFileHash(SetupFileHash *hash)
1353 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1356 char *getHashEntry(SetupFileHash *hash, char *token)
1361 return search_hash_entry(hash, token);
1364 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1371 value_copy = getStringCopy(value);
1373 /* change value; if it does not exist, insert it as new */
1374 if (!change_hash_entry(hash, token, value_copy))
1375 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1376 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1379 char *removeHashEntry(SetupFileHash *hash, char *token)
1384 return remove_hash_entry(hash, token);
1389 static void printSetupFileHash(SetupFileHash *hash)
1391 BEGIN_HASH_ITERATION(hash, itr)
1393 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1394 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1396 END_HASH_ITERATION(hash, itr)
1401 static void *loadSetupFileData(char *filename, boolean use_hash)
1403 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1404 char *token, *value, *line_ptr;
1405 void *setup_file_data, *insert_ptr = NULL;
1406 boolean read_continued_line = FALSE;
1410 setup_file_data = newSetupFileHash();
1412 insert_ptr = setup_file_data = newSetupFileList("", "");
1414 if (!(file = fopen(filename, MODE_READ)))
1416 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1422 /* read next line of input file */
1423 if (!fgets(line, MAX_LINE_LEN, file))
1426 /* cut trailing newline or carriage return */
1427 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1428 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1431 if (read_continued_line)
1433 /* cut leading whitespaces from input line */
1434 for (line_ptr = line; *line_ptr; line_ptr++)
1435 if (*line_ptr != ' ' && *line_ptr != '\t')
1438 /* append new line to existing line, if there is enough space */
1439 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1440 strcat(previous_line, line_ptr);
1442 strcpy(line, previous_line); /* copy storage buffer to line */
1444 read_continued_line = FALSE;
1447 /* if the last character is '\', continue at next line */
1448 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1450 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1451 strcpy(previous_line, line); /* copy line to storage buffer */
1453 read_continued_line = TRUE;
1458 /* cut trailing comment from input line */
1459 for (line_ptr = line; *line_ptr; line_ptr++)
1461 if (*line_ptr == '#')
1468 /* cut trailing whitespaces from input line */
1469 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1470 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1473 /* ignore empty lines */
1477 /* cut leading whitespaces from token */
1478 for (token = line; *token; token++)
1479 if (*token != ' ' && *token != '\t')
1482 /* start with empty value as reliable default */
1485 /* find end of token to determine start of value */
1486 for (line_ptr = token; *line_ptr; line_ptr++)
1488 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1490 *line_ptr = '\0'; /* terminate token string */
1491 value = line_ptr + 1; /* set beginning of value */
1497 /* cut leading whitespaces from value */
1498 for (; *value; value++)
1499 if (*value != ' ' && *value != '\t')
1504 value = "true"; /* treat tokens without value as "true" */
1510 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1512 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1520 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1521 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1525 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1526 SetupFileList *first_valid_list_entry = setup_file_list->next;
1528 /* free empty list header */
1529 setup_file_list->next = NULL;
1530 freeSetupFileList(setup_file_list);
1531 setup_file_data = first_valid_list_entry;
1533 if (first_valid_list_entry == NULL)
1534 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1537 return setup_file_data;
1540 SetupFileList *loadSetupFileList(char *filename)
1542 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1545 SetupFileHash *loadSetupFileHash(char *filename)
1547 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1550 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1553 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1556 Error(ERR_WARN, "configuration file has no file identifier");
1557 else if (!checkCookieString(value, identifier))
1558 Error(ERR_WARN, "configuration file has wrong file identifier");
1562 /* ========================================================================= */
1563 /* setup file stuff */
1564 /* ========================================================================= */
1566 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1567 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1568 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1570 /* level directory info */
1571 #define LEVELINFO_TOKEN_IDENTIFIER 0
1572 #define LEVELINFO_TOKEN_NAME 1
1573 #define LEVELINFO_TOKEN_NAME_SORTING 2
1574 #define LEVELINFO_TOKEN_AUTHOR 3
1575 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1576 #define LEVELINFO_TOKEN_LEVELS 5
1577 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1578 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1579 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1580 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1581 #define LEVELINFO_TOKEN_READONLY 10
1582 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1583 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1584 #define LEVELINFO_TOKEN_MUSIC_SET 13
1585 #define LEVELINFO_TOKEN_FILENAME 14
1586 #define LEVELINFO_TOKEN_FILETYPE 15
1588 #define NUM_LEVELINFO_TOKENS 16
1590 static LevelDirTree ldi;
1592 static struct TokenInfo levelinfo_tokens[] =
1594 /* level directory info */
1595 { TYPE_STRING, &ldi.identifier, "identifier" },
1596 { TYPE_STRING, &ldi.name, "name" },
1597 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1598 { TYPE_STRING, &ldi.author, "author" },
1599 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1600 { TYPE_INTEGER, &ldi.levels, "levels" },
1601 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1602 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1603 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1604 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1605 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1606 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1607 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1608 { TYPE_STRING, &ldi.music_set, "music_set" },
1609 { TYPE_STRING, &ldi.level_filename, "filename" },
1610 { TYPE_STRING, &ldi.level_filetype, "filetype" }
1613 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1617 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1618 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1619 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1620 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1623 ldi->node_parent = NULL;
1624 ldi->node_group = NULL;
1628 ldi->cl_cursor = -1;
1631 ldi->fullpath = NULL;
1632 ldi->basepath = NULL;
1633 ldi->identifier = NULL;
1634 ldi->name = getStringCopy(ANONYMOUS_NAME);
1635 ldi->name_sorting = NULL;
1636 ldi->author = getStringCopy(ANONYMOUS_NAME);
1638 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1639 ldi->latest_engine = FALSE; /* default: get from level */
1640 ldi->parent_link = FALSE;
1641 ldi->user_defined = FALSE;
1643 ldi->class_desc = NULL;
1645 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1647 ldi->imported_from = NULL;
1649 ldi->graphics_set = NULL;
1650 ldi->sounds_set = NULL;
1651 ldi->music_set = NULL;
1652 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1653 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1654 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1656 ldi->level_filename = NULL;
1657 ldi->level_filetype = NULL;
1660 ldi->first_level = 0;
1661 ldi->last_level = 0;
1662 ldi->level_group = FALSE;
1663 ldi->handicap_level = 0;
1664 ldi->readonly = TRUE;
1668 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1672 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1674 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1680 /* copy all values from the parent structure */
1682 ldi->type = parent->type;
1684 ldi->node_top = parent->node_top;
1685 ldi->node_parent = parent;
1686 ldi->node_group = NULL;
1690 ldi->cl_cursor = -1;
1693 ldi->fullpath = NULL;
1694 ldi->basepath = NULL;
1695 ldi->identifier = NULL;
1696 ldi->name = getStringCopy(ANONYMOUS_NAME);
1697 ldi->name_sorting = NULL;
1698 ldi->author = getStringCopy(parent->author);
1700 ldi->sort_priority = parent->sort_priority;
1701 ldi->latest_engine = parent->latest_engine;
1702 ldi->parent_link = FALSE;
1703 ldi->user_defined = parent->user_defined;
1704 ldi->color = parent->color;
1705 ldi->class_desc = getStringCopy(parent->class_desc);
1707 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1709 ldi->imported_from = getStringCopy(parent->imported_from);
1711 ldi->graphics_set = NULL;
1712 ldi->sounds_set = NULL;
1713 ldi->music_set = NULL;
1714 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1715 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1716 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1718 ldi->level_filename = NULL;
1719 ldi->level_filetype = NULL;
1722 ldi->first_level = 0;
1723 ldi->last_level = 0;
1724 ldi->level_group = FALSE;
1725 ldi->handicap_level = 0;
1726 ldi->readonly = TRUE;
1732 /* first copy all values from the parent structure ... */
1735 /* ... then set all fields to default that cannot be inherited from parent.
1736 This is especially important for all those fields that can be set from
1737 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1738 calls 'free()' for all already set token values which requires that no
1739 other structure's pointer may point to them!
1743 ldi->fullpath = NULL;
1744 ldi->basepath = NULL;
1745 ldi->identifier = NULL;
1746 ldi->name = getStringCopy(ANONYMOUS_NAME);
1747 ldi->name_sorting = NULL;
1748 ldi->author = getStringCopy(parent->author);
1750 ldi->imported_from = getStringCopy(parent->imported_from);
1751 ldi->class_desc = getStringCopy(parent->class_desc);
1753 ldi->graphics_set = NULL;
1754 ldi->sounds_set = NULL;
1755 ldi->music_set = NULL;
1756 ldi->graphics_path = NULL;
1757 ldi->sounds_path = NULL;
1758 ldi->music_path = NULL;
1760 ldi->level_group = FALSE;
1761 ldi->parent_link = FALSE;
1763 ldi->node_top = parent->node_top;
1764 ldi->node_parent = parent;
1765 ldi->node_group = NULL;
1771 static void freeTreeInfo(TreeInfo *ldi)
1773 checked_free(ldi->subdir);
1774 checked_free(ldi->fullpath);
1775 checked_free(ldi->basepath);
1776 checked_free(ldi->identifier);
1778 checked_free(ldi->name);
1779 checked_free(ldi->name_sorting);
1780 checked_free(ldi->author);
1782 checked_free(ldi->class_desc);
1784 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1786 checked_free(ldi->graphics_set);
1787 checked_free(ldi->sounds_set);
1788 checked_free(ldi->music_set);
1790 checked_free(ldi->graphics_path);
1791 checked_free(ldi->sounds_path);
1792 checked_free(ldi->music_path);
1796 void setSetupInfo(struct TokenInfo *token_info,
1797 int token_nr, char *token_value)
1799 int token_type = token_info[token_nr].type;
1800 void *setup_value = token_info[token_nr].value;
1802 if (token_value == NULL)
1805 /* set setup field to corresponding token value */
1810 *(boolean *)setup_value = get_boolean_from_string(token_value);
1814 *(Key *)setup_value = getKeyFromKeyName(token_value);
1818 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1822 *(int *)setup_value = get_integer_from_string(token_value);
1826 checked_free(*(char **)setup_value);
1827 *(char **)setup_value = getStringCopy(token_value);
1835 static int compareTreeInfoEntries(const void *object1, const void *object2)
1837 const TreeInfo *entry1 = *((TreeInfo **)object1);
1838 const TreeInfo *entry2 = *((TreeInfo **)object2);
1839 int class_sorting1, class_sorting2;
1842 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1844 class_sorting1 = LEVELSORTING(entry1);
1845 class_sorting2 = LEVELSORTING(entry2);
1849 class_sorting1 = ARTWORKSORTING(entry1);
1850 class_sorting2 = ARTWORKSORTING(entry2);
1853 if (entry1->parent_link || entry2->parent_link)
1854 compare_result = (entry1->parent_link ? -1 : +1);
1855 else if (entry1->sort_priority == entry2->sort_priority)
1857 char *name1 = getStringToLower(entry1->name_sorting);
1858 char *name2 = getStringToLower(entry2->name_sorting);
1860 compare_result = strcmp(name1, name2);
1865 else if (class_sorting1 == class_sorting2)
1866 compare_result = entry1->sort_priority - entry2->sort_priority;
1868 compare_result = class_sorting1 - class_sorting2;
1870 return compare_result;
1873 static void createParentTreeInfoNode(TreeInfo *node_parent)
1877 if (node_parent == NULL)
1880 ti_new = newTreeInfo();
1881 setTreeInfoToDefaults(ti_new, node_parent->type);
1883 ti_new->node_parent = node_parent;
1884 ti_new->parent_link = TRUE;
1887 setString(&ti_new->identifier, node_parent->identifier);
1888 setString(&ti_new->name, ".. (parent directory)");
1889 setString(&ti_new->name_sorting, ti_new->name);
1891 setString(&ti_new->subdir, "..");
1892 setString(&ti_new->fullpath, node_parent->fullpath);
1894 ti_new->sort_priority = node_parent->sort_priority;
1895 ti_new->latest_engine = node_parent->latest_engine;
1897 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1899 ti_new->identifier = getStringCopy(node_parent->identifier);
1900 ti_new->name = ".. (parent directory)";
1901 ti_new->name_sorting = getStringCopy(ti_new->name);
1903 ti_new->subdir = "..";
1904 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1906 ti_new->sort_priority = node_parent->sort_priority;
1907 ti_new->latest_engine = node_parent->latest_engine;
1909 ti_new->class_desc = getLevelClassDescription(ti_new);
1912 pushTreeInfo(&node_parent->node_group, ti_new);
1915 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1916 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1918 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1919 TreeInfo *node_parent,
1920 char *level_directory,
1921 char *directory_name)
1923 char *directory_path = getPath2(level_directory, directory_name);
1924 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1925 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1926 LevelDirTree *leveldir_new = NULL;
1929 if (setup_file_hash == NULL)
1931 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1933 free(directory_path);
1939 leveldir_new = newTreeInfo();
1942 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1944 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1946 leveldir_new->subdir = getStringCopy(directory_name);
1948 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1950 /* set all structure fields according to the token/value pairs */
1951 ldi = *leveldir_new;
1952 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1953 setSetupInfo(levelinfo_tokens, i,
1954 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1955 *leveldir_new = ldi;
1958 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1959 setString(&leveldir_new->name, leveldir_new->subdir);
1961 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1963 free(leveldir_new->name);
1964 leveldir_new->name = getStringCopy(leveldir_new->subdir);
1968 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1970 if (leveldir_new->identifier == NULL)
1971 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
1973 if (leveldir_new->name_sorting == NULL)
1974 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1976 if (node_parent == NULL) /* top level group */
1978 leveldir_new->basepath = getStringCopy(level_directory);
1979 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
1981 else /* sub level group */
1983 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1984 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1987 if (leveldir_new->levels < 1)
1988 leveldir_new->levels = 1;
1990 leveldir_new->last_level =
1991 leveldir_new->first_level + leveldir_new->levels - 1;
1994 leveldir_new->user_defined =
1995 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1997 leveldir_new->user_defined =
1998 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2001 leveldir_new->color = LEVELCOLOR(leveldir_new);
2003 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2005 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2008 leveldir_new->handicap_level = /* set handicap to default value */
2009 (leveldir_new->user_defined ?
2010 leveldir_new->last_level :
2011 leveldir_new->first_level);
2013 pushTreeInfo(node_first, leveldir_new);
2015 freeSetupFileHash(setup_file_hash);
2017 if (leveldir_new->level_group)
2019 /* create node to link back to current level directory */
2020 createParentTreeInfoNode(leveldir_new);
2022 /* step into sub-directory and look for more level series */
2023 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2024 leveldir_new, directory_path);
2027 free(directory_path);
2033 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2034 TreeInfo *node_parent,
2035 char *level_directory)
2038 struct dirent *dir_entry;
2039 boolean valid_entry_found = FALSE;
2041 if ((dir = opendir(level_directory)) == NULL)
2043 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2047 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2049 struct stat file_status;
2050 char *directory_name = dir_entry->d_name;
2051 char *directory_path = getPath2(level_directory, directory_name);
2053 /* skip entries for current and parent directory */
2054 if (strcmp(directory_name, ".") == 0 ||
2055 strcmp(directory_name, "..") == 0)
2057 free(directory_path);
2061 /* find out if directory entry is itself a directory */
2062 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2063 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2065 free(directory_path);
2069 free(directory_path);
2071 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2072 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2073 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2076 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2083 if (!valid_entry_found)
2085 /* check if this directory directly contains a file "levelinfo.conf" */
2086 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2087 level_directory, ".");
2090 if (!valid_entry_found)
2091 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2095 void LoadLevelInfo()
2097 InitUserLevelDirectory(getLoginName());
2099 DrawInitText("Loading level series:", 120, FC_GREEN);
2101 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2102 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2104 /* before sorting, the first entries will be from the user directory */
2105 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2107 if (leveldir_first == NULL)
2108 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2110 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2113 dumpTreeInfo(leveldir_first, 0);
2117 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2118 TreeInfo *node_parent,
2119 char *base_directory,
2120 char *directory_name, int type)
2122 char *directory_path = getPath2(base_directory, directory_name);
2123 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2124 SetupFileHash *setup_file_hash = NULL;
2125 TreeInfo *artwork_new = NULL;
2128 if (access(filename, F_OK) == 0) /* file exists */
2129 setup_file_hash = loadSetupFileHash(filename);
2131 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2134 struct dirent *dir_entry;
2135 boolean valid_file_found = FALSE;
2137 if ((dir = opendir(directory_path)) != NULL)
2139 while ((dir_entry = readdir(dir)) != NULL)
2141 char *entry_name = dir_entry->d_name;
2143 if (FileIsArtworkType(entry_name, type))
2145 valid_file_found = TRUE;
2153 if (!valid_file_found)
2155 if (strcmp(directory_name, ".") != 0)
2156 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2158 free(directory_path);
2165 artwork_new = newTreeInfo();
2168 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2170 setTreeInfoToDefaults(artwork_new, type);
2172 artwork_new->subdir = getStringCopy(directory_name);
2174 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2177 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2180 /* set all structure fields according to the token/value pairs */
2182 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2183 setSetupInfo(levelinfo_tokens, i,
2184 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2188 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2189 setString(&artwork_new->name, artwork_new->subdir);
2191 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2193 free(artwork_new->name);
2194 artwork_new->name = getStringCopy(artwork_new->subdir);
2199 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2202 if (artwork_new->identifier == NULL)
2203 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2205 if (artwork_new->name_sorting == NULL)
2206 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2209 if (node_parent == NULL) /* top level group */
2211 artwork_new->basepath = getStringCopy(base_directory);
2212 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2214 else /* sub level group */
2216 artwork_new->basepath = getStringCopy(node_parent->basepath);
2217 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2221 artwork_new->user_defined =
2222 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2224 artwork_new->user_defined =
2225 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2228 /* (may use ".sort_priority" from "setup_file_hash" above) */
2229 artwork_new->color = ARTWORKCOLOR(artwork_new);
2231 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2233 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2236 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2239 if (artwork_new->name != NULL)
2241 free(artwork_new->name);
2242 artwork_new->name = NULL;
2247 if (artwork_new->identifier != NULL)
2249 free(artwork_new->identifier);
2250 artwork_new->identifier = NULL;
2254 if (strcmp(artwork_new->subdir, ".") == 0)
2256 if (artwork_new->user_defined)
2259 setString(&artwork_new->identifier, "private");
2261 artwork_new->identifier = getStringCopy("private");
2263 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2268 setString(&artwork_new->identifier, "classic");
2270 artwork_new->identifier = getStringCopy("classic");
2272 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2275 /* set to new values after changing ".sort_priority" */
2276 artwork_new->color = ARTWORKCOLOR(artwork_new);
2278 setString(&artwork_new->class_desc,
2279 getLevelClassDescription(artwork_new));
2281 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2287 setString(&artwork_new->identifier, artwork_new->subdir);
2289 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2294 setString(&artwork_new->name, artwork_new->identifier);
2295 setString(&artwork_new->name_sorting, artwork_new->name);
2297 artwork_new->name = getStringCopy(artwork_new->identifier);
2298 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2302 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2304 pushTreeInfo(node_first, artwork_new);
2306 freeSetupFileHash(setup_file_hash);
2308 free(directory_path);
2314 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2315 TreeInfo *node_parent,
2316 char *base_directory, int type)
2319 struct dirent *dir_entry;
2320 boolean valid_entry_found = FALSE;
2322 if ((dir = opendir(base_directory)) == NULL)
2324 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2325 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2329 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2331 struct stat file_status;
2332 char *directory_name = dir_entry->d_name;
2333 char *directory_path = getPath2(base_directory, directory_name);
2335 /* skip entries for current and parent directory */
2336 if (strcmp(directory_name, ".") == 0 ||
2337 strcmp(directory_name, "..") == 0)
2339 free(directory_path);
2343 /* find out if directory entry is itself a directory */
2344 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2345 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2347 free(directory_path);
2351 free(directory_path);
2353 /* check if this directory contains artwork with or without config file */
2354 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2356 directory_name, type);
2361 /* check if this directory directly contains artwork itself */
2362 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2363 base_directory, ".",
2365 if (!valid_entry_found)
2366 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2370 static TreeInfo *getDummyArtworkInfo(int type)
2372 /* this is only needed when there is completely no artwork available */
2373 TreeInfo *artwork_new = newTreeInfo();
2375 setTreeInfoToDefaults(artwork_new, type);
2378 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2379 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2380 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2382 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2383 setString(&artwork_new->name, UNDEFINED_FILENAME);
2384 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2386 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2387 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2388 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2390 checked_free(artwork_new->name);
2392 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2393 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2394 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2400 void LoadArtworkInfo()
2402 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2404 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2405 options.graphics_directory,
2406 TREE_TYPE_GRAPHICS_DIR);
2407 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2408 getUserGraphicsDir(),
2409 TREE_TYPE_GRAPHICS_DIR);
2411 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2412 options.sounds_directory,
2413 TREE_TYPE_SOUNDS_DIR);
2414 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2416 TREE_TYPE_SOUNDS_DIR);
2418 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2419 options.music_directory,
2420 TREE_TYPE_MUSIC_DIR);
2421 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2423 TREE_TYPE_MUSIC_DIR);
2425 if (artwork.gfx_first == NULL)
2426 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2427 if (artwork.snd_first == NULL)
2428 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2429 if (artwork.mus_first == NULL)
2430 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2432 /* before sorting, the first entries will be from the user directory */
2433 artwork.gfx_current =
2434 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2435 if (artwork.gfx_current == NULL)
2436 artwork.gfx_current =
2437 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2438 if (artwork.gfx_current == NULL)
2439 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2441 artwork.snd_current =
2442 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2443 if (artwork.snd_current == NULL)
2444 artwork.snd_current =
2445 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2446 if (artwork.snd_current == NULL)
2447 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2449 artwork.mus_current =
2450 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2451 if (artwork.mus_current == NULL)
2452 artwork.mus_current =
2453 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2454 if (artwork.mus_current == NULL)
2455 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2457 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2458 artwork.snd_current_identifier = artwork.snd_current->identifier;
2459 artwork.mus_current_identifier = artwork.mus_current->identifier;
2462 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2463 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2464 printf("music set == %s\n\n", artwork.mus_current_identifier);
2467 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2468 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2469 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2472 dumpTreeInfo(artwork.gfx_first, 0);
2473 dumpTreeInfo(artwork.snd_first, 0);
2474 dumpTreeInfo(artwork.mus_first, 0);
2478 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2479 LevelDirTree *level_node)
2481 /* recursively check all level directories for artwork sub-directories */
2485 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2486 ARTWORK_DIRECTORY((*artwork_node)->type));
2489 if (!level_node->parent_link)
2490 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2491 level_node->subdir, level_node->name);
2494 if (!level_node->parent_link)
2496 TreeInfo *topnode_last = *artwork_node;
2498 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2499 (*artwork_node)->type);
2501 if (topnode_last != *artwork_node)
2503 free((*artwork_node)->identifier);
2504 free((*artwork_node)->name);
2505 free((*artwork_node)->name_sorting);
2507 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2508 (*artwork_node)->name = getStringCopy(level_node->name);
2509 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2511 (*artwork_node)->sort_priority = level_node->sort_priority;
2512 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2518 if (level_node->node_group != NULL)
2519 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2521 level_node = level_node->next;
2525 void LoadLevelArtworkInfo()
2527 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2529 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2530 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2531 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2533 /* needed for reloading level artwork not known at ealier stage */
2535 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2537 artwork.gfx_current =
2538 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2539 if (artwork.gfx_current == NULL)
2540 artwork.gfx_current =
2541 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2542 if (artwork.gfx_current == NULL)
2543 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2546 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2548 artwork.snd_current =
2549 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2550 if (artwork.snd_current == NULL)
2551 artwork.snd_current =
2552 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2553 if (artwork.snd_current == NULL)
2554 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2557 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2559 artwork.mus_current =
2560 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2561 if (artwork.mus_current == NULL)
2562 artwork.mus_current =
2563 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2564 if (artwork.mus_current == NULL)
2565 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2568 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2569 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2570 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2573 dumpTreeInfo(artwork.gfx_first, 0);
2574 dumpTreeInfo(artwork.snd_first, 0);
2575 dumpTreeInfo(artwork.mus_first, 0);
2579 static void SaveUserLevelInfo()
2581 LevelDirTree *level_info;
2586 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2588 if (!(file = fopen(filename, MODE_WRITE)))
2590 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2595 level_info = newTreeInfo();
2597 /* always start with reliable default values */
2598 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2601 setString(&level_info->name, getLoginName());
2602 setString(&level_info->author, getRealName());
2603 level_info->levels = 100;
2604 level_info->first_level = 1;
2605 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2606 level_info->readonly = FALSE;
2607 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2608 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2609 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2611 ldi.name = getStringCopy(getLoginName());
2612 ldi.author = getStringCopy(getRealName());
2614 ldi.first_level = 1;
2615 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2616 ldi.readonly = FALSE;
2617 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2618 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2619 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2622 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2623 getCookie("LEVELINFO")));
2626 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2627 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2628 i != LEVELINFO_TOKEN_NAME_SORTING &&
2629 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2630 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2634 SetFilePermissions(filename, PERMS_PRIVATE);
2636 freeTreeInfo(level_info);
2640 char *getSetupValue(int type, void *value)
2642 static char value_string[MAX_LINE_LEN];
2650 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2654 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2658 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2662 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2666 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2670 sprintf(value_string, "%d", *(int *)value);
2674 strcpy(value_string, *(char **)value);
2678 value_string[0] = '\0';
2682 return value_string;
2685 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2689 static char token_string[MAX_LINE_LEN];
2690 int token_type = token_info[token_nr].type;
2691 void *setup_value = token_info[token_nr].value;
2692 char *token_text = token_info[token_nr].text;
2693 char *value_string = getSetupValue(token_type, setup_value);
2695 /* build complete token string */
2696 sprintf(token_string, "%s%s", prefix, token_text);
2698 /* build setup entry line */
2699 line = getFormattedSetupEntry(token_string, value_string);
2701 if (token_type == TYPE_KEY_X11)
2703 Key key = *(Key *)setup_value;
2704 char *keyname = getKeyNameFromKey(key);
2706 /* add comment, if useful */
2707 if (strcmp(keyname, "(undefined)") != 0 &&
2708 strcmp(keyname, "(unknown)") != 0)
2710 /* add at least one whitespace */
2712 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2716 strcat(line, keyname);
2723 void LoadLevelSetup_LastSeries()
2725 /* ----------------------------------------------------------------------- */
2726 /* ~/.<program>/levelsetup.conf */
2727 /* ----------------------------------------------------------------------- */
2729 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2730 SetupFileHash *level_setup_hash = NULL;
2732 /* always start with reliable default values */
2733 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2735 if ((level_setup_hash = loadSetupFileHash(filename)))
2737 char *last_level_series =
2738 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2740 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2742 if (leveldir_current == NULL)
2743 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2745 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2747 freeSetupFileHash(level_setup_hash);
2750 Error(ERR_WARN, "using default setup values");
2755 void SaveLevelSetup_LastSeries()
2757 /* ----------------------------------------------------------------------- */
2758 /* ~/.<program>/levelsetup.conf */
2759 /* ----------------------------------------------------------------------- */
2761 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2762 char *level_subdir = leveldir_current->subdir;
2765 InitUserDataDirectory();
2767 if (!(file = fopen(filename, MODE_WRITE)))
2769 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2774 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2775 getCookie("LEVELSETUP")));
2776 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2781 SetFilePermissions(filename, PERMS_PRIVATE);
2786 static void checkSeriesInfo()
2788 static char *level_directory = NULL;
2790 struct dirent *dir_entry;
2792 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2794 level_directory = getPath2((leveldir_current->user_defined ?
2795 getUserLevelDir(NULL) :
2796 options.level_directory),
2797 leveldir_current->fullpath);
2799 if ((dir = opendir(level_directory)) == NULL)
2801 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2805 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2807 if (strlen(dir_entry->d_name) > 4 &&
2808 dir_entry->d_name[3] == '.' &&
2809 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2811 char levelnum_str[4];
2814 strncpy(levelnum_str, dir_entry->d_name, 3);
2815 levelnum_str[3] = '\0';
2817 levelnum_value = atoi(levelnum_str);
2820 if (levelnum_value < leveldir_current->first_level)
2822 Error(ERR_WARN, "additional level %d found", levelnum_value);
2823 leveldir_current->first_level = levelnum_value;
2825 else if (levelnum_value > leveldir_current->last_level)
2827 Error(ERR_WARN, "additional level %d found", levelnum_value);
2828 leveldir_current->last_level = levelnum_value;
2837 void LoadLevelSetup_SeriesInfo()
2840 SetupFileHash *level_setup_hash = NULL;
2841 char *level_subdir = leveldir_current->subdir;
2843 /* always start with reliable default values */
2844 level_nr = leveldir_current->first_level;
2846 checkSeriesInfo(leveldir_current);
2848 /* ----------------------------------------------------------------------- */
2849 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2850 /* ----------------------------------------------------------------------- */
2852 level_subdir = leveldir_current->subdir;
2854 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2856 if ((level_setup_hash = loadSetupFileHash(filename)))
2860 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2864 level_nr = atoi(token_value);
2866 if (level_nr < leveldir_current->first_level)
2867 level_nr = leveldir_current->first_level;
2868 if (level_nr > leveldir_current->last_level)
2869 level_nr = leveldir_current->last_level;
2872 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2876 int level_nr = atoi(token_value);
2878 if (level_nr < leveldir_current->first_level)
2879 level_nr = leveldir_current->first_level;
2880 if (level_nr > leveldir_current->last_level + 1)
2881 level_nr = leveldir_current->last_level;
2883 if (leveldir_current->user_defined)
2884 level_nr = leveldir_current->last_level;
2886 leveldir_current->handicap_level = level_nr;
2889 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2891 freeSetupFileHash(level_setup_hash);
2894 Error(ERR_WARN, "using default setup values");
2899 void SaveLevelSetup_SeriesInfo()
2902 char *level_subdir = leveldir_current->subdir;
2903 char *level_nr_str = int2str(level_nr, 0);
2904 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2907 /* ----------------------------------------------------------------------- */
2908 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2909 /* ----------------------------------------------------------------------- */
2911 InitLevelSetupDirectory(level_subdir);
2913 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2915 if (!(file = fopen(filename, MODE_WRITE)))
2917 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2922 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2923 getCookie("LEVELSETUP")));
2924 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2926 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2927 handicap_level_str));
2931 SetFilePermissions(filename, PERMS_PRIVATE);