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;
103 if (level_subdir != NULL)
104 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
106 userlevel_dir = getPath2(data_dir, userlevel_subdir);
108 return userlevel_dir;
111 static char *getScoreDir(char *level_subdir)
113 static char *score_dir = NULL;
114 char *data_dir = getCommonDataDir();
115 char *score_subdir = SCORES_DIRECTORY;
120 if (level_subdir != NULL)
121 score_dir = getPath3(data_dir, score_subdir, level_subdir);
123 score_dir = getPath2(data_dir, score_subdir);
128 static char *getLevelSetupDir(char *level_subdir)
130 static char *levelsetup_dir = NULL;
131 char *data_dir = getUserDataDir();
132 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
135 free(levelsetup_dir);
137 if (level_subdir != NULL)
138 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
140 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
142 return levelsetup_dir;
145 static char *getLevelDirFromTreeInfo(TreeInfo *node)
147 static char *level_dir = NULL;
150 return options.level_directory;
155 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
156 options.level_directory), node->fullpath);
161 static char *getCurrentLevelDir()
163 return getLevelDirFromTreeInfo(leveldir_current);
166 static char *getTapeDir(char *level_subdir)
168 static char *tape_dir = NULL;
169 char *data_dir = getUserDataDir();
170 char *tape_subdir = TAPES_DIRECTORY;
175 if (level_subdir != NULL)
176 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
178 tape_dir = getPath2(data_dir, tape_subdir);
183 static char *getSolutionTapeDir()
185 static char *tape_dir = NULL;
186 char *data_dir = getCurrentLevelDir();
187 char *tape_subdir = TAPES_DIRECTORY;
192 tape_dir = getPath2(data_dir, tape_subdir);
197 static char *getDefaultGraphicsDir(char *graphics_subdir)
199 static char *graphics_dir = NULL;
201 if (graphics_subdir == NULL)
202 return options.graphics_directory;
207 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
212 static char *getDefaultSoundsDir(char *sounds_subdir)
214 static char *sounds_dir = NULL;
216 if (sounds_subdir == NULL)
217 return options.sounds_directory;
222 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
227 static char *getDefaultMusicDir(char *music_subdir)
229 static char *music_dir = NULL;
231 if (music_subdir == NULL)
232 return options.music_directory;
237 music_dir = getPath2(options.music_directory, music_subdir);
242 static char *getDefaultArtworkSet(int type)
244 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
245 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
246 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
249 static char *getDefaultArtworkDir(int type)
251 return (type == TREE_TYPE_GRAPHICS_DIR ?
252 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
253 type == TREE_TYPE_SOUNDS_DIR ?
254 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
255 type == TREE_TYPE_MUSIC_DIR ?
256 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
259 static char *getUserGraphicsDir()
261 static char *usergraphics_dir = NULL;
263 if (usergraphics_dir == NULL)
264 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
266 return usergraphics_dir;
269 static char *getUserSoundsDir()
271 static char *usersounds_dir = NULL;
273 if (usersounds_dir == NULL)
274 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
276 return usersounds_dir;
279 static char *getUserMusicDir()
281 static char *usermusic_dir = NULL;
283 if (usermusic_dir == NULL)
284 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
286 return usermusic_dir;
289 static char *getSetupArtworkDir(TreeInfo *ti)
291 static char *artwork_dir = NULL;
293 if (artwork_dir != NULL)
296 artwork_dir = getPath2(ti->basepath, ti->fullpath);
301 char *setLevelArtworkDir(TreeInfo *ti)
303 char **artwork_path_ptr, **artwork_set_ptr;
304 TreeInfo *level_artwork;
306 if (ti == NULL || leveldir_current == NULL)
309 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
310 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
312 if (*artwork_path_ptr != NULL)
313 free(*artwork_path_ptr);
315 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
316 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
319 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
320 normally result in using the artwork configured in the setup menu. But
321 if an artwork subdirectory exists (which might contain custom artwork
322 or an artwork configuration file), this level artwork must be treated
323 as relative to the default "classic" artwork, not to the artwork that
324 is currently configured in the setup menu. */
326 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
328 if (*artwork_set_ptr != NULL)
329 free(*artwork_set_ptr);
333 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
334 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
338 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
339 *artwork_set_ptr = NULL;
345 return *artwork_set_ptr;
348 inline static char *getLevelArtworkSet(int type)
350 if (leveldir_current == NULL)
353 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
356 inline static char *getLevelArtworkDir(int type)
358 if (leveldir_current == NULL)
359 return UNDEFINED_FILENAME;
361 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
364 char *getLevelFilename(int nr)
366 static char *filename = NULL;
367 char basename[MAX_FILENAME_LEN];
369 if (filename != NULL)
373 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
375 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
377 filename = getPath2(getCurrentLevelDir(), basename);
382 char *getTapeFilename(int nr)
384 static char *filename = NULL;
385 char basename[MAX_FILENAME_LEN];
387 if (filename != NULL)
390 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
391 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
396 char *getSolutionTapeFilename(int nr)
398 static char *filename = NULL;
399 char basename[MAX_FILENAME_LEN];
401 if (filename != NULL)
404 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
405 filename = getPath2(getSolutionTapeDir(), basename);
410 char *getScoreFilename(int nr)
412 static char *filename = NULL;
413 char basename[MAX_FILENAME_LEN];
415 if (filename != NULL)
418 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
419 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
424 char *getSetupFilename()
426 static char *filename = NULL;
428 if (filename != NULL)
431 filename = getPath2(getSetupDir(), SETUP_FILENAME);
436 char *getEditorSetupFilename()
438 static char *filename = NULL;
440 if (filename != NULL)
443 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
448 char *getHelpAnimFilename()
450 static char *filename = NULL;
452 if (filename != NULL)
455 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
460 char *getHelpTextFilename()
462 static char *filename = NULL;
464 if (filename != NULL)
467 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
472 char *getLevelSetInfoFilename()
474 static char *filename = NULL;
489 for (i = 0; basenames[i] != NULL; i++)
491 if (filename != NULL)
494 filename = getPath2(getCurrentLevelDir(), basenames[i]);
495 if (fileExists(filename))
502 static char *getCorrectedArtworkBasename(char *basename)
504 char *basename_corrected = basename;
506 #if defined(PLATFORM_MSDOS)
507 if (program.filename_prefix != NULL)
509 int prefix_len = strlen(program.filename_prefix);
511 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
512 basename_corrected = &basename[prefix_len];
514 /* if corrected filename is still longer than standard MS-DOS filename
515 size (8 characters + 1 dot + 3 characters file extension), shorten
516 filename by writing file extension after 8th basename character */
517 if (strlen(basename_corrected) > 8 + 1 + 3)
519 static char *msdos_filename = NULL;
521 if (msdos_filename != NULL)
522 free(msdos_filename);
524 msdos_filename = getStringCopy(basename_corrected);
525 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
527 basename_corrected = msdos_filename;
532 return basename_corrected;
535 char *getCustomImageFilename(char *basename)
537 static char *filename = NULL;
538 boolean skip_setup_artwork = FALSE;
540 if (filename != NULL)
543 basename = getCorrectedArtworkBasename(basename);
545 if (!setup.override_level_graphics)
547 /* 1st try: look for special artwork in current level series directory */
548 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
549 if (fileExists(filename))
554 /* check if there is special artwork configured in level series config */
555 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
557 /* 2nd try: look for special artwork configured in level series config */
558 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
559 if (fileExists(filename))
564 /* take missing artwork configured in level set config from default */
565 skip_setup_artwork = TRUE;
569 if (!skip_setup_artwork)
571 /* 3rd try: look for special artwork in configured artwork directory */
572 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
573 if (fileExists(filename))
579 /* 4th try: look for default artwork in new default artwork directory */
580 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
581 if (fileExists(filename))
586 /* 5th try: look for default artwork in old default artwork directory */
587 filename = getPath2(options.graphics_directory, basename);
588 if (fileExists(filename))
591 return NULL; /* cannot find specified artwork file anywhere */
594 char *getCustomSoundFilename(char *basename)
596 static char *filename = NULL;
597 boolean skip_setup_artwork = FALSE;
599 if (filename != NULL)
602 basename = getCorrectedArtworkBasename(basename);
604 if (!setup.override_level_sounds)
606 /* 1st try: look for special artwork in current level series directory */
607 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
608 if (fileExists(filename))
613 /* check if there is special artwork configured in level series config */
614 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
616 /* 2nd try: look for special artwork configured in level series config */
617 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
618 if (fileExists(filename))
623 /* take missing artwork configured in level set config from default */
624 skip_setup_artwork = TRUE;
628 if (!skip_setup_artwork)
630 /* 3rd try: look for special artwork in configured artwork directory */
631 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
632 if (fileExists(filename))
638 /* 4th try: look for default artwork in new default artwork directory */
639 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
640 if (fileExists(filename))
645 /* 5th try: look for default artwork in old default artwork directory */
646 filename = getPath2(options.sounds_directory, basename);
647 if (fileExists(filename))
650 return NULL; /* cannot find specified artwork file anywhere */
653 char *getCustomMusicFilename(char *basename)
655 static char *filename = NULL;
656 boolean skip_setup_artwork = FALSE;
658 if (filename != NULL)
661 basename = getCorrectedArtworkBasename(basename);
663 if (!setup.override_level_music)
665 /* 1st try: look for special artwork in current level series directory */
666 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
667 if (fileExists(filename))
672 /* check if there is special artwork configured in level series config */
673 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
675 /* 2nd try: look for special artwork configured in level series config */
676 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
677 if (fileExists(filename))
682 /* take missing artwork configured in level set config from default */
683 skip_setup_artwork = TRUE;
687 if (!skip_setup_artwork)
689 /* 3rd try: look for special artwork in configured artwork directory */
690 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
691 if (fileExists(filename))
697 /* 4th try: look for default artwork in new default artwork directory */
698 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
699 if (fileExists(filename))
704 /* 5th try: look for default artwork in old default artwork directory */
705 filename = getPath2(options.music_directory, basename);
706 if (fileExists(filename))
709 return NULL; /* cannot find specified artwork file anywhere */
712 char *getCustomArtworkFilename(char *basename, int type)
714 if (type == ARTWORK_TYPE_GRAPHICS)
715 return getCustomImageFilename(basename);
716 else if (type == ARTWORK_TYPE_SOUNDS)
717 return getCustomSoundFilename(basename);
718 else if (type == ARTWORK_TYPE_MUSIC)
719 return getCustomMusicFilename(basename);
721 return UNDEFINED_FILENAME;
724 char *getCustomArtworkConfigFilename(int type)
726 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
729 char *getCustomArtworkLevelConfigFilename(int type)
731 static char *filename = NULL;
733 if (filename != NULL)
736 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
741 char *getCustomMusicDirectory(void)
743 static char *directory = NULL;
744 boolean skip_setup_artwork = FALSE;
746 if (directory != NULL)
749 if (!setup.override_level_music)
751 /* 1st try: look for special artwork in current level series directory */
752 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
753 if (fileExists(directory))
758 /* check if there is special artwork configured in level series config */
759 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
761 /* 2nd try: look for special artwork configured in level series config */
762 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
763 if (fileExists(directory))
768 /* take missing artwork configured in level set config from default */
769 skip_setup_artwork = TRUE;
773 if (!skip_setup_artwork)
775 /* 3rd try: look for special artwork in configured artwork directory */
776 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
777 if (fileExists(directory))
783 /* 4th try: look for default artwork in new default artwork directory */
784 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
785 if (fileExists(directory))
790 /* 5th try: look for default artwork in old default artwork directory */
791 directory = getStringCopy(options.music_directory);
792 if (fileExists(directory))
795 return NULL; /* cannot find specified artwork file anywhere */
798 void InitTapeDirectory(char *level_subdir)
800 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
801 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
802 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
805 void InitScoreDirectory(char *level_subdir)
807 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
808 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
809 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
812 static void SaveUserLevelInfo();
814 void InitUserLevelDirectory(char *level_subdir)
816 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
818 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
819 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
820 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
826 void InitLevelSetupDirectory(char *level_subdir)
828 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
829 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
830 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
834 /* ------------------------------------------------------------------------- */
835 /* some functions to handle lists of level directories */
836 /* ------------------------------------------------------------------------- */
838 TreeInfo *newTreeInfo()
840 return checked_calloc(sizeof(TreeInfo));
843 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
845 node_new->next = *node_first;
846 *node_first = node_new;
849 int numTreeInfo(TreeInfo *node)
862 boolean validLevelSeries(TreeInfo *node)
864 return (node != NULL && !node->node_group && !node->parent_link);
867 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
872 if (node->node_group) /* enter level group (step down into tree) */
873 return getFirstValidTreeInfoEntry(node->node_group);
874 else if (node->parent_link) /* skip start entry of level group */
876 if (node->next) /* get first real level series entry */
877 return getFirstValidTreeInfoEntry(node->next);
878 else /* leave empty level group and go on */
879 return getFirstValidTreeInfoEntry(node->node_parent->next);
881 else /* this seems to be a regular level series */
885 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
890 if (node->node_parent == NULL) /* top level group */
891 return *node->node_top;
892 else /* sub level group */
893 return node->node_parent->node_group;
896 int numTreeInfoInGroup(TreeInfo *node)
898 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
901 int posTreeInfo(TreeInfo *node)
903 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
908 if (node_cmp == node)
912 node_cmp = node_cmp->next;
918 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
920 TreeInfo *node_default = node;
935 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
937 if (identifier == NULL)
942 if (node->node_group)
944 TreeInfo *node_group;
946 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
951 else if (!node->parent_link)
953 if (strcmp(identifier, node->identifier) == 0)
963 void dumpTreeInfo(TreeInfo *node, int depth)
967 printf("Dumping TreeInfo:\n");
971 for (i = 0; i < (depth + 1) * 3; i++)
975 printf("filename == '%s' ['%s', '%s'] [%d])\n",
976 node->filename, node->fullpath, node->basepath, node->user_defined);
978 printf("filename == '%s' (%s) [%s] (%d)\n",
979 node->filename, node->name, node->identifier, node->sort_priority);
982 if (node->node_group != NULL)
983 dumpTreeInfo(node->node_group, depth + 1);
989 void sortTreeInfo(TreeInfo **node_first,
990 int (*compare_function)(const void *, const void *))
992 int num_nodes = numTreeInfo(*node_first);
993 TreeInfo **sort_array;
994 TreeInfo *node = *node_first;
1000 /* allocate array for sorting structure pointers */
1001 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
1003 /* writing structure pointers to sorting array */
1004 while (i < num_nodes && node) /* double boundary check... */
1006 sort_array[i] = node;
1012 /* sorting the structure pointers in the sorting array */
1013 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
1016 /* update the linkage of list elements with the sorted node array */
1017 for (i = 0; i < num_nodes - 1; i++)
1018 sort_array[i]->next = sort_array[i + 1];
1019 sort_array[num_nodes - 1]->next = NULL;
1021 /* update the linkage of the main list anchor pointer */
1022 *node_first = sort_array[0];
1026 /* now recursively sort the level group structures */
1030 if (node->node_group != NULL)
1031 sortTreeInfo(&node->node_group, compare_function);
1038 /* ========================================================================= */
1039 /* some stuff from "files.c" */
1040 /* ========================================================================= */
1042 #if defined(PLATFORM_WIN32)
1044 #define S_IRGRP S_IRUSR
1047 #define S_IROTH S_IRUSR
1050 #define S_IWGRP S_IWUSR
1053 #define S_IWOTH S_IWUSR
1056 #define S_IXGRP S_IXUSR
1059 #define S_IXOTH S_IXUSR
1062 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1067 #endif /* PLATFORM_WIN32 */
1069 /* file permissions for newly written files */
1070 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1071 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1072 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1074 #define MODE_W_PRIVATE (S_IWUSR)
1075 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1076 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1078 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1079 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1081 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1082 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1084 char *getUserDataDir(void)
1086 static char *userdata_dir = NULL;
1088 if (userdata_dir == NULL)
1089 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1091 return userdata_dir;
1094 char *getCommonDataDir(void)
1096 static char *common_data_dir = NULL;
1098 #if defined(PLATFORM_WIN32)
1099 if (common_data_dir == NULL)
1101 char *dir = checked_malloc(MAX_PATH + 1);
1103 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1104 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1105 common_data_dir = getPath2(dir, program.userdata_directory);
1107 common_data_dir = options.rw_base_directory;
1110 if (common_data_dir == NULL)
1111 common_data_dir = options.rw_base_directory;
1114 return common_data_dir;
1119 return getUserDataDir();
1122 static mode_t posix_umask(mode_t mask)
1124 #if defined(PLATFORM_UNIX)
1131 static int posix_mkdir(const char *pathname, mode_t mode)
1133 #if defined(PLATFORM_WIN32)
1134 return mkdir(pathname);
1136 return mkdir(pathname, mode);
1140 void createDirectory(char *dir, char *text, int permission_class)
1142 /* leave "other" permissions in umask untouched, but ensure group parts
1143 of USERDATA_DIR_MODE are not masked */
1144 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1145 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1146 mode_t normal_umask = posix_umask(0);
1147 mode_t group_umask = ~(dir_mode & S_IRWXG);
1148 posix_umask(normal_umask & group_umask);
1150 if (access(dir, F_OK) != 0)
1151 if (posix_mkdir(dir, dir_mode) != 0)
1152 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1154 posix_umask(normal_umask); /* reset normal umask */
1157 void InitUserDataDirectory()
1159 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1162 void SetFilePermissions(char *filename, int permission_class)
1164 chmod(filename, (permission_class == PERMS_PRIVATE ?
1165 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1168 char *getCookie(char *file_type)
1170 static char cookie[MAX_COOKIE_LEN + 1];
1172 if (strlen(program.cookie_prefix) + 1 +
1173 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1174 return "[COOKIE ERROR]"; /* should never happen */
1176 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1177 program.cookie_prefix, file_type,
1178 program.version_major, program.version_minor);
1183 int getFileVersionFromCookieString(const char *cookie)
1185 const char *ptr_cookie1, *ptr_cookie2;
1186 const char *pattern1 = "_FILE_VERSION_";
1187 const char *pattern2 = "?.?";
1188 const int len_cookie = strlen(cookie);
1189 const int len_pattern1 = strlen(pattern1);
1190 const int len_pattern2 = strlen(pattern2);
1191 const int len_pattern = len_pattern1 + len_pattern2;
1192 int version_major, version_minor;
1194 if (len_cookie <= len_pattern)
1197 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1198 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1200 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1203 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1204 ptr_cookie2[1] != '.' ||
1205 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1208 version_major = ptr_cookie2[0] - '0';
1209 version_minor = ptr_cookie2[2] - '0';
1211 return VERSION_IDENT(version_major, version_minor, 0, 0);
1214 boolean checkCookieString(const char *cookie, const char *template)
1216 const char *pattern = "_FILE_VERSION_?.?";
1217 const int len_cookie = strlen(cookie);
1218 const int len_template = strlen(template);
1219 const int len_pattern = strlen(pattern);
1221 if (len_cookie != len_template)
1224 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1230 /* ------------------------------------------------------------------------- */
1231 /* setup file list and hash handling functions */
1232 /* ------------------------------------------------------------------------- */
1234 char *getFormattedSetupEntry(char *token, char *value)
1237 static char entry[MAX_LINE_LEN];
1239 /* if value is an empty string, just return token without value */
1243 /* start with the token and some spaces to format output line */
1244 sprintf(entry, "%s:", token);
1245 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1248 /* continue with the token's value */
1249 strcat(entry, value);
1254 SetupFileList *newSetupFileList(char *token, char *value)
1256 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1258 new->token = getStringCopy(token);
1259 new->value = getStringCopy(value);
1266 void freeSetupFileList(SetupFileList *list)
1276 freeSetupFileList(list->next);
1280 char *getListEntry(SetupFileList *list, char *token)
1285 if (strcmp(list->token, token) == 0)
1288 return getListEntry(list->next, token);
1291 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1296 if (strcmp(list->token, token) == 0)
1301 list->value = getStringCopy(value);
1305 else if (list->next == NULL)
1306 return (list->next = newSetupFileList(token, value));
1308 return setListEntry(list->next, token, value);
1311 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1316 if (list->next == NULL)
1317 return (list->next = newSetupFileList(token, value));
1319 return addListEntry(list->next, token, value);
1323 static void printSetupFileList(SetupFileList *list)
1328 printf("token: '%s'\n", list->token);
1329 printf("value: '%s'\n", list->value);
1331 printSetupFileList(list->next);
1336 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1337 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1338 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1339 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1341 #define insert_hash_entry hashtable_insert
1342 #define search_hash_entry hashtable_search
1343 #define change_hash_entry hashtable_change
1344 #define remove_hash_entry hashtable_remove
1347 static unsigned int get_hash_from_key(void *key)
1352 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1353 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1354 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1355 it works better than many other constants, prime or not) has never been
1356 adequately explained.
1358 If you just want to have a good hash function, and cannot wait, djb2
1359 is one of the best string hash functions i know. It has excellent
1360 distribution and speed on many different sets of keys and table sizes.
1361 You are not likely to do better with one of the "well known" functions
1362 such as PJW, K&R, etc.
1364 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1367 char *str = (char *)key;
1368 unsigned int hash = 5381;
1371 while ((c = *str++))
1372 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1377 static int keys_are_equal(void *key1, void *key2)
1379 return (strcmp((char *)key1, (char *)key2) == 0);
1382 SetupFileHash *newSetupFileHash()
1384 SetupFileHash *new_hash =
1385 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1387 if (new_hash == NULL)
1388 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1393 void freeSetupFileHash(SetupFileHash *hash)
1398 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1401 char *getHashEntry(SetupFileHash *hash, char *token)
1406 return search_hash_entry(hash, token);
1409 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1416 value_copy = getStringCopy(value);
1418 /* change value; if it does not exist, insert it as new */
1419 if (!change_hash_entry(hash, token, value_copy))
1420 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1421 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1424 char *removeHashEntry(SetupFileHash *hash, char *token)
1429 return remove_hash_entry(hash, token);
1434 static void printSetupFileHash(SetupFileHash *hash)
1436 BEGIN_HASH_ITERATION(hash, itr)
1438 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1439 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1441 END_HASH_ITERATION(hash, itr)
1446 static void *loadSetupFileData(char *filename, boolean use_hash)
1448 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1449 char *token, *value, *line_ptr;
1450 void *setup_file_data, *insert_ptr = NULL;
1451 boolean read_continued_line = FALSE;
1455 setup_file_data = newSetupFileHash();
1457 insert_ptr = setup_file_data = newSetupFileList("", "");
1459 if (!(file = fopen(filename, MODE_READ)))
1461 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1467 /* read next line of input file */
1468 if (!fgets(line, MAX_LINE_LEN, file))
1471 /* cut trailing newline or carriage return */
1472 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1473 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1476 if (read_continued_line)
1478 /* cut leading whitespaces from input line */
1479 for (line_ptr = line; *line_ptr; line_ptr++)
1480 if (*line_ptr != ' ' && *line_ptr != '\t')
1483 /* append new line to existing line, if there is enough space */
1484 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1485 strcat(previous_line, line_ptr);
1487 strcpy(line, previous_line); /* copy storage buffer to line */
1489 read_continued_line = FALSE;
1492 /* if the last character is '\', continue at next line */
1493 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1495 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1496 strcpy(previous_line, line); /* copy line to storage buffer */
1498 read_continued_line = TRUE;
1503 /* cut trailing comment from input line */
1504 for (line_ptr = line; *line_ptr; line_ptr++)
1506 if (*line_ptr == '#')
1513 /* cut trailing whitespaces from input line */
1514 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1515 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1518 /* ignore empty lines */
1522 /* cut leading whitespaces from token */
1523 for (token = line; *token; token++)
1524 if (*token != ' ' && *token != '\t')
1527 /* start with empty value as reliable default */
1530 /* find end of token to determine start of value */
1531 for (line_ptr = token; *line_ptr; line_ptr++)
1533 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1535 *line_ptr = '\0'; /* terminate token string */
1536 value = line_ptr + 1; /* set beginning of value */
1542 /* cut leading whitespaces from value */
1543 for (; *value; value++)
1544 if (*value != ' ' && *value != '\t')
1549 value = "true"; /* treat tokens without value as "true" */
1555 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1557 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1565 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1566 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1570 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1571 SetupFileList *first_valid_list_entry = setup_file_list->next;
1573 /* free empty list header */
1574 setup_file_list->next = NULL;
1575 freeSetupFileList(setup_file_list);
1576 setup_file_data = first_valid_list_entry;
1578 if (first_valid_list_entry == NULL)
1579 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1582 return setup_file_data;
1585 SetupFileList *loadSetupFileList(char *filename)
1587 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1590 SetupFileHash *loadSetupFileHash(char *filename)
1592 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1595 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1598 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1601 Error(ERR_WARN, "configuration file has no file identifier");
1602 else if (!checkCookieString(value, identifier))
1603 Error(ERR_WARN, "configuration file has wrong file identifier");
1607 /* ========================================================================= */
1608 /* setup file stuff */
1609 /* ========================================================================= */
1611 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1612 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1613 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1615 /* level directory info */
1616 #define LEVELINFO_TOKEN_IDENTIFIER 0
1617 #define LEVELINFO_TOKEN_NAME 1
1618 #define LEVELINFO_TOKEN_NAME_SORTING 2
1619 #define LEVELINFO_TOKEN_AUTHOR 3
1620 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1621 #define LEVELINFO_TOKEN_LEVELS 5
1622 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1623 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1624 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1625 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1626 #define LEVELINFO_TOKEN_READONLY 10
1627 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1628 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1629 #define LEVELINFO_TOKEN_MUSIC_SET 13
1631 #define NUM_LEVELINFO_TOKENS 14
1633 static LevelDirTree ldi;
1635 static struct TokenInfo levelinfo_tokens[] =
1637 /* level directory info */
1638 { TYPE_STRING, &ldi.identifier, "identifier" },
1639 { TYPE_STRING, &ldi.name, "name" },
1640 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1641 { TYPE_STRING, &ldi.author, "author" },
1642 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1643 { TYPE_INTEGER, &ldi.levels, "levels" },
1644 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1645 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1646 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1647 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1648 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1649 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1650 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1651 { TYPE_STRING, &ldi.music_set, "music_set" }
1654 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1658 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1659 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1660 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1661 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1664 ldi->node_parent = NULL;
1665 ldi->node_group = NULL;
1669 ldi->cl_cursor = -1;
1671 ldi->filename = NULL;
1672 ldi->fullpath = NULL;
1673 ldi->basepath = NULL;
1674 ldi->identifier = NULL;
1675 ldi->name = getStringCopy(ANONYMOUS_NAME);
1676 ldi->name_sorting = NULL;
1677 ldi->author = getStringCopy(ANONYMOUS_NAME);
1679 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1680 ldi->latest_engine = FALSE; /* default: get from level */
1681 ldi->parent_link = FALSE;
1682 ldi->user_defined = FALSE;
1684 ldi->class_desc = NULL;
1686 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1688 ldi->imported_from = NULL;
1690 ldi->graphics_set = NULL;
1691 ldi->sounds_set = NULL;
1692 ldi->music_set = NULL;
1693 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1694 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1695 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1698 ldi->first_level = 0;
1699 ldi->last_level = 0;
1700 ldi->level_group = FALSE;
1701 ldi->handicap_level = 0;
1702 ldi->readonly = TRUE;
1706 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1710 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1712 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1718 /* copy all values from the parent structure */
1720 ldi->type = parent->type;
1722 ldi->node_top = parent->node_top;
1723 ldi->node_parent = parent;
1724 ldi->node_group = NULL;
1728 ldi->cl_cursor = -1;
1730 ldi->filename = NULL;
1731 ldi->fullpath = NULL;
1732 ldi->basepath = NULL;
1733 ldi->identifier = NULL;
1734 ldi->name = getStringCopy(ANONYMOUS_NAME);
1735 ldi->name_sorting = NULL;
1736 ldi->author = getStringCopy(parent->author);
1738 ldi->sort_priority = parent->sort_priority;
1739 ldi->latest_engine = parent->latest_engine;
1740 ldi->parent_link = FALSE;
1741 ldi->user_defined = parent->user_defined;
1742 ldi->color = parent->color;
1743 ldi->class_desc = getStringCopy(parent->class_desc);
1745 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1747 ldi->imported_from = getStringCopy(parent->imported_from);
1749 ldi->graphics_set = NULL;
1750 ldi->sounds_set = NULL;
1751 ldi->music_set = NULL;
1752 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1753 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1754 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1757 ldi->first_level = 0;
1758 ldi->last_level = 0;
1759 ldi->level_group = FALSE;
1760 ldi->handicap_level = 0;
1761 ldi->readonly = TRUE;
1767 /* first copy all values from the parent structure ... */
1770 /* ... then set all fields to default that cannot be inherited from parent.
1771 This is especially important for all those fields that can be set from
1772 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1773 calls 'free()' for all already set token values which requires that no
1774 other structure's pointer may point to them!
1777 ldi->filename = NULL;
1778 ldi->fullpath = NULL;
1779 ldi->basepath = NULL;
1780 ldi->identifier = NULL;
1781 ldi->name = getStringCopy(ANONYMOUS_NAME);
1782 ldi->name_sorting = NULL;
1783 ldi->author = getStringCopy(parent->author);
1785 ldi->imported_from = getStringCopy(parent->imported_from);
1786 ldi->class_desc = getStringCopy(parent->class_desc);
1788 ldi->graphics_set = NULL;
1789 ldi->sounds_set = NULL;
1790 ldi->music_set = NULL;
1791 ldi->graphics_path = NULL;
1792 ldi->sounds_path = NULL;
1793 ldi->music_path = NULL;
1795 ldi->level_group = FALSE;
1796 ldi->parent_link = FALSE;
1798 ldi->node_top = parent->node_top;
1799 ldi->node_parent = parent;
1800 ldi->node_group = NULL;
1806 static void freeTreeInfo(TreeInfo *ldi)
1809 free(ldi->filename);
1811 free(ldi->fullpath);
1813 free(ldi->basepath);
1814 if (ldi->identifier)
1815 free(ldi->identifier);
1819 if (ldi->name_sorting)
1820 free(ldi->name_sorting);
1824 if (ldi->class_desc)
1825 free(ldi->class_desc);
1827 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1829 if (ldi->graphics_set)
1830 free(ldi->graphics_set);
1831 if (ldi->sounds_set)
1832 free(ldi->sounds_set);
1834 free(ldi->music_set);
1836 if (ldi->graphics_path)
1837 free(ldi->graphics_path);
1838 if (ldi->sounds_path)
1839 free(ldi->sounds_path);
1840 if (ldi->music_path)
1841 free(ldi->music_path);
1845 void setSetupInfo(struct TokenInfo *token_info,
1846 int token_nr, char *token_value)
1848 int token_type = token_info[token_nr].type;
1849 void *setup_value = token_info[token_nr].value;
1851 if (token_value == NULL)
1854 /* set setup field to corresponding token value */
1859 *(boolean *)setup_value = get_boolean_from_string(token_value);
1863 *(Key *)setup_value = getKeyFromKeyName(token_value);
1867 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1871 *(int *)setup_value = get_integer_from_string(token_value);
1875 if (*(char **)setup_value != NULL)
1876 free(*(char **)setup_value);
1877 *(char **)setup_value = getStringCopy(token_value);
1885 static int compareTreeInfoEntries(const void *object1, const void *object2)
1887 const TreeInfo *entry1 = *((TreeInfo **)object1);
1888 const TreeInfo *entry2 = *((TreeInfo **)object2);
1889 int class_sorting1, class_sorting2;
1892 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1894 class_sorting1 = LEVELSORTING(entry1);
1895 class_sorting2 = LEVELSORTING(entry2);
1899 class_sorting1 = ARTWORKSORTING(entry1);
1900 class_sorting2 = ARTWORKSORTING(entry2);
1903 if (entry1->parent_link || entry2->parent_link)
1904 compare_result = (entry1->parent_link ? -1 : +1);
1905 else if (entry1->sort_priority == entry2->sort_priority)
1907 char *name1 = getStringToLower(entry1->name_sorting);
1908 char *name2 = getStringToLower(entry2->name_sorting);
1910 compare_result = strcmp(name1, name2);
1915 else if (class_sorting1 == class_sorting2)
1916 compare_result = entry1->sort_priority - entry2->sort_priority;
1918 compare_result = class_sorting1 - class_sorting2;
1920 return compare_result;
1923 static void createParentTreeInfoNode(TreeInfo *node_parent)
1927 if (node_parent == NULL)
1930 ti_new = newTreeInfo();
1931 setTreeInfoToDefaults(ti_new, node_parent->type);
1933 ti_new->node_parent = node_parent;
1934 ti_new->parent_link = TRUE;
1937 setString(&ti_new->identifier, node_parent->identifier);
1938 setString(&ti_new->name, ".. (parent directory)");
1939 setString(&ti_new->name_sorting, ti_new->name);
1941 setString(&ti_new->filename, "..");
1942 setString(&ti_new->fullpath, node_parent->fullpath);
1944 ti_new->sort_priority = node_parent->sort_priority;
1945 ti_new->latest_engine = node_parent->latest_engine;
1947 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1949 ti_new->identifier = getStringCopy(node_parent->identifier);
1950 ti_new->name = ".. (parent directory)";
1951 ti_new->name_sorting = getStringCopy(ti_new->name);
1953 ti_new->filename = "..";
1954 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1956 ti_new->sort_priority = node_parent->sort_priority;
1957 ti_new->latest_engine = node_parent->latest_engine;
1959 ti_new->class_desc = getLevelClassDescription(ti_new);
1962 pushTreeInfo(&node_parent->node_group, ti_new);
1965 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1966 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1968 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1969 TreeInfo *node_parent,
1970 char *level_directory,
1971 char *directory_name)
1973 char *directory_path = getPath2(level_directory, directory_name);
1974 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1975 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1976 LevelDirTree *leveldir_new = NULL;
1979 if (setup_file_hash == NULL)
1981 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1983 free(directory_path);
1989 leveldir_new = newTreeInfo();
1992 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1994 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1996 leveldir_new->filename = getStringCopy(directory_name);
1998 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
2000 /* set all structure fields according to the token/value pairs */
2001 ldi = *leveldir_new;
2002 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2003 setSetupInfo(levelinfo_tokens, i,
2004 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2005 *leveldir_new = ldi;
2008 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2009 setString(&leveldir_new->name, leveldir_new->filename);
2011 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2013 free(leveldir_new->name);
2014 leveldir_new->name = getStringCopy(leveldir_new->filename);
2018 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2020 if (leveldir_new->identifier == NULL)
2021 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
2023 if (leveldir_new->name_sorting == NULL)
2024 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2026 if (node_parent == NULL) /* top level group */
2028 leveldir_new->basepath = getStringCopy(level_directory);
2029 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
2031 else /* sub level group */
2033 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2034 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2037 if (leveldir_new->levels < 1)
2038 leveldir_new->levels = 1;
2040 leveldir_new->last_level =
2041 leveldir_new->first_level + leveldir_new->levels - 1;
2044 leveldir_new->user_defined =
2045 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2047 leveldir_new->user_defined =
2048 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2051 leveldir_new->color = LEVELCOLOR(leveldir_new);
2053 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2055 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2058 leveldir_new->handicap_level = /* set handicap to default value */
2059 (leveldir_new->user_defined ?
2060 leveldir_new->last_level :
2061 leveldir_new->first_level);
2063 pushTreeInfo(node_first, leveldir_new);
2065 freeSetupFileHash(setup_file_hash);
2067 if (leveldir_new->level_group)
2069 /* create node to link back to current level directory */
2070 createParentTreeInfoNode(leveldir_new);
2072 /* step into sub-directory and look for more level series */
2073 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2074 leveldir_new, directory_path);
2077 free(directory_path);
2083 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2084 TreeInfo *node_parent,
2085 char *level_directory)
2088 struct dirent *dir_entry;
2089 boolean valid_entry_found = FALSE;
2091 if ((dir = opendir(level_directory)) == NULL)
2093 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2097 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2099 struct stat file_status;
2100 char *directory_name = dir_entry->d_name;
2101 char *directory_path = getPath2(level_directory, directory_name);
2103 /* skip entries for current and parent directory */
2104 if (strcmp(directory_name, ".") == 0 ||
2105 strcmp(directory_name, "..") == 0)
2107 free(directory_path);
2111 /* find out if directory entry is itself a directory */
2112 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2113 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2115 free(directory_path);
2119 free(directory_path);
2121 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2122 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2123 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2126 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2133 if (!valid_entry_found)
2135 /* check if this directory directly contains a file "levelinfo.conf" */
2136 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2137 level_directory, ".");
2140 if (!valid_entry_found)
2141 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2145 void LoadLevelInfo()
2147 InitUserLevelDirectory(getLoginName());
2149 DrawInitText("Loading level series:", 120, FC_GREEN);
2151 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2152 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2154 /* before sorting, the first entries will be from the user directory */
2155 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2157 if (leveldir_first == NULL)
2158 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2160 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2163 dumpTreeInfo(leveldir_first, 0);
2167 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2168 TreeInfo *node_parent,
2169 char *base_directory,
2170 char *directory_name, int type)
2172 char *directory_path = getPath2(base_directory, directory_name);
2173 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2174 SetupFileHash *setup_file_hash = NULL;
2175 TreeInfo *artwork_new = NULL;
2178 if (access(filename, F_OK) == 0) /* file exists */
2179 setup_file_hash = loadSetupFileHash(filename);
2181 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2184 struct dirent *dir_entry;
2185 boolean valid_file_found = FALSE;
2187 if ((dir = opendir(directory_path)) != NULL)
2189 while ((dir_entry = readdir(dir)) != NULL)
2191 char *entry_name = dir_entry->d_name;
2193 if (FileIsArtworkType(entry_name, type))
2195 valid_file_found = TRUE;
2203 if (!valid_file_found)
2205 if (strcmp(directory_name, ".") != 0)
2206 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2208 free(directory_path);
2215 artwork_new = newTreeInfo();
2218 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2220 setTreeInfoToDefaults(artwork_new, type);
2222 artwork_new->filename = getStringCopy(directory_name);
2224 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2227 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2230 /* set all structure fields according to the token/value pairs */
2232 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2233 setSetupInfo(levelinfo_tokens, i,
2234 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2238 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2239 setString(&artwork_new->name, artwork_new->filename);
2241 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2243 free(artwork_new->name);
2244 artwork_new->name = getStringCopy(artwork_new->filename);
2249 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2252 if (artwork_new->identifier == NULL)
2253 artwork_new->identifier = getStringCopy(artwork_new->filename);
2255 if (artwork_new->name_sorting == NULL)
2256 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2259 if (node_parent == NULL) /* top level group */
2261 artwork_new->basepath = getStringCopy(base_directory);
2262 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2264 else /* sub level group */
2266 artwork_new->basepath = getStringCopy(node_parent->basepath);
2267 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2271 artwork_new->user_defined =
2272 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2274 artwork_new->user_defined =
2275 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2278 /* (may use ".sort_priority" from "setup_file_hash" above) */
2279 artwork_new->color = ARTWORKCOLOR(artwork_new);
2281 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2283 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2286 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2289 if (artwork_new->name != NULL)
2291 free(artwork_new->name);
2292 artwork_new->name = NULL;
2297 if (artwork_new->identifier != NULL)
2299 free(artwork_new->identifier);
2300 artwork_new->identifier = NULL;
2304 if (strcmp(artwork_new->filename, ".") == 0)
2306 if (artwork_new->user_defined)
2309 setString(&artwork_new->identifier, "private");
2311 artwork_new->identifier = getStringCopy("private");
2313 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2318 setString(&artwork_new->identifier, "classic");
2320 artwork_new->identifier = getStringCopy("classic");
2322 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2325 /* set to new values after changing ".sort_priority" */
2326 artwork_new->color = ARTWORKCOLOR(artwork_new);
2328 setString(&artwork_new->class_desc,
2329 getLevelClassDescription(artwork_new));
2331 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2337 setString(&artwork_new->identifier, artwork_new->filename);
2339 artwork_new->identifier = getStringCopy(artwork_new->filename);
2344 setString(&artwork_new->name, artwork_new->identifier);
2345 setString(&artwork_new->name_sorting, artwork_new->name);
2347 artwork_new->name = getStringCopy(artwork_new->identifier);
2348 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2352 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2354 pushTreeInfo(node_first, artwork_new);
2356 freeSetupFileHash(setup_file_hash);
2358 free(directory_path);
2364 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2365 TreeInfo *node_parent,
2366 char *base_directory, int type)
2369 struct dirent *dir_entry;
2370 boolean valid_entry_found = FALSE;
2372 if ((dir = opendir(base_directory)) == NULL)
2374 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2375 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2379 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2381 struct stat file_status;
2382 char *directory_name = dir_entry->d_name;
2383 char *directory_path = getPath2(base_directory, directory_name);
2385 /* skip entries for current and parent directory */
2386 if (strcmp(directory_name, ".") == 0 ||
2387 strcmp(directory_name, "..") == 0)
2389 free(directory_path);
2393 /* find out if directory entry is itself a directory */
2394 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2395 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2397 free(directory_path);
2401 free(directory_path);
2403 /* check if this directory contains artwork with or without config file */
2404 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2406 directory_name, type);
2411 /* check if this directory directly contains artwork itself */
2412 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2413 base_directory, ".",
2415 if (!valid_entry_found)
2416 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2420 static TreeInfo *getDummyArtworkInfo(int type)
2422 /* this is only needed when there is completely no artwork available */
2423 TreeInfo *artwork_new = newTreeInfo();
2425 setTreeInfoToDefaults(artwork_new, type);
2428 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2429 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2430 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2432 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2433 setString(&artwork_new->name, UNDEFINED_FILENAME);
2434 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2436 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2437 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2438 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2440 if (artwork_new->name != NULL)
2441 free(artwork_new->name);
2443 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2444 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2445 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2451 void LoadArtworkInfo()
2453 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2455 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2456 options.graphics_directory,
2457 TREE_TYPE_GRAPHICS_DIR);
2458 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2459 getUserGraphicsDir(),
2460 TREE_TYPE_GRAPHICS_DIR);
2462 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2463 options.sounds_directory,
2464 TREE_TYPE_SOUNDS_DIR);
2465 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2467 TREE_TYPE_SOUNDS_DIR);
2469 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2470 options.music_directory,
2471 TREE_TYPE_MUSIC_DIR);
2472 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2474 TREE_TYPE_MUSIC_DIR);
2476 if (artwork.gfx_first == NULL)
2477 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2478 if (artwork.snd_first == NULL)
2479 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2480 if (artwork.mus_first == NULL)
2481 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2483 /* before sorting, the first entries will be from the user directory */
2484 artwork.gfx_current =
2485 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2486 if (artwork.gfx_current == NULL)
2487 artwork.gfx_current =
2488 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2489 if (artwork.gfx_current == NULL)
2490 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2492 artwork.snd_current =
2493 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2494 if (artwork.snd_current == NULL)
2495 artwork.snd_current =
2496 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2497 if (artwork.snd_current == NULL)
2498 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2500 artwork.mus_current =
2501 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2502 if (artwork.mus_current == NULL)
2503 artwork.mus_current =
2504 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2505 if (artwork.mus_current == NULL)
2506 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2508 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2509 artwork.snd_current_identifier = artwork.snd_current->identifier;
2510 artwork.mus_current_identifier = artwork.mus_current->identifier;
2513 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2514 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2515 printf("music set == %s\n\n", artwork.mus_current_identifier);
2518 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2519 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2520 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2523 dumpTreeInfo(artwork.gfx_first, 0);
2524 dumpTreeInfo(artwork.snd_first, 0);
2525 dumpTreeInfo(artwork.mus_first, 0);
2529 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2530 LevelDirTree *level_node)
2532 /* recursively check all level directories for artwork sub-directories */
2536 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2537 ARTWORK_DIRECTORY((*artwork_node)->type));
2540 if (!level_node->parent_link)
2541 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2542 level_node->filename, level_node->name);
2545 if (!level_node->parent_link)
2547 TreeInfo *topnode_last = *artwork_node;
2549 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2550 (*artwork_node)->type);
2552 if (topnode_last != *artwork_node)
2554 free((*artwork_node)->identifier);
2555 free((*artwork_node)->name);
2556 free((*artwork_node)->name_sorting);
2558 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2559 (*artwork_node)->name = getStringCopy(level_node->name);
2560 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2562 (*artwork_node)->sort_priority = level_node->sort_priority;
2563 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2569 if (level_node->node_group != NULL)
2570 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2572 level_node = level_node->next;
2576 void LoadLevelArtworkInfo()
2578 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2580 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2581 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2582 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2584 /* needed for reloading level artwork not known at ealier stage */
2586 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2588 artwork.gfx_current =
2589 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2590 if (artwork.gfx_current == NULL)
2591 artwork.gfx_current =
2592 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2593 if (artwork.gfx_current == NULL)
2594 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2597 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2599 artwork.snd_current =
2600 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2601 if (artwork.snd_current == NULL)
2602 artwork.snd_current =
2603 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2604 if (artwork.snd_current == NULL)
2605 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2608 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2610 artwork.mus_current =
2611 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2612 if (artwork.mus_current == NULL)
2613 artwork.mus_current =
2614 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2615 if (artwork.mus_current == NULL)
2616 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2619 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2620 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2621 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2624 dumpTreeInfo(artwork.gfx_first, 0);
2625 dumpTreeInfo(artwork.snd_first, 0);
2626 dumpTreeInfo(artwork.mus_first, 0);
2630 static void SaveUserLevelInfo()
2632 LevelDirTree *level_info;
2637 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2639 if (!(file = fopen(filename, MODE_WRITE)))
2641 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2646 level_info = newTreeInfo();
2648 /* always start with reliable default values */
2649 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2652 setString(&level_info->name, getLoginName());
2653 setString(&level_info->author, getRealName());
2654 level_info->levels = 100;
2655 level_info->first_level = 1;
2656 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2657 level_info->readonly = FALSE;
2658 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2659 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2660 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2662 ldi.name = getStringCopy(getLoginName());
2663 ldi.author = getStringCopy(getRealName());
2665 ldi.first_level = 1;
2666 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2667 ldi.readonly = FALSE;
2668 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2669 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2670 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2673 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2674 getCookie("LEVELINFO")));
2677 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2678 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2679 i != LEVELINFO_TOKEN_NAME_SORTING &&
2680 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2681 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2685 SetFilePermissions(filename, PERMS_PRIVATE);
2687 freeTreeInfo(level_info);
2691 char *getSetupValue(int type, void *value)
2693 static char value_string[MAX_LINE_LEN];
2701 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2705 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2709 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2713 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2717 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2721 sprintf(value_string, "%d", *(int *)value);
2725 strcpy(value_string, *(char **)value);
2729 value_string[0] = '\0';
2733 return value_string;
2736 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2740 static char token_string[MAX_LINE_LEN];
2741 int token_type = token_info[token_nr].type;
2742 void *setup_value = token_info[token_nr].value;
2743 char *token_text = token_info[token_nr].text;
2744 char *value_string = getSetupValue(token_type, setup_value);
2746 /* build complete token string */
2747 sprintf(token_string, "%s%s", prefix, token_text);
2749 /* build setup entry line */
2750 line = getFormattedSetupEntry(token_string, value_string);
2752 if (token_type == TYPE_KEY_X11)
2754 Key key = *(Key *)setup_value;
2755 char *keyname = getKeyNameFromKey(key);
2757 /* add comment, if useful */
2758 if (strcmp(keyname, "(undefined)") != 0 &&
2759 strcmp(keyname, "(unknown)") != 0)
2761 /* add at least one whitespace */
2763 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2767 strcat(line, keyname);
2774 void LoadLevelSetup_LastSeries()
2776 /* ----------------------------------------------------------------------- */
2777 /* ~/.<program>/levelsetup.conf */
2778 /* ----------------------------------------------------------------------- */
2780 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2781 SetupFileHash *level_setup_hash = NULL;
2783 /* always start with reliable default values */
2784 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2786 if ((level_setup_hash = loadSetupFileHash(filename)))
2788 char *last_level_series =
2789 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2791 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2793 if (leveldir_current == NULL)
2794 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2796 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2798 freeSetupFileHash(level_setup_hash);
2801 Error(ERR_WARN, "using default setup values");
2806 void SaveLevelSetup_LastSeries()
2808 /* ----------------------------------------------------------------------- */
2809 /* ~/.<program>/levelsetup.conf */
2810 /* ----------------------------------------------------------------------- */
2812 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2813 char *level_subdir = leveldir_current->filename;
2816 InitUserDataDirectory();
2818 if (!(file = fopen(filename, MODE_WRITE)))
2820 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2825 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2826 getCookie("LEVELSETUP")));
2827 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2832 SetFilePermissions(filename, PERMS_PRIVATE);
2837 static void checkSeriesInfo()
2839 static char *level_directory = NULL;
2841 struct dirent *dir_entry;
2843 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2845 level_directory = getPath2((leveldir_current->user_defined ?
2846 getUserLevelDir(NULL) :
2847 options.level_directory),
2848 leveldir_current->fullpath);
2850 if ((dir = opendir(level_directory)) == NULL)
2852 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2856 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2858 if (strlen(dir_entry->d_name) > 4 &&
2859 dir_entry->d_name[3] == '.' &&
2860 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2862 char levelnum_str[4];
2865 strncpy(levelnum_str, dir_entry->d_name, 3);
2866 levelnum_str[3] = '\0';
2868 levelnum_value = atoi(levelnum_str);
2871 if (levelnum_value < leveldir_current->first_level)
2873 Error(ERR_WARN, "additional level %d found", levelnum_value);
2874 leveldir_current->first_level = levelnum_value;
2876 else if (levelnum_value > leveldir_current->last_level)
2878 Error(ERR_WARN, "additional level %d found", levelnum_value);
2879 leveldir_current->last_level = levelnum_value;
2888 void LoadLevelSetup_SeriesInfo()
2891 SetupFileHash *level_setup_hash = NULL;
2892 char *level_subdir = leveldir_current->filename;
2894 /* always start with reliable default values */
2895 level_nr = leveldir_current->first_level;
2897 checkSeriesInfo(leveldir_current);
2899 /* ----------------------------------------------------------------------- */
2900 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2901 /* ----------------------------------------------------------------------- */
2903 level_subdir = leveldir_current->filename;
2905 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2907 if ((level_setup_hash = loadSetupFileHash(filename)))
2911 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2915 level_nr = atoi(token_value);
2917 if (level_nr < leveldir_current->first_level)
2918 level_nr = leveldir_current->first_level;
2919 if (level_nr > leveldir_current->last_level)
2920 level_nr = leveldir_current->last_level;
2923 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2927 int level_nr = atoi(token_value);
2929 if (level_nr < leveldir_current->first_level)
2930 level_nr = leveldir_current->first_level;
2931 if (level_nr > leveldir_current->last_level + 1)
2932 level_nr = leveldir_current->last_level;
2934 if (leveldir_current->user_defined)
2935 level_nr = leveldir_current->last_level;
2937 leveldir_current->handicap_level = level_nr;
2940 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2942 freeSetupFileHash(level_setup_hash);
2945 Error(ERR_WARN, "using default setup values");
2950 void SaveLevelSetup_SeriesInfo()
2953 char *level_subdir = leveldir_current->filename;
2954 char *level_nr_str = int2str(level_nr, 0);
2955 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2958 /* ----------------------------------------------------------------------- */
2959 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2960 /* ----------------------------------------------------------------------- */
2962 InitLevelSetupDirectory(level_subdir);
2964 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2966 if (!(file = fopen(filename, MODE_WRITE)))
2968 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2973 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2974 getCookie("LEVELSETUP")));
2975 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2977 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2978 handicap_level_str));
2982 SetFilePermissions(filename, PERMS_PRIVATE);