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
1587 #define LEVELINFO_TOKEN_HANDICAP 16
1589 #define NUM_LEVELINFO_TOKENS 17
1591 static LevelDirTree ldi;
1593 static struct TokenInfo levelinfo_tokens[] =
1595 /* level directory info */
1596 { TYPE_STRING, &ldi.identifier, "identifier" },
1597 { TYPE_STRING, &ldi.name, "name" },
1598 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1599 { TYPE_STRING, &ldi.author, "author" },
1600 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1601 { TYPE_INTEGER, &ldi.levels, "levels" },
1602 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1603 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1604 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1605 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1606 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1607 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1608 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1609 { TYPE_STRING, &ldi.music_set, "music_set" },
1610 { TYPE_STRING, &ldi.level_filename, "filename" },
1611 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1612 { TYPE_BOOLEAN, &ldi.handicap, "handicap" }
1615 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1619 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1620 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1621 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1622 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1625 ldi->node_parent = NULL;
1626 ldi->node_group = NULL;
1630 ldi->cl_cursor = -1;
1633 ldi->fullpath = NULL;
1634 ldi->basepath = NULL;
1635 ldi->identifier = NULL;
1636 ldi->name = getStringCopy(ANONYMOUS_NAME);
1637 ldi->name_sorting = NULL;
1638 ldi->author = getStringCopy(ANONYMOUS_NAME);
1640 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1641 ldi->latest_engine = FALSE; /* default: get from level */
1642 ldi->parent_link = FALSE;
1643 ldi->user_defined = FALSE;
1645 ldi->class_desc = NULL;
1647 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1649 ldi->imported_from = NULL;
1651 ldi->graphics_set = NULL;
1652 ldi->sounds_set = NULL;
1653 ldi->music_set = NULL;
1654 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1655 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1656 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1658 ldi->level_filename = NULL;
1659 ldi->level_filetype = NULL;
1662 ldi->first_level = 0;
1663 ldi->last_level = 0;
1664 ldi->level_group = FALSE;
1665 ldi->handicap_level = 0;
1666 ldi->readonly = TRUE;
1667 ldi->handicap = TRUE;
1671 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1675 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1677 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1683 /* copy all values from the parent structure */
1685 ldi->type = parent->type;
1687 ldi->node_top = parent->node_top;
1688 ldi->node_parent = parent;
1689 ldi->node_group = NULL;
1693 ldi->cl_cursor = -1;
1696 ldi->fullpath = NULL;
1697 ldi->basepath = NULL;
1698 ldi->identifier = NULL;
1699 ldi->name = getStringCopy(ANONYMOUS_NAME);
1700 ldi->name_sorting = NULL;
1701 ldi->author = getStringCopy(parent->author);
1703 ldi->sort_priority = parent->sort_priority;
1704 ldi->latest_engine = parent->latest_engine;
1705 ldi->parent_link = FALSE;
1706 ldi->user_defined = parent->user_defined;
1707 ldi->color = parent->color;
1708 ldi->class_desc = getStringCopy(parent->class_desc);
1710 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1712 ldi->imported_from = getStringCopy(parent->imported_from);
1714 ldi->graphics_set = NULL;
1715 ldi->sounds_set = NULL;
1716 ldi->music_set = NULL;
1717 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1718 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1719 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1721 ldi->level_filename = NULL;
1722 ldi->level_filetype = NULL;
1725 ldi->first_level = 0;
1726 ldi->last_level = 0;
1727 ldi->level_group = FALSE;
1728 ldi->handicap_level = 0;
1729 ldi->readonly = TRUE;
1730 ldi->handicap = TRUE;
1735 /* first copy all values from the parent structure ... */
1738 /* ... then set all fields to default that cannot be inherited from parent.
1739 This is especially important for all those fields that can be set from
1740 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1741 calls 'free()' for all already set token values which requires that no
1742 other structure's pointer may point to them!
1746 ldi->fullpath = NULL;
1747 ldi->basepath = NULL;
1748 ldi->identifier = NULL;
1749 ldi->name = getStringCopy(ANONYMOUS_NAME);
1750 ldi->name_sorting = NULL;
1751 ldi->author = getStringCopy(parent->author);
1753 ldi->imported_from = getStringCopy(parent->imported_from);
1754 ldi->class_desc = getStringCopy(parent->class_desc);
1756 ldi->graphics_set = NULL;
1757 ldi->sounds_set = NULL;
1758 ldi->music_set = NULL;
1759 ldi->graphics_path = NULL;
1760 ldi->sounds_path = NULL;
1761 ldi->music_path = NULL;
1763 ldi->level_group = FALSE;
1764 ldi->parent_link = FALSE;
1766 ldi->node_top = parent->node_top;
1767 ldi->node_parent = parent;
1768 ldi->node_group = NULL;
1774 static void freeTreeInfo(TreeInfo *ldi)
1776 checked_free(ldi->subdir);
1777 checked_free(ldi->fullpath);
1778 checked_free(ldi->basepath);
1779 checked_free(ldi->identifier);
1781 checked_free(ldi->name);
1782 checked_free(ldi->name_sorting);
1783 checked_free(ldi->author);
1785 checked_free(ldi->class_desc);
1787 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1789 checked_free(ldi->imported_from);
1791 checked_free(ldi->graphics_set);
1792 checked_free(ldi->sounds_set);
1793 checked_free(ldi->music_set);
1795 checked_free(ldi->graphics_path);
1796 checked_free(ldi->sounds_path);
1797 checked_free(ldi->music_path);
1799 checked_free(ldi->level_filename);
1800 checked_free(ldi->level_filetype);
1804 void setSetupInfo(struct TokenInfo *token_info,
1805 int token_nr, char *token_value)
1807 int token_type = token_info[token_nr].type;
1808 void *setup_value = token_info[token_nr].value;
1810 if (token_value == NULL)
1813 /* set setup field to corresponding token value */
1818 *(boolean *)setup_value = get_boolean_from_string(token_value);
1822 *(Key *)setup_value = getKeyFromKeyName(token_value);
1826 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1830 *(int *)setup_value = get_integer_from_string(token_value);
1834 checked_free(*(char **)setup_value);
1835 *(char **)setup_value = getStringCopy(token_value);
1843 static int compareTreeInfoEntries(const void *object1, const void *object2)
1845 const TreeInfo *entry1 = *((TreeInfo **)object1);
1846 const TreeInfo *entry2 = *((TreeInfo **)object2);
1847 int class_sorting1, class_sorting2;
1850 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1852 class_sorting1 = LEVELSORTING(entry1);
1853 class_sorting2 = LEVELSORTING(entry2);
1857 class_sorting1 = ARTWORKSORTING(entry1);
1858 class_sorting2 = ARTWORKSORTING(entry2);
1861 if (entry1->parent_link || entry2->parent_link)
1862 compare_result = (entry1->parent_link ? -1 : +1);
1863 else if (entry1->sort_priority == entry2->sort_priority)
1865 char *name1 = getStringToLower(entry1->name_sorting);
1866 char *name2 = getStringToLower(entry2->name_sorting);
1868 compare_result = strcmp(name1, name2);
1873 else if (class_sorting1 == class_sorting2)
1874 compare_result = entry1->sort_priority - entry2->sort_priority;
1876 compare_result = class_sorting1 - class_sorting2;
1878 return compare_result;
1881 static void createParentTreeInfoNode(TreeInfo *node_parent)
1885 if (node_parent == NULL)
1888 ti_new = newTreeInfo();
1889 setTreeInfoToDefaults(ti_new, node_parent->type);
1891 ti_new->node_parent = node_parent;
1892 ti_new->parent_link = TRUE;
1895 setString(&ti_new->identifier, node_parent->identifier);
1896 setString(&ti_new->name, ".. (parent directory)");
1897 setString(&ti_new->name_sorting, ti_new->name);
1899 setString(&ti_new->subdir, "..");
1900 setString(&ti_new->fullpath, node_parent->fullpath);
1902 ti_new->sort_priority = node_parent->sort_priority;
1903 ti_new->latest_engine = node_parent->latest_engine;
1905 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1907 ti_new->identifier = getStringCopy(node_parent->identifier);
1908 ti_new->name = ".. (parent directory)";
1909 ti_new->name_sorting = getStringCopy(ti_new->name);
1911 ti_new->subdir = "..";
1912 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1914 ti_new->sort_priority = node_parent->sort_priority;
1915 ti_new->latest_engine = node_parent->latest_engine;
1917 ti_new->class_desc = getLevelClassDescription(ti_new);
1920 pushTreeInfo(&node_parent->node_group, ti_new);
1923 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1924 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1926 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1927 TreeInfo *node_parent,
1928 char *level_directory,
1929 char *directory_name)
1931 char *directory_path = getPath2(level_directory, directory_name);
1932 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1933 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1934 LevelDirTree *leveldir_new = NULL;
1937 if (setup_file_hash == NULL)
1939 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1941 free(directory_path);
1947 leveldir_new = newTreeInfo();
1950 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1952 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1954 leveldir_new->subdir = getStringCopy(directory_name);
1956 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1958 /* set all structure fields according to the token/value pairs */
1959 ldi = *leveldir_new;
1960 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1961 setSetupInfo(levelinfo_tokens, i,
1962 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1963 *leveldir_new = ldi;
1966 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1967 setString(&leveldir_new->name, leveldir_new->subdir);
1969 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1971 free(leveldir_new->name);
1972 leveldir_new->name = getStringCopy(leveldir_new->subdir);
1976 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1978 if (leveldir_new->identifier == NULL)
1979 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
1981 if (leveldir_new->name_sorting == NULL)
1982 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1984 if (node_parent == NULL) /* top level group */
1986 leveldir_new->basepath = getStringCopy(level_directory);
1987 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
1989 else /* sub level group */
1991 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1992 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1995 if (leveldir_new->levels < 1)
1996 leveldir_new->levels = 1;
1998 leveldir_new->last_level =
1999 leveldir_new->first_level + leveldir_new->levels - 1;
2002 leveldir_new->user_defined =
2003 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2005 leveldir_new->user_defined =
2006 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2009 leveldir_new->color = LEVELCOLOR(leveldir_new);
2011 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2013 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2016 leveldir_new->handicap_level = /* set handicap to default value */
2017 (leveldir_new->user_defined || !leveldir_new->handicap ?
2018 leveldir_new->last_level : leveldir_new->first_level);
2020 pushTreeInfo(node_first, leveldir_new);
2022 freeSetupFileHash(setup_file_hash);
2024 if (leveldir_new->level_group)
2026 /* create node to link back to current level directory */
2027 createParentTreeInfoNode(leveldir_new);
2029 /* step into sub-directory and look for more level series */
2030 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2031 leveldir_new, directory_path);
2034 free(directory_path);
2040 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2041 TreeInfo *node_parent,
2042 char *level_directory)
2045 struct dirent *dir_entry;
2046 boolean valid_entry_found = FALSE;
2048 if ((dir = opendir(level_directory)) == NULL)
2050 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2054 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2056 struct stat file_status;
2057 char *directory_name = dir_entry->d_name;
2058 char *directory_path = getPath2(level_directory, directory_name);
2060 /* skip entries for current and parent directory */
2061 if (strcmp(directory_name, ".") == 0 ||
2062 strcmp(directory_name, "..") == 0)
2064 free(directory_path);
2068 /* find out if directory entry is itself a directory */
2069 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2070 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2072 free(directory_path);
2076 free(directory_path);
2078 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2079 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2080 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2083 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2090 if (!valid_entry_found)
2092 /* check if this directory directly contains a file "levelinfo.conf" */
2093 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2094 level_directory, ".");
2097 if (!valid_entry_found)
2098 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2102 void LoadLevelInfo()
2104 InitUserLevelDirectory(getLoginName());
2106 DrawInitText("Loading level series:", 120, FC_GREEN);
2108 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2109 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2111 /* before sorting, the first entries will be from the user directory */
2112 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2114 if (leveldir_first == NULL)
2115 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2117 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2120 dumpTreeInfo(leveldir_first, 0);
2124 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2125 TreeInfo *node_parent,
2126 char *base_directory,
2127 char *directory_name, int type)
2129 char *directory_path = getPath2(base_directory, directory_name);
2130 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2131 SetupFileHash *setup_file_hash = NULL;
2132 TreeInfo *artwork_new = NULL;
2135 if (access(filename, F_OK) == 0) /* file exists */
2136 setup_file_hash = loadSetupFileHash(filename);
2138 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2141 struct dirent *dir_entry;
2142 boolean valid_file_found = FALSE;
2144 if ((dir = opendir(directory_path)) != NULL)
2146 while ((dir_entry = readdir(dir)) != NULL)
2148 char *entry_name = dir_entry->d_name;
2150 if (FileIsArtworkType(entry_name, type))
2152 valid_file_found = TRUE;
2160 if (!valid_file_found)
2162 if (strcmp(directory_name, ".") != 0)
2163 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2165 free(directory_path);
2172 artwork_new = newTreeInfo();
2175 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2177 setTreeInfoToDefaults(artwork_new, type);
2179 artwork_new->subdir = getStringCopy(directory_name);
2181 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2184 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2187 /* set all structure fields according to the token/value pairs */
2189 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2190 setSetupInfo(levelinfo_tokens, i,
2191 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2195 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2196 setString(&artwork_new->name, artwork_new->subdir);
2198 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2200 free(artwork_new->name);
2201 artwork_new->name = getStringCopy(artwork_new->subdir);
2206 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2209 if (artwork_new->identifier == NULL)
2210 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2212 if (artwork_new->name_sorting == NULL)
2213 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2216 if (node_parent == NULL) /* top level group */
2218 artwork_new->basepath = getStringCopy(base_directory);
2219 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2221 else /* sub level group */
2223 artwork_new->basepath = getStringCopy(node_parent->basepath);
2224 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2228 artwork_new->user_defined =
2229 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2231 artwork_new->user_defined =
2232 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2235 /* (may use ".sort_priority" from "setup_file_hash" above) */
2236 artwork_new->color = ARTWORKCOLOR(artwork_new);
2238 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2240 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2243 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2246 if (artwork_new->name != NULL)
2248 free(artwork_new->name);
2249 artwork_new->name = NULL;
2254 if (artwork_new->identifier != NULL)
2256 free(artwork_new->identifier);
2257 artwork_new->identifier = NULL;
2261 if (strcmp(artwork_new->subdir, ".") == 0)
2263 if (artwork_new->user_defined)
2266 setString(&artwork_new->identifier, "private");
2268 artwork_new->identifier = getStringCopy("private");
2270 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2275 setString(&artwork_new->identifier, "classic");
2277 artwork_new->identifier = getStringCopy("classic");
2279 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2282 /* set to new values after changing ".sort_priority" */
2283 artwork_new->color = ARTWORKCOLOR(artwork_new);
2285 setString(&artwork_new->class_desc,
2286 getLevelClassDescription(artwork_new));
2288 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2294 setString(&artwork_new->identifier, artwork_new->subdir);
2296 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2301 setString(&artwork_new->name, artwork_new->identifier);
2302 setString(&artwork_new->name_sorting, artwork_new->name);
2304 artwork_new->name = getStringCopy(artwork_new->identifier);
2305 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2309 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2311 pushTreeInfo(node_first, artwork_new);
2313 freeSetupFileHash(setup_file_hash);
2315 free(directory_path);
2321 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2322 TreeInfo *node_parent,
2323 char *base_directory, int type)
2326 struct dirent *dir_entry;
2327 boolean valid_entry_found = FALSE;
2329 if ((dir = opendir(base_directory)) == NULL)
2331 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2332 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2336 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2338 struct stat file_status;
2339 char *directory_name = dir_entry->d_name;
2340 char *directory_path = getPath2(base_directory, directory_name);
2342 /* skip entries for current and parent directory */
2343 if (strcmp(directory_name, ".") == 0 ||
2344 strcmp(directory_name, "..") == 0)
2346 free(directory_path);
2350 /* find out if directory entry is itself a directory */
2351 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2352 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2354 free(directory_path);
2358 free(directory_path);
2360 /* check if this directory contains artwork with or without config file */
2361 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2363 directory_name, type);
2368 /* check if this directory directly contains artwork itself */
2369 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2370 base_directory, ".",
2372 if (!valid_entry_found)
2373 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2377 static TreeInfo *getDummyArtworkInfo(int type)
2379 /* this is only needed when there is completely no artwork available */
2380 TreeInfo *artwork_new = newTreeInfo();
2382 setTreeInfoToDefaults(artwork_new, type);
2385 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2386 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2387 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2389 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2390 setString(&artwork_new->name, UNDEFINED_FILENAME);
2391 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2393 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2394 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2395 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2397 checked_free(artwork_new->name);
2399 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2400 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2401 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2407 void LoadArtworkInfo()
2409 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2411 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2412 options.graphics_directory,
2413 TREE_TYPE_GRAPHICS_DIR);
2414 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2415 getUserGraphicsDir(),
2416 TREE_TYPE_GRAPHICS_DIR);
2418 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2419 options.sounds_directory,
2420 TREE_TYPE_SOUNDS_DIR);
2421 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2423 TREE_TYPE_SOUNDS_DIR);
2425 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2426 options.music_directory,
2427 TREE_TYPE_MUSIC_DIR);
2428 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2430 TREE_TYPE_MUSIC_DIR);
2432 if (artwork.gfx_first == NULL)
2433 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2434 if (artwork.snd_first == NULL)
2435 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2436 if (artwork.mus_first == NULL)
2437 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2439 /* before sorting, the first entries will be from the user directory */
2440 artwork.gfx_current =
2441 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2442 if (artwork.gfx_current == NULL)
2443 artwork.gfx_current =
2444 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2445 if (artwork.gfx_current == NULL)
2446 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2448 artwork.snd_current =
2449 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2450 if (artwork.snd_current == NULL)
2451 artwork.snd_current =
2452 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2453 if (artwork.snd_current == NULL)
2454 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2456 artwork.mus_current =
2457 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2458 if (artwork.mus_current == NULL)
2459 artwork.mus_current =
2460 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2461 if (artwork.mus_current == NULL)
2462 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2464 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2465 artwork.snd_current_identifier = artwork.snd_current->identifier;
2466 artwork.mus_current_identifier = artwork.mus_current->identifier;
2469 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2470 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2471 printf("music set == %s\n\n", artwork.mus_current_identifier);
2474 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2475 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2476 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2479 dumpTreeInfo(artwork.gfx_first, 0);
2480 dumpTreeInfo(artwork.snd_first, 0);
2481 dumpTreeInfo(artwork.mus_first, 0);
2485 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2486 LevelDirTree *level_node)
2488 /* recursively check all level directories for artwork sub-directories */
2492 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2493 ARTWORK_DIRECTORY((*artwork_node)->type));
2496 if (!level_node->parent_link)
2497 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2498 level_node->subdir, level_node->name);
2501 if (!level_node->parent_link)
2503 TreeInfo *topnode_last = *artwork_node;
2505 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2506 (*artwork_node)->type);
2508 if (topnode_last != *artwork_node)
2510 free((*artwork_node)->identifier);
2511 free((*artwork_node)->name);
2512 free((*artwork_node)->name_sorting);
2514 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2515 (*artwork_node)->name = getStringCopy(level_node->name);
2516 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2518 (*artwork_node)->sort_priority = level_node->sort_priority;
2519 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2525 if (level_node->node_group != NULL)
2526 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2528 level_node = level_node->next;
2532 void LoadLevelArtworkInfo()
2534 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2536 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2537 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2538 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2540 /* needed for reloading level artwork not known at ealier stage */
2542 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2544 artwork.gfx_current =
2545 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2546 if (artwork.gfx_current == NULL)
2547 artwork.gfx_current =
2548 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2549 if (artwork.gfx_current == NULL)
2550 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2553 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2555 artwork.snd_current =
2556 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2557 if (artwork.snd_current == NULL)
2558 artwork.snd_current =
2559 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2560 if (artwork.snd_current == NULL)
2561 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2564 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2566 artwork.mus_current =
2567 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2568 if (artwork.mus_current == NULL)
2569 artwork.mus_current =
2570 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2571 if (artwork.mus_current == NULL)
2572 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2575 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2576 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2577 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2580 dumpTreeInfo(artwork.gfx_first, 0);
2581 dumpTreeInfo(artwork.snd_first, 0);
2582 dumpTreeInfo(artwork.mus_first, 0);
2586 static void SaveUserLevelInfo()
2588 LevelDirTree *level_info;
2593 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2595 if (!(file = fopen(filename, MODE_WRITE)))
2597 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2602 level_info = newTreeInfo();
2604 /* always start with reliable default values */
2605 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2608 setString(&level_info->name, getLoginName());
2609 setString(&level_info->author, getRealName());
2610 level_info->levels = 100;
2611 level_info->first_level = 1;
2612 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2613 level_info->readonly = FALSE;
2614 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2615 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2616 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2618 ldi.name = getStringCopy(getLoginName());
2619 ldi.author = getStringCopy(getRealName());
2621 ldi.first_level = 1;
2622 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2623 ldi.readonly = FALSE;
2624 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2625 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2626 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2629 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2630 getCookie("LEVELINFO")));
2633 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2634 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2635 i != LEVELINFO_TOKEN_NAME_SORTING &&
2636 i != LEVELINFO_TOKEN_IMPORTED_FROM &&
2637 i != LEVELINFO_TOKEN_FILENAME &&
2638 i != LEVELINFO_TOKEN_FILETYPE)
2639 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2643 SetFilePermissions(filename, PERMS_PRIVATE);
2645 freeTreeInfo(level_info);
2649 char *getSetupValue(int type, void *value)
2651 static char value_string[MAX_LINE_LEN];
2659 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2663 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2667 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2671 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2675 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2679 sprintf(value_string, "%d", *(int *)value);
2683 strcpy(value_string, *(char **)value);
2687 value_string[0] = '\0';
2691 return value_string;
2694 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2698 static char token_string[MAX_LINE_LEN];
2699 int token_type = token_info[token_nr].type;
2700 void *setup_value = token_info[token_nr].value;
2701 char *token_text = token_info[token_nr].text;
2702 char *value_string = getSetupValue(token_type, setup_value);
2704 /* build complete token string */
2705 sprintf(token_string, "%s%s", prefix, token_text);
2707 /* build setup entry line */
2708 line = getFormattedSetupEntry(token_string, value_string);
2710 if (token_type == TYPE_KEY_X11)
2712 Key key = *(Key *)setup_value;
2713 char *keyname = getKeyNameFromKey(key);
2715 /* add comment, if useful */
2716 if (strcmp(keyname, "(undefined)") != 0 &&
2717 strcmp(keyname, "(unknown)") != 0)
2719 /* add at least one whitespace */
2721 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2725 strcat(line, keyname);
2732 void LoadLevelSetup_LastSeries()
2734 /* ----------------------------------------------------------------------- */
2735 /* ~/.<program>/levelsetup.conf */
2736 /* ----------------------------------------------------------------------- */
2738 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2739 SetupFileHash *level_setup_hash = NULL;
2741 /* always start with reliable default values */
2742 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2744 if ((level_setup_hash = loadSetupFileHash(filename)))
2746 char *last_level_series =
2747 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2749 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2751 if (leveldir_current == NULL)
2752 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2754 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2756 freeSetupFileHash(level_setup_hash);
2759 Error(ERR_WARN, "using default setup values");
2764 void SaveLevelSetup_LastSeries()
2766 /* ----------------------------------------------------------------------- */
2767 /* ~/.<program>/levelsetup.conf */
2768 /* ----------------------------------------------------------------------- */
2770 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2771 char *level_subdir = leveldir_current->subdir;
2774 InitUserDataDirectory();
2776 if (!(file = fopen(filename, MODE_WRITE)))
2778 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2783 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2784 getCookie("LEVELSETUP")));
2785 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2790 SetFilePermissions(filename, PERMS_PRIVATE);
2795 static void checkSeriesInfo()
2797 static char *level_directory = NULL;
2799 struct dirent *dir_entry;
2801 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2803 level_directory = getPath2((leveldir_current->user_defined ?
2804 getUserLevelDir(NULL) :
2805 options.level_directory),
2806 leveldir_current->fullpath);
2808 if ((dir = opendir(level_directory)) == NULL)
2810 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2814 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2816 if (strlen(dir_entry->d_name) > 4 &&
2817 dir_entry->d_name[3] == '.' &&
2818 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2820 char levelnum_str[4];
2823 strncpy(levelnum_str, dir_entry->d_name, 3);
2824 levelnum_str[3] = '\0';
2826 levelnum_value = atoi(levelnum_str);
2829 if (levelnum_value < leveldir_current->first_level)
2831 Error(ERR_WARN, "additional level %d found", levelnum_value);
2832 leveldir_current->first_level = levelnum_value;
2834 else if (levelnum_value > leveldir_current->last_level)
2836 Error(ERR_WARN, "additional level %d found", levelnum_value);
2837 leveldir_current->last_level = levelnum_value;
2846 void LoadLevelSetup_SeriesInfo()
2849 SetupFileHash *level_setup_hash = NULL;
2850 char *level_subdir = leveldir_current->subdir;
2852 /* always start with reliable default values */
2853 level_nr = leveldir_current->first_level;
2855 checkSeriesInfo(leveldir_current);
2857 /* ----------------------------------------------------------------------- */
2858 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2859 /* ----------------------------------------------------------------------- */
2861 level_subdir = leveldir_current->subdir;
2863 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2865 if ((level_setup_hash = loadSetupFileHash(filename)))
2869 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2873 level_nr = atoi(token_value);
2875 if (level_nr < leveldir_current->first_level)
2876 level_nr = leveldir_current->first_level;
2877 if (level_nr > leveldir_current->last_level)
2878 level_nr = leveldir_current->last_level;
2881 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2885 int level_nr = atoi(token_value);
2887 if (level_nr < leveldir_current->first_level)
2888 level_nr = leveldir_current->first_level;
2889 if (level_nr > leveldir_current->last_level + 1)
2890 level_nr = leveldir_current->last_level;
2892 if (leveldir_current->user_defined || !leveldir_current->handicap)
2893 level_nr = leveldir_current->last_level;
2895 leveldir_current->handicap_level = level_nr;
2898 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2900 freeSetupFileHash(level_setup_hash);
2903 Error(ERR_WARN, "using default setup values");
2908 void SaveLevelSetup_SeriesInfo()
2911 char *level_subdir = leveldir_current->subdir;
2912 char *level_nr_str = int2str(level_nr, 0);
2913 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2916 /* ----------------------------------------------------------------------- */
2917 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2918 /* ----------------------------------------------------------------------- */
2920 InitLevelSetupDirectory(level_subdir);
2922 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2924 if (!(file = fopen(filename, MODE_WRITE)))
2926 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2931 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2932 getCookie("LEVELSETUP")));
2933 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2935 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2936 handicap_level_str));
2940 SetFilePermissions(filename, PERMS_PRIVATE);