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_STRING, &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->graphics_set);
1790 checked_free(ldi->sounds_set);
1791 checked_free(ldi->music_set);
1793 checked_free(ldi->graphics_path);
1794 checked_free(ldi->sounds_path);
1795 checked_free(ldi->music_path);
1799 void setSetupInfo(struct TokenInfo *token_info,
1800 int token_nr, char *token_value)
1802 int token_type = token_info[token_nr].type;
1803 void *setup_value = token_info[token_nr].value;
1805 if (token_value == NULL)
1808 /* set setup field to corresponding token value */
1813 *(boolean *)setup_value = get_boolean_from_string(token_value);
1817 *(Key *)setup_value = getKeyFromKeyName(token_value);
1821 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1825 *(int *)setup_value = get_integer_from_string(token_value);
1829 checked_free(*(char **)setup_value);
1830 *(char **)setup_value = getStringCopy(token_value);
1838 static int compareTreeInfoEntries(const void *object1, const void *object2)
1840 const TreeInfo *entry1 = *((TreeInfo **)object1);
1841 const TreeInfo *entry2 = *((TreeInfo **)object2);
1842 int class_sorting1, class_sorting2;
1845 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1847 class_sorting1 = LEVELSORTING(entry1);
1848 class_sorting2 = LEVELSORTING(entry2);
1852 class_sorting1 = ARTWORKSORTING(entry1);
1853 class_sorting2 = ARTWORKSORTING(entry2);
1856 if (entry1->parent_link || entry2->parent_link)
1857 compare_result = (entry1->parent_link ? -1 : +1);
1858 else if (entry1->sort_priority == entry2->sort_priority)
1860 char *name1 = getStringToLower(entry1->name_sorting);
1861 char *name2 = getStringToLower(entry2->name_sorting);
1863 compare_result = strcmp(name1, name2);
1868 else if (class_sorting1 == class_sorting2)
1869 compare_result = entry1->sort_priority - entry2->sort_priority;
1871 compare_result = class_sorting1 - class_sorting2;
1873 return compare_result;
1876 static void createParentTreeInfoNode(TreeInfo *node_parent)
1880 if (node_parent == NULL)
1883 ti_new = newTreeInfo();
1884 setTreeInfoToDefaults(ti_new, node_parent->type);
1886 ti_new->node_parent = node_parent;
1887 ti_new->parent_link = TRUE;
1890 setString(&ti_new->identifier, node_parent->identifier);
1891 setString(&ti_new->name, ".. (parent directory)");
1892 setString(&ti_new->name_sorting, ti_new->name);
1894 setString(&ti_new->subdir, "..");
1895 setString(&ti_new->fullpath, node_parent->fullpath);
1897 ti_new->sort_priority = node_parent->sort_priority;
1898 ti_new->latest_engine = node_parent->latest_engine;
1900 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1902 ti_new->identifier = getStringCopy(node_parent->identifier);
1903 ti_new->name = ".. (parent directory)";
1904 ti_new->name_sorting = getStringCopy(ti_new->name);
1906 ti_new->subdir = "..";
1907 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1909 ti_new->sort_priority = node_parent->sort_priority;
1910 ti_new->latest_engine = node_parent->latest_engine;
1912 ti_new->class_desc = getLevelClassDescription(ti_new);
1915 pushTreeInfo(&node_parent->node_group, ti_new);
1918 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1919 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1921 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1922 TreeInfo *node_parent,
1923 char *level_directory,
1924 char *directory_name)
1926 char *directory_path = getPath2(level_directory, directory_name);
1927 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1928 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1929 LevelDirTree *leveldir_new = NULL;
1932 if (setup_file_hash == NULL)
1934 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1936 free(directory_path);
1942 leveldir_new = newTreeInfo();
1945 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1947 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1949 leveldir_new->subdir = getStringCopy(directory_name);
1951 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1953 /* set all structure fields according to the token/value pairs */
1954 ldi = *leveldir_new;
1955 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1956 setSetupInfo(levelinfo_tokens, i,
1957 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1958 *leveldir_new = ldi;
1961 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1962 setString(&leveldir_new->name, leveldir_new->subdir);
1964 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1966 free(leveldir_new->name);
1967 leveldir_new->name = getStringCopy(leveldir_new->subdir);
1971 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1973 if (leveldir_new->identifier == NULL)
1974 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
1976 if (leveldir_new->name_sorting == NULL)
1977 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1979 if (node_parent == NULL) /* top level group */
1981 leveldir_new->basepath = getStringCopy(level_directory);
1982 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
1984 else /* sub level group */
1986 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1987 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1990 if (leveldir_new->levels < 1)
1991 leveldir_new->levels = 1;
1993 leveldir_new->last_level =
1994 leveldir_new->first_level + leveldir_new->levels - 1;
1997 leveldir_new->user_defined =
1998 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2000 leveldir_new->user_defined =
2001 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2004 leveldir_new->color = LEVELCOLOR(leveldir_new);
2006 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2008 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2011 leveldir_new->handicap_level = /* set handicap to default value */
2012 (leveldir_new->user_defined || !leveldir_new->handicap ?
2013 leveldir_new->last_level : leveldir_new->first_level);
2015 pushTreeInfo(node_first, leveldir_new);
2017 freeSetupFileHash(setup_file_hash);
2019 if (leveldir_new->level_group)
2021 /* create node to link back to current level directory */
2022 createParentTreeInfoNode(leveldir_new);
2024 /* step into sub-directory and look for more level series */
2025 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2026 leveldir_new, directory_path);
2029 free(directory_path);
2035 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2036 TreeInfo *node_parent,
2037 char *level_directory)
2040 struct dirent *dir_entry;
2041 boolean valid_entry_found = FALSE;
2043 if ((dir = opendir(level_directory)) == NULL)
2045 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2049 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2051 struct stat file_status;
2052 char *directory_name = dir_entry->d_name;
2053 char *directory_path = getPath2(level_directory, directory_name);
2055 /* skip entries for current and parent directory */
2056 if (strcmp(directory_name, ".") == 0 ||
2057 strcmp(directory_name, "..") == 0)
2059 free(directory_path);
2063 /* find out if directory entry is itself a directory */
2064 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2065 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2067 free(directory_path);
2071 free(directory_path);
2073 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2074 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2075 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2078 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2085 if (!valid_entry_found)
2087 /* check if this directory directly contains a file "levelinfo.conf" */
2088 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2089 level_directory, ".");
2092 if (!valid_entry_found)
2093 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2097 void LoadLevelInfo()
2099 InitUserLevelDirectory(getLoginName());
2101 DrawInitText("Loading level series:", 120, FC_GREEN);
2103 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2104 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2106 /* before sorting, the first entries will be from the user directory */
2107 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2109 if (leveldir_first == NULL)
2110 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2112 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2115 dumpTreeInfo(leveldir_first, 0);
2119 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2120 TreeInfo *node_parent,
2121 char *base_directory,
2122 char *directory_name, int type)
2124 char *directory_path = getPath2(base_directory, directory_name);
2125 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2126 SetupFileHash *setup_file_hash = NULL;
2127 TreeInfo *artwork_new = NULL;
2130 if (access(filename, F_OK) == 0) /* file exists */
2131 setup_file_hash = loadSetupFileHash(filename);
2133 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2136 struct dirent *dir_entry;
2137 boolean valid_file_found = FALSE;
2139 if ((dir = opendir(directory_path)) != NULL)
2141 while ((dir_entry = readdir(dir)) != NULL)
2143 char *entry_name = dir_entry->d_name;
2145 if (FileIsArtworkType(entry_name, type))
2147 valid_file_found = TRUE;
2155 if (!valid_file_found)
2157 if (strcmp(directory_name, ".") != 0)
2158 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2160 free(directory_path);
2167 artwork_new = newTreeInfo();
2170 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2172 setTreeInfoToDefaults(artwork_new, type);
2174 artwork_new->subdir = getStringCopy(directory_name);
2176 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2179 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2182 /* set all structure fields according to the token/value pairs */
2184 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2185 setSetupInfo(levelinfo_tokens, i,
2186 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2190 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2191 setString(&artwork_new->name, artwork_new->subdir);
2193 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2195 free(artwork_new->name);
2196 artwork_new->name = getStringCopy(artwork_new->subdir);
2201 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2204 if (artwork_new->identifier == NULL)
2205 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2207 if (artwork_new->name_sorting == NULL)
2208 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2211 if (node_parent == NULL) /* top level group */
2213 artwork_new->basepath = getStringCopy(base_directory);
2214 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2216 else /* sub level group */
2218 artwork_new->basepath = getStringCopy(node_parent->basepath);
2219 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2223 artwork_new->user_defined =
2224 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2226 artwork_new->user_defined =
2227 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2230 /* (may use ".sort_priority" from "setup_file_hash" above) */
2231 artwork_new->color = ARTWORKCOLOR(artwork_new);
2233 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2235 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2238 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2241 if (artwork_new->name != NULL)
2243 free(artwork_new->name);
2244 artwork_new->name = NULL;
2249 if (artwork_new->identifier != NULL)
2251 free(artwork_new->identifier);
2252 artwork_new->identifier = NULL;
2256 if (strcmp(artwork_new->subdir, ".") == 0)
2258 if (artwork_new->user_defined)
2261 setString(&artwork_new->identifier, "private");
2263 artwork_new->identifier = getStringCopy("private");
2265 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2270 setString(&artwork_new->identifier, "classic");
2272 artwork_new->identifier = getStringCopy("classic");
2274 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2277 /* set to new values after changing ".sort_priority" */
2278 artwork_new->color = ARTWORKCOLOR(artwork_new);
2280 setString(&artwork_new->class_desc,
2281 getLevelClassDescription(artwork_new));
2283 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2289 setString(&artwork_new->identifier, artwork_new->subdir);
2291 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2296 setString(&artwork_new->name, artwork_new->identifier);
2297 setString(&artwork_new->name_sorting, artwork_new->name);
2299 artwork_new->name = getStringCopy(artwork_new->identifier);
2300 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2304 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2306 pushTreeInfo(node_first, artwork_new);
2308 freeSetupFileHash(setup_file_hash);
2310 free(directory_path);
2316 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2317 TreeInfo *node_parent,
2318 char *base_directory, int type)
2321 struct dirent *dir_entry;
2322 boolean valid_entry_found = FALSE;
2324 if ((dir = opendir(base_directory)) == NULL)
2326 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2327 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2331 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2333 struct stat file_status;
2334 char *directory_name = dir_entry->d_name;
2335 char *directory_path = getPath2(base_directory, directory_name);
2337 /* skip entries for current and parent directory */
2338 if (strcmp(directory_name, ".") == 0 ||
2339 strcmp(directory_name, "..") == 0)
2341 free(directory_path);
2345 /* find out if directory entry is itself a directory */
2346 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2347 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2349 free(directory_path);
2353 free(directory_path);
2355 /* check if this directory contains artwork with or without config file */
2356 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2358 directory_name, type);
2363 /* check if this directory directly contains artwork itself */
2364 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2365 base_directory, ".",
2367 if (!valid_entry_found)
2368 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2372 static TreeInfo *getDummyArtworkInfo(int type)
2374 /* this is only needed when there is completely no artwork available */
2375 TreeInfo *artwork_new = newTreeInfo();
2377 setTreeInfoToDefaults(artwork_new, type);
2380 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2381 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2382 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2384 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2385 setString(&artwork_new->name, UNDEFINED_FILENAME);
2386 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2388 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2389 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2390 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2392 checked_free(artwork_new->name);
2394 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2395 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2396 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2402 void LoadArtworkInfo()
2404 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2406 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2407 options.graphics_directory,
2408 TREE_TYPE_GRAPHICS_DIR);
2409 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2410 getUserGraphicsDir(),
2411 TREE_TYPE_GRAPHICS_DIR);
2413 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2414 options.sounds_directory,
2415 TREE_TYPE_SOUNDS_DIR);
2416 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2418 TREE_TYPE_SOUNDS_DIR);
2420 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2421 options.music_directory,
2422 TREE_TYPE_MUSIC_DIR);
2423 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2425 TREE_TYPE_MUSIC_DIR);
2427 if (artwork.gfx_first == NULL)
2428 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2429 if (artwork.snd_first == NULL)
2430 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2431 if (artwork.mus_first == NULL)
2432 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2434 /* before sorting, the first entries will be from the user directory */
2435 artwork.gfx_current =
2436 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2437 if (artwork.gfx_current == NULL)
2438 artwork.gfx_current =
2439 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2440 if (artwork.gfx_current == NULL)
2441 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2443 artwork.snd_current =
2444 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2445 if (artwork.snd_current == NULL)
2446 artwork.snd_current =
2447 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2448 if (artwork.snd_current == NULL)
2449 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2451 artwork.mus_current =
2452 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2453 if (artwork.mus_current == NULL)
2454 artwork.mus_current =
2455 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2456 if (artwork.mus_current == NULL)
2457 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2459 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2460 artwork.snd_current_identifier = artwork.snd_current->identifier;
2461 artwork.mus_current_identifier = artwork.mus_current->identifier;
2464 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2465 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2466 printf("music set == %s\n\n", artwork.mus_current_identifier);
2469 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2470 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2471 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2474 dumpTreeInfo(artwork.gfx_first, 0);
2475 dumpTreeInfo(artwork.snd_first, 0);
2476 dumpTreeInfo(artwork.mus_first, 0);
2480 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2481 LevelDirTree *level_node)
2483 /* recursively check all level directories for artwork sub-directories */
2487 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2488 ARTWORK_DIRECTORY((*artwork_node)->type));
2491 if (!level_node->parent_link)
2492 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2493 level_node->subdir, level_node->name);
2496 if (!level_node->parent_link)
2498 TreeInfo *topnode_last = *artwork_node;
2500 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2501 (*artwork_node)->type);
2503 if (topnode_last != *artwork_node)
2505 free((*artwork_node)->identifier);
2506 free((*artwork_node)->name);
2507 free((*artwork_node)->name_sorting);
2509 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2510 (*artwork_node)->name = getStringCopy(level_node->name);
2511 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2513 (*artwork_node)->sort_priority = level_node->sort_priority;
2514 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2520 if (level_node->node_group != NULL)
2521 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2523 level_node = level_node->next;
2527 void LoadLevelArtworkInfo()
2529 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2531 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2532 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2533 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2535 /* needed for reloading level artwork not known at ealier stage */
2537 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2539 artwork.gfx_current =
2540 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2541 if (artwork.gfx_current == NULL)
2542 artwork.gfx_current =
2543 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2544 if (artwork.gfx_current == NULL)
2545 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2548 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2550 artwork.snd_current =
2551 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2552 if (artwork.snd_current == NULL)
2553 artwork.snd_current =
2554 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2555 if (artwork.snd_current == NULL)
2556 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2559 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2561 artwork.mus_current =
2562 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2563 if (artwork.mus_current == NULL)
2564 artwork.mus_current =
2565 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2566 if (artwork.mus_current == NULL)
2567 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2570 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2571 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2572 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2575 dumpTreeInfo(artwork.gfx_first, 0);
2576 dumpTreeInfo(artwork.snd_first, 0);
2577 dumpTreeInfo(artwork.mus_first, 0);
2581 static void SaveUserLevelInfo()
2583 LevelDirTree *level_info;
2588 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2590 if (!(file = fopen(filename, MODE_WRITE)))
2592 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2597 level_info = newTreeInfo();
2599 /* always start with reliable default values */
2600 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2603 setString(&level_info->name, getLoginName());
2604 setString(&level_info->author, getRealName());
2605 level_info->levels = 100;
2606 level_info->first_level = 1;
2607 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2608 level_info->readonly = FALSE;
2609 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2610 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2611 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2613 ldi.name = getStringCopy(getLoginName());
2614 ldi.author = getStringCopy(getRealName());
2616 ldi.first_level = 1;
2617 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2618 ldi.readonly = FALSE;
2619 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2620 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2621 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2624 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2625 getCookie("LEVELINFO")));
2628 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2629 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2630 i != LEVELINFO_TOKEN_NAME_SORTING &&
2631 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2632 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2636 SetFilePermissions(filename, PERMS_PRIVATE);
2638 freeTreeInfo(level_info);
2642 char *getSetupValue(int type, void *value)
2644 static char value_string[MAX_LINE_LEN];
2652 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2656 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2660 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2664 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2668 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2672 sprintf(value_string, "%d", *(int *)value);
2676 strcpy(value_string, *(char **)value);
2680 value_string[0] = '\0';
2684 return value_string;
2687 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2691 static char token_string[MAX_LINE_LEN];
2692 int token_type = token_info[token_nr].type;
2693 void *setup_value = token_info[token_nr].value;
2694 char *token_text = token_info[token_nr].text;
2695 char *value_string = getSetupValue(token_type, setup_value);
2697 /* build complete token string */
2698 sprintf(token_string, "%s%s", prefix, token_text);
2700 /* build setup entry line */
2701 line = getFormattedSetupEntry(token_string, value_string);
2703 if (token_type == TYPE_KEY_X11)
2705 Key key = *(Key *)setup_value;
2706 char *keyname = getKeyNameFromKey(key);
2708 /* add comment, if useful */
2709 if (strcmp(keyname, "(undefined)") != 0 &&
2710 strcmp(keyname, "(unknown)") != 0)
2712 /* add at least one whitespace */
2714 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2718 strcat(line, keyname);
2725 void LoadLevelSetup_LastSeries()
2727 /* ----------------------------------------------------------------------- */
2728 /* ~/.<program>/levelsetup.conf */
2729 /* ----------------------------------------------------------------------- */
2731 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2732 SetupFileHash *level_setup_hash = NULL;
2734 /* always start with reliable default values */
2735 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2737 if ((level_setup_hash = loadSetupFileHash(filename)))
2739 char *last_level_series =
2740 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2742 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2744 if (leveldir_current == NULL)
2745 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2747 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2749 freeSetupFileHash(level_setup_hash);
2752 Error(ERR_WARN, "using default setup values");
2757 void SaveLevelSetup_LastSeries()
2759 /* ----------------------------------------------------------------------- */
2760 /* ~/.<program>/levelsetup.conf */
2761 /* ----------------------------------------------------------------------- */
2763 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2764 char *level_subdir = leveldir_current->subdir;
2767 InitUserDataDirectory();
2769 if (!(file = fopen(filename, MODE_WRITE)))
2771 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2776 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2777 getCookie("LEVELSETUP")));
2778 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2783 SetFilePermissions(filename, PERMS_PRIVATE);
2788 static void checkSeriesInfo()
2790 static char *level_directory = NULL;
2792 struct dirent *dir_entry;
2794 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2796 level_directory = getPath2((leveldir_current->user_defined ?
2797 getUserLevelDir(NULL) :
2798 options.level_directory),
2799 leveldir_current->fullpath);
2801 if ((dir = opendir(level_directory)) == NULL)
2803 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2807 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2809 if (strlen(dir_entry->d_name) > 4 &&
2810 dir_entry->d_name[3] == '.' &&
2811 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2813 char levelnum_str[4];
2816 strncpy(levelnum_str, dir_entry->d_name, 3);
2817 levelnum_str[3] = '\0';
2819 levelnum_value = atoi(levelnum_str);
2822 if (levelnum_value < leveldir_current->first_level)
2824 Error(ERR_WARN, "additional level %d found", levelnum_value);
2825 leveldir_current->first_level = levelnum_value;
2827 else if (levelnum_value > leveldir_current->last_level)
2829 Error(ERR_WARN, "additional level %d found", levelnum_value);
2830 leveldir_current->last_level = levelnum_value;
2839 void LoadLevelSetup_SeriesInfo()
2842 SetupFileHash *level_setup_hash = NULL;
2843 char *level_subdir = leveldir_current->subdir;
2845 /* always start with reliable default values */
2846 level_nr = leveldir_current->first_level;
2848 checkSeriesInfo(leveldir_current);
2850 /* ----------------------------------------------------------------------- */
2851 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2852 /* ----------------------------------------------------------------------- */
2854 level_subdir = leveldir_current->subdir;
2856 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2858 if ((level_setup_hash = loadSetupFileHash(filename)))
2862 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2866 level_nr = atoi(token_value);
2868 if (level_nr < leveldir_current->first_level)
2869 level_nr = leveldir_current->first_level;
2870 if (level_nr > leveldir_current->last_level)
2871 level_nr = leveldir_current->last_level;
2874 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2878 int level_nr = atoi(token_value);
2880 if (level_nr < leveldir_current->first_level)
2881 level_nr = leveldir_current->first_level;
2882 if (level_nr > leveldir_current->last_level + 1)
2883 level_nr = leveldir_current->last_level;
2885 if (leveldir_current->user_defined || !leveldir_current->handicap)
2886 level_nr = leveldir_current->last_level;
2888 leveldir_current->handicap_level = level_nr;
2891 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2893 freeSetupFileHash(level_setup_hash);
2896 Error(ERR_WARN, "using default setup values");
2901 void SaveLevelSetup_SeriesInfo()
2904 char *level_subdir = leveldir_current->subdir;
2905 char *level_nr_str = int2str(level_nr, 0);
2906 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2909 /* ----------------------------------------------------------------------- */
2910 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2911 /* ----------------------------------------------------------------------- */
2913 InitLevelSetupDirectory(level_subdir);
2915 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2917 if (!(file = fopen(filename, MODE_WRITE)))
2919 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2924 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2925 getCookie("LEVELSETUP")));
2926 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2928 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2929 handicap_level_str));
2933 SetFilePermissions(filename, PERMS_PRIVATE);