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;
487 for (i = 0; basenames[i] != NULL; i++)
489 if (filename != NULL)
492 filename = getPath2(getCurrentLevelDir(), basenames[i]);
493 if (fileExists(filename))
500 static char *getCorrectedArtworkBasename(char *basename)
502 char *basename_corrected = basename;
504 #if defined(PLATFORM_MSDOS)
505 if (program.filename_prefix != NULL)
507 int prefix_len = strlen(program.filename_prefix);
509 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
510 basename_corrected = &basename[prefix_len];
512 /* if corrected filename is still longer than standard MS-DOS filename
513 size (8 characters + 1 dot + 3 characters file extension), shorten
514 filename by writing file extension after 8th basename character */
515 if (strlen(basename_corrected) > 8 + 1 + 3)
517 static char *msdos_filename = NULL;
519 if (msdos_filename != NULL)
520 free(msdos_filename);
522 msdos_filename = getStringCopy(basename_corrected);
523 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
525 basename_corrected = msdos_filename;
530 return basename_corrected;
533 char *getCustomImageFilename(char *basename)
535 static char *filename = NULL;
536 boolean skip_setup_artwork = FALSE;
538 if (filename != NULL)
541 basename = getCorrectedArtworkBasename(basename);
543 if (!setup.override_level_graphics)
545 /* 1st try: look for special artwork in current level series directory */
546 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
547 if (fileExists(filename))
552 /* check if there is special artwork configured in level series config */
553 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
555 /* 2nd try: look for special artwork configured in level series config */
556 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
557 if (fileExists(filename))
562 /* take missing artwork configured in level set config from default */
563 skip_setup_artwork = TRUE;
567 if (!skip_setup_artwork)
569 /* 3rd try: look for special artwork in configured artwork directory */
570 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
571 if (fileExists(filename))
577 /* 4th try: look for default artwork in new default artwork directory */
578 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
579 if (fileExists(filename))
584 /* 5th try: look for default artwork in old default artwork directory */
585 filename = getPath2(options.graphics_directory, basename);
586 if (fileExists(filename))
589 return NULL; /* cannot find specified artwork file anywhere */
592 char *getCustomSoundFilename(char *basename)
594 static char *filename = NULL;
595 boolean skip_setup_artwork = FALSE;
597 if (filename != NULL)
600 basename = getCorrectedArtworkBasename(basename);
602 if (!setup.override_level_sounds)
604 /* 1st try: look for special artwork in current level series directory */
605 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
606 if (fileExists(filename))
611 /* check if there is special artwork configured in level series config */
612 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
614 /* 2nd try: look for special artwork configured in level series config */
615 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
616 if (fileExists(filename))
621 /* take missing artwork configured in level set config from default */
622 skip_setup_artwork = TRUE;
626 if (!skip_setup_artwork)
628 /* 3rd try: look for special artwork in configured artwork directory */
629 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
630 if (fileExists(filename))
636 /* 4th try: look for default artwork in new default artwork directory */
637 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
638 if (fileExists(filename))
643 /* 5th try: look for default artwork in old default artwork directory */
644 filename = getPath2(options.sounds_directory, basename);
645 if (fileExists(filename))
648 return NULL; /* cannot find specified artwork file anywhere */
651 char *getCustomMusicFilename(char *basename)
653 static char *filename = NULL;
654 boolean skip_setup_artwork = FALSE;
656 if (filename != NULL)
659 basename = getCorrectedArtworkBasename(basename);
661 if (!setup.override_level_music)
663 /* 1st try: look for special artwork in current level series directory */
664 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
665 if (fileExists(filename))
670 /* check if there is special artwork configured in level series config */
671 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
673 /* 2nd try: look for special artwork configured in level series config */
674 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
675 if (fileExists(filename))
680 /* take missing artwork configured in level set config from default */
681 skip_setup_artwork = TRUE;
685 if (!skip_setup_artwork)
687 /* 3rd try: look for special artwork in configured artwork directory */
688 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
689 if (fileExists(filename))
695 /* 4th try: look for default artwork in new default artwork directory */
696 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
697 if (fileExists(filename))
702 /* 5th try: look for default artwork in old default artwork directory */
703 filename = getPath2(options.music_directory, basename);
704 if (fileExists(filename))
707 return NULL; /* cannot find specified artwork file anywhere */
710 char *getCustomArtworkFilename(char *basename, int type)
712 if (type == ARTWORK_TYPE_GRAPHICS)
713 return getCustomImageFilename(basename);
714 else if (type == ARTWORK_TYPE_SOUNDS)
715 return getCustomSoundFilename(basename);
716 else if (type == ARTWORK_TYPE_MUSIC)
717 return getCustomMusicFilename(basename);
719 return UNDEFINED_FILENAME;
722 char *getCustomArtworkConfigFilename(int type)
724 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
727 char *getCustomArtworkLevelConfigFilename(int type)
729 static char *filename = NULL;
731 if (filename != NULL)
734 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
739 char *getCustomMusicDirectory(void)
741 static char *directory = NULL;
742 boolean skip_setup_artwork = FALSE;
744 if (directory != NULL)
747 if (!setup.override_level_music)
749 /* 1st try: look for special artwork in current level series directory */
750 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
751 if (fileExists(directory))
756 /* check if there is special artwork configured in level series config */
757 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
759 /* 2nd try: look for special artwork configured in level series config */
760 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
761 if (fileExists(directory))
766 /* take missing artwork configured in level set config from default */
767 skip_setup_artwork = TRUE;
771 if (!skip_setup_artwork)
773 /* 3rd try: look for special artwork in configured artwork directory */
774 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
775 if (fileExists(directory))
781 /* 4th try: look for default artwork in new default artwork directory */
782 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
783 if (fileExists(directory))
788 /* 5th try: look for default artwork in old default artwork directory */
789 directory = getStringCopy(options.music_directory);
790 if (fileExists(directory))
793 return NULL; /* cannot find specified artwork file anywhere */
796 void InitTapeDirectory(char *level_subdir)
798 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
799 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
800 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
803 void InitScoreDirectory(char *level_subdir)
805 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
806 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
807 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
810 static void SaveUserLevelInfo();
812 void InitUserLevelDirectory(char *level_subdir)
814 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
816 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
817 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
818 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
824 void InitLevelSetupDirectory(char *level_subdir)
826 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
827 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
828 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
832 /* ------------------------------------------------------------------------- */
833 /* some functions to handle lists of level directories */
834 /* ------------------------------------------------------------------------- */
836 TreeInfo *newTreeInfo()
838 return checked_calloc(sizeof(TreeInfo));
841 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
843 node_new->next = *node_first;
844 *node_first = node_new;
847 int numTreeInfo(TreeInfo *node)
860 boolean validLevelSeries(TreeInfo *node)
862 return (node != NULL && !node->node_group && !node->parent_link);
865 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
870 if (node->node_group) /* enter level group (step down into tree) */
871 return getFirstValidTreeInfoEntry(node->node_group);
872 else if (node->parent_link) /* skip start entry of level group */
874 if (node->next) /* get first real level series entry */
875 return getFirstValidTreeInfoEntry(node->next);
876 else /* leave empty level group and go on */
877 return getFirstValidTreeInfoEntry(node->node_parent->next);
879 else /* this seems to be a regular level series */
883 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
888 if (node->node_parent == NULL) /* top level group */
889 return *node->node_top;
890 else /* sub level group */
891 return node->node_parent->node_group;
894 int numTreeInfoInGroup(TreeInfo *node)
896 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
899 int posTreeInfo(TreeInfo *node)
901 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
906 if (node_cmp == node)
910 node_cmp = node_cmp->next;
916 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
918 TreeInfo *node_default = node;
933 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
935 if (identifier == NULL)
940 if (node->node_group)
942 TreeInfo *node_group;
944 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
949 else if (!node->parent_link)
951 if (strcmp(identifier, node->identifier) == 0)
961 void dumpTreeInfo(TreeInfo *node, int depth)
965 printf("Dumping TreeInfo:\n");
969 for (i = 0; i < (depth + 1) * 3; i++)
973 printf("filename == '%s' ['%s', '%s'] [%d])\n",
974 node->filename, node->fullpath, node->basepath, node->user_defined);
976 printf("filename == '%s' (%s) [%s] (%d)\n",
977 node->filename, node->name, node->identifier, node->sort_priority);
980 if (node->node_group != NULL)
981 dumpTreeInfo(node->node_group, depth + 1);
987 void sortTreeInfo(TreeInfo **node_first,
988 int (*compare_function)(const void *, const void *))
990 int num_nodes = numTreeInfo(*node_first);
991 TreeInfo **sort_array;
992 TreeInfo *node = *node_first;
998 /* allocate array for sorting structure pointers */
999 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
1001 /* writing structure pointers to sorting array */
1002 while (i < num_nodes && node) /* double boundary check... */
1004 sort_array[i] = node;
1010 /* sorting the structure pointers in the sorting array */
1011 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
1014 /* update the linkage of list elements with the sorted node array */
1015 for (i = 0; i < num_nodes - 1; i++)
1016 sort_array[i]->next = sort_array[i + 1];
1017 sort_array[num_nodes - 1]->next = NULL;
1019 /* update the linkage of the main list anchor pointer */
1020 *node_first = sort_array[0];
1024 /* now recursively sort the level group structures */
1028 if (node->node_group != NULL)
1029 sortTreeInfo(&node->node_group, compare_function);
1036 /* ========================================================================= */
1037 /* some stuff from "files.c" */
1038 /* ========================================================================= */
1040 #if defined(PLATFORM_WIN32)
1042 #define S_IRGRP S_IRUSR
1045 #define S_IROTH S_IRUSR
1048 #define S_IWGRP S_IWUSR
1051 #define S_IWOTH S_IWUSR
1054 #define S_IXGRP S_IXUSR
1057 #define S_IXOTH S_IXUSR
1060 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1065 #endif /* PLATFORM_WIN32 */
1067 /* file permissions for newly written files */
1068 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1069 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1070 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1072 #define MODE_W_PRIVATE (S_IWUSR)
1073 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1074 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1076 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1077 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1079 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1080 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1082 char *getUserDataDir(void)
1084 static char *userdata_dir = NULL;
1086 if (userdata_dir == NULL)
1087 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1089 return userdata_dir;
1092 char *getCommonDataDir(void)
1094 static char *common_data_dir = NULL;
1096 #if defined(PLATFORM_WIN32)
1097 if (common_data_dir == NULL)
1099 char *dir = checked_malloc(MAX_PATH + 1);
1101 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1102 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1103 common_data_dir = getPath2(dir, program.userdata_directory);
1105 common_data_dir = options.rw_base_directory;
1108 if (common_data_dir == NULL)
1109 common_data_dir = options.rw_base_directory;
1112 return common_data_dir;
1117 return getUserDataDir();
1120 static mode_t posix_umask(mode_t mask)
1122 #if defined(PLATFORM_UNIX)
1129 static int posix_mkdir(const char *pathname, mode_t mode)
1131 #if defined(PLATFORM_WIN32)
1132 return mkdir(pathname);
1134 return mkdir(pathname, mode);
1138 void createDirectory(char *dir, char *text, int permission_class)
1140 /* leave "other" permissions in umask untouched, but ensure group parts
1141 of USERDATA_DIR_MODE are not masked */
1142 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1143 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1144 mode_t normal_umask = posix_umask(0);
1145 mode_t group_umask = ~(dir_mode & S_IRWXG);
1146 posix_umask(normal_umask & group_umask);
1148 if (access(dir, F_OK) != 0)
1149 if (posix_mkdir(dir, dir_mode) != 0)
1150 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1152 posix_umask(normal_umask); /* reset normal umask */
1155 void InitUserDataDirectory()
1157 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1160 void SetFilePermissions(char *filename, int permission_class)
1162 chmod(filename, (permission_class == PERMS_PRIVATE ?
1163 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1166 char *getCookie(char *file_type)
1168 static char cookie[MAX_COOKIE_LEN + 1];
1170 if (strlen(program.cookie_prefix) + 1 +
1171 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1172 return "[COOKIE ERROR]"; /* should never happen */
1174 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1175 program.cookie_prefix, file_type,
1176 program.version_major, program.version_minor);
1181 int getFileVersionFromCookieString(const char *cookie)
1183 const char *ptr_cookie1, *ptr_cookie2;
1184 const char *pattern1 = "_FILE_VERSION_";
1185 const char *pattern2 = "?.?";
1186 const int len_cookie = strlen(cookie);
1187 const int len_pattern1 = strlen(pattern1);
1188 const int len_pattern2 = strlen(pattern2);
1189 const int len_pattern = len_pattern1 + len_pattern2;
1190 int version_major, version_minor;
1192 if (len_cookie <= len_pattern)
1195 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1196 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1198 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1201 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1202 ptr_cookie2[1] != '.' ||
1203 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1206 version_major = ptr_cookie2[0] - '0';
1207 version_minor = ptr_cookie2[2] - '0';
1209 return VERSION_IDENT(version_major, version_minor, 0, 0);
1212 boolean checkCookieString(const char *cookie, const char *template)
1214 const char *pattern = "_FILE_VERSION_?.?";
1215 const int len_cookie = strlen(cookie);
1216 const int len_template = strlen(template);
1217 const int len_pattern = strlen(pattern);
1219 if (len_cookie != len_template)
1222 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1228 /* ------------------------------------------------------------------------- */
1229 /* setup file list and hash handling functions */
1230 /* ------------------------------------------------------------------------- */
1232 char *getFormattedSetupEntry(char *token, char *value)
1235 static char entry[MAX_LINE_LEN];
1237 /* if value is an empty string, just return token without value */
1241 /* start with the token and some spaces to format output line */
1242 sprintf(entry, "%s:", token);
1243 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1246 /* continue with the token's value */
1247 strcat(entry, value);
1252 SetupFileList *newSetupFileList(char *token, char *value)
1254 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1256 new->token = getStringCopy(token);
1257 new->value = getStringCopy(value);
1264 void freeSetupFileList(SetupFileList *list)
1274 freeSetupFileList(list->next);
1278 char *getListEntry(SetupFileList *list, char *token)
1283 if (strcmp(list->token, token) == 0)
1286 return getListEntry(list->next, token);
1289 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1294 if (strcmp(list->token, token) == 0)
1299 list->value = getStringCopy(value);
1303 else if (list->next == NULL)
1304 return (list->next = newSetupFileList(token, value));
1306 return setListEntry(list->next, token, value);
1309 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1314 if (list->next == NULL)
1315 return (list->next = newSetupFileList(token, value));
1317 return addListEntry(list->next, token, value);
1321 static void printSetupFileList(SetupFileList *list)
1326 printf("token: '%s'\n", list->token);
1327 printf("value: '%s'\n", list->value);
1329 printSetupFileList(list->next);
1334 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1335 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1336 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1337 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1339 #define insert_hash_entry hashtable_insert
1340 #define search_hash_entry hashtable_search
1341 #define change_hash_entry hashtable_change
1342 #define remove_hash_entry hashtable_remove
1345 static unsigned int get_hash_from_key(void *key)
1350 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1351 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1352 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1353 it works better than many other constants, prime or not) has never been
1354 adequately explained.
1356 If you just want to have a good hash function, and cannot wait, djb2
1357 is one of the best string hash functions i know. It has excellent
1358 distribution and speed on many different sets of keys and table sizes.
1359 You are not likely to do better with one of the "well known" functions
1360 such as PJW, K&R, etc.
1362 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1365 char *str = (char *)key;
1366 unsigned int hash = 5381;
1369 while ((c = *str++))
1370 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1375 static int keys_are_equal(void *key1, void *key2)
1377 return (strcmp((char *)key1, (char *)key2) == 0);
1380 SetupFileHash *newSetupFileHash()
1382 SetupFileHash *new_hash =
1383 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1385 if (new_hash == NULL)
1386 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1391 void freeSetupFileHash(SetupFileHash *hash)
1396 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1399 char *getHashEntry(SetupFileHash *hash, char *token)
1404 return search_hash_entry(hash, token);
1407 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1414 value_copy = getStringCopy(value);
1416 /* change value; if it does not exist, insert it as new */
1417 if (!change_hash_entry(hash, token, value_copy))
1418 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1419 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1422 char *removeHashEntry(SetupFileHash *hash, char *token)
1427 return remove_hash_entry(hash, token);
1432 static void printSetupFileHash(SetupFileHash *hash)
1434 BEGIN_HASH_ITERATION(hash, itr)
1436 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1437 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1439 END_HASH_ITERATION(hash, itr)
1444 static void *loadSetupFileData(char *filename, boolean use_hash)
1446 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1447 char *token, *value, *line_ptr;
1448 void *setup_file_data, *insert_ptr = NULL;
1449 boolean read_continued_line = FALSE;
1453 setup_file_data = newSetupFileHash();
1455 insert_ptr = setup_file_data = newSetupFileList("", "");
1457 if (!(file = fopen(filename, MODE_READ)))
1459 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1465 /* read next line of input file */
1466 if (!fgets(line, MAX_LINE_LEN, file))
1469 /* cut trailing newline or carriage return */
1470 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1471 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1474 if (read_continued_line)
1476 /* cut leading whitespaces from input line */
1477 for (line_ptr = line; *line_ptr; line_ptr++)
1478 if (*line_ptr != ' ' && *line_ptr != '\t')
1481 /* append new line to existing line, if there is enough space */
1482 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1483 strcat(previous_line, line_ptr);
1485 strcpy(line, previous_line); /* copy storage buffer to line */
1487 read_continued_line = FALSE;
1490 /* if the last character is '\', continue at next line */
1491 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1493 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1494 strcpy(previous_line, line); /* copy line to storage buffer */
1496 read_continued_line = TRUE;
1501 /* cut trailing comment from input line */
1502 for (line_ptr = line; *line_ptr; line_ptr++)
1504 if (*line_ptr == '#')
1511 /* cut trailing whitespaces from input line */
1512 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1513 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1516 /* ignore empty lines */
1520 /* cut leading whitespaces from token */
1521 for (token = line; *token; token++)
1522 if (*token != ' ' && *token != '\t')
1525 /* start with empty value as reliable default */
1528 /* find end of token to determine start of value */
1529 for (line_ptr = token; *line_ptr; line_ptr++)
1531 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1533 *line_ptr = '\0'; /* terminate token string */
1534 value = line_ptr + 1; /* set beginning of value */
1540 /* cut leading whitespaces from value */
1541 for (; *value; value++)
1542 if (*value != ' ' && *value != '\t')
1547 value = "true"; /* treat tokens without value as "true" */
1553 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1555 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1563 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1564 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1568 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1569 SetupFileList *first_valid_list_entry = setup_file_list->next;
1571 /* free empty list header */
1572 setup_file_list->next = NULL;
1573 freeSetupFileList(setup_file_list);
1574 setup_file_data = first_valid_list_entry;
1576 if (first_valid_list_entry == NULL)
1577 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1580 return setup_file_data;
1583 SetupFileList *loadSetupFileList(char *filename)
1585 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1588 SetupFileHash *loadSetupFileHash(char *filename)
1590 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1593 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1596 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1599 Error(ERR_WARN, "configuration file has no file identifier");
1600 else if (!checkCookieString(value, identifier))
1601 Error(ERR_WARN, "configuration file has wrong file identifier");
1605 /* ========================================================================= */
1606 /* setup file stuff */
1607 /* ========================================================================= */
1609 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1610 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1611 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1613 /* level directory info */
1614 #define LEVELINFO_TOKEN_IDENTIFIER 0
1615 #define LEVELINFO_TOKEN_NAME 1
1616 #define LEVELINFO_TOKEN_NAME_SORTING 2
1617 #define LEVELINFO_TOKEN_AUTHOR 3
1618 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1619 #define LEVELINFO_TOKEN_LEVELS 5
1620 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1621 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1622 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1623 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1624 #define LEVELINFO_TOKEN_READONLY 10
1625 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1626 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1627 #define LEVELINFO_TOKEN_MUSIC_SET 13
1629 #define NUM_LEVELINFO_TOKENS 14
1631 static LevelDirTree ldi;
1633 static struct TokenInfo levelinfo_tokens[] =
1635 /* level directory info */
1636 { TYPE_STRING, &ldi.identifier, "identifier" },
1637 { TYPE_STRING, &ldi.name, "name" },
1638 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1639 { TYPE_STRING, &ldi.author, "author" },
1640 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1641 { TYPE_INTEGER, &ldi.levels, "levels" },
1642 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1643 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1644 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1645 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1646 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1647 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1648 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1649 { TYPE_STRING, &ldi.music_set, "music_set" }
1652 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1656 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1657 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1658 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1659 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1662 ldi->node_parent = NULL;
1663 ldi->node_group = NULL;
1667 ldi->cl_cursor = -1;
1669 ldi->filename = NULL;
1670 ldi->fullpath = NULL;
1671 ldi->basepath = NULL;
1672 ldi->identifier = NULL;
1673 ldi->name = getStringCopy(ANONYMOUS_NAME);
1674 ldi->name_sorting = NULL;
1675 ldi->author = getStringCopy(ANONYMOUS_NAME);
1677 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1678 ldi->latest_engine = FALSE; /* default: get from level */
1679 ldi->parent_link = FALSE;
1680 ldi->user_defined = FALSE;
1682 ldi->class_desc = NULL;
1684 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1686 ldi->imported_from = NULL;
1688 ldi->graphics_set = NULL;
1689 ldi->sounds_set = NULL;
1690 ldi->music_set = NULL;
1691 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1692 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1693 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1696 ldi->first_level = 0;
1697 ldi->last_level = 0;
1698 ldi->level_group = FALSE;
1699 ldi->handicap_level = 0;
1700 ldi->readonly = TRUE;
1704 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1708 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1710 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1716 /* copy all values from the parent structure */
1718 ldi->type = parent->type;
1720 ldi->node_top = parent->node_top;
1721 ldi->node_parent = parent;
1722 ldi->node_group = NULL;
1726 ldi->cl_cursor = -1;
1728 ldi->filename = NULL;
1729 ldi->fullpath = NULL;
1730 ldi->basepath = NULL;
1731 ldi->identifier = NULL;
1732 ldi->name = getStringCopy(ANONYMOUS_NAME);
1733 ldi->name_sorting = NULL;
1734 ldi->author = getStringCopy(parent->author);
1736 ldi->sort_priority = parent->sort_priority;
1737 ldi->latest_engine = parent->latest_engine;
1738 ldi->parent_link = FALSE;
1739 ldi->user_defined = parent->user_defined;
1740 ldi->color = parent->color;
1741 ldi->class_desc = getStringCopy(parent->class_desc);
1743 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1745 ldi->imported_from = getStringCopy(parent->imported_from);
1747 ldi->graphics_set = NULL;
1748 ldi->sounds_set = NULL;
1749 ldi->music_set = NULL;
1750 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1751 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1752 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1755 ldi->first_level = 0;
1756 ldi->last_level = 0;
1757 ldi->level_group = FALSE;
1758 ldi->handicap_level = 0;
1759 ldi->readonly = TRUE;
1765 /* first copy all values from the parent structure ... */
1768 /* ... then set all fields to default that cannot be inherited from parent.
1769 This is especially important for all those fields that can be set from
1770 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1771 calls 'free()' for all already set token values which requires that no
1772 other structure's pointer may point to them!
1775 ldi->filename = NULL;
1776 ldi->fullpath = NULL;
1777 ldi->basepath = NULL;
1778 ldi->identifier = NULL;
1779 ldi->name = getStringCopy(ANONYMOUS_NAME);
1780 ldi->name_sorting = NULL;
1781 ldi->author = getStringCopy(parent->author);
1783 ldi->imported_from = getStringCopy(parent->imported_from);
1784 ldi->class_desc = getStringCopy(parent->class_desc);
1786 ldi->graphics_set = NULL;
1787 ldi->sounds_set = NULL;
1788 ldi->music_set = NULL;
1789 ldi->graphics_path = NULL;
1790 ldi->sounds_path = NULL;
1791 ldi->music_path = NULL;
1793 ldi->level_group = FALSE;
1794 ldi->parent_link = FALSE;
1796 ldi->node_top = parent->node_top;
1797 ldi->node_parent = parent;
1798 ldi->node_group = NULL;
1804 static void freeTreeInfo(TreeInfo *ldi)
1807 free(ldi->filename);
1809 free(ldi->fullpath);
1811 free(ldi->basepath);
1812 if (ldi->identifier)
1813 free(ldi->identifier);
1817 if (ldi->name_sorting)
1818 free(ldi->name_sorting);
1822 if (ldi->class_desc)
1823 free(ldi->class_desc);
1825 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1827 if (ldi->graphics_set)
1828 free(ldi->graphics_set);
1829 if (ldi->sounds_set)
1830 free(ldi->sounds_set);
1832 free(ldi->music_set);
1834 if (ldi->graphics_path)
1835 free(ldi->graphics_path);
1836 if (ldi->sounds_path)
1837 free(ldi->sounds_path);
1838 if (ldi->music_path)
1839 free(ldi->music_path);
1843 void setSetupInfo(struct TokenInfo *token_info,
1844 int token_nr, char *token_value)
1846 int token_type = token_info[token_nr].type;
1847 void *setup_value = token_info[token_nr].value;
1849 if (token_value == NULL)
1852 /* set setup field to corresponding token value */
1857 *(boolean *)setup_value = get_boolean_from_string(token_value);
1861 *(Key *)setup_value = getKeyFromKeyName(token_value);
1865 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1869 *(int *)setup_value = get_integer_from_string(token_value);
1873 if (*(char **)setup_value != NULL)
1874 free(*(char **)setup_value);
1875 *(char **)setup_value = getStringCopy(token_value);
1883 static int compareTreeInfoEntries(const void *object1, const void *object2)
1885 const TreeInfo *entry1 = *((TreeInfo **)object1);
1886 const TreeInfo *entry2 = *((TreeInfo **)object2);
1887 int class_sorting1, class_sorting2;
1890 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1892 class_sorting1 = LEVELSORTING(entry1);
1893 class_sorting2 = LEVELSORTING(entry2);
1897 class_sorting1 = ARTWORKSORTING(entry1);
1898 class_sorting2 = ARTWORKSORTING(entry2);
1901 if (entry1->parent_link || entry2->parent_link)
1902 compare_result = (entry1->parent_link ? -1 : +1);
1903 else if (entry1->sort_priority == entry2->sort_priority)
1905 char *name1 = getStringToLower(entry1->name_sorting);
1906 char *name2 = getStringToLower(entry2->name_sorting);
1908 compare_result = strcmp(name1, name2);
1913 else if (class_sorting1 == class_sorting2)
1914 compare_result = entry1->sort_priority - entry2->sort_priority;
1916 compare_result = class_sorting1 - class_sorting2;
1918 return compare_result;
1921 static void createParentTreeInfoNode(TreeInfo *node_parent)
1925 if (node_parent == NULL)
1928 ti_new = newTreeInfo();
1929 setTreeInfoToDefaults(ti_new, node_parent->type);
1931 ti_new->node_parent = node_parent;
1932 ti_new->parent_link = TRUE;
1935 setString(&ti_new->identifier, node_parent->identifier);
1936 setString(&ti_new->name, ".. (parent directory)");
1937 setString(&ti_new->name_sorting, ti_new->name);
1939 setString(&ti_new->filename, "..");
1940 setString(&ti_new->fullpath, node_parent->fullpath);
1942 ti_new->sort_priority = node_parent->sort_priority;
1943 ti_new->latest_engine = node_parent->latest_engine;
1945 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1947 ti_new->identifier = getStringCopy(node_parent->identifier);
1948 ti_new->name = ".. (parent directory)";
1949 ti_new->name_sorting = getStringCopy(ti_new->name);
1951 ti_new->filename = "..";
1952 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1954 ti_new->sort_priority = node_parent->sort_priority;
1955 ti_new->latest_engine = node_parent->latest_engine;
1957 ti_new->class_desc = getLevelClassDescription(ti_new);
1960 pushTreeInfo(&node_parent->node_group, ti_new);
1963 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1964 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1966 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1967 TreeInfo *node_parent,
1968 char *level_directory,
1969 char *directory_name)
1971 char *directory_path = getPath2(level_directory, directory_name);
1972 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1973 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1974 LevelDirTree *leveldir_new = NULL;
1977 if (setup_file_hash == NULL)
1979 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1981 free(directory_path);
1987 leveldir_new = newTreeInfo();
1990 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1992 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1994 leveldir_new->filename = getStringCopy(directory_name);
1996 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1998 /* set all structure fields according to the token/value pairs */
1999 ldi = *leveldir_new;
2000 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2001 setSetupInfo(levelinfo_tokens, i,
2002 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2003 *leveldir_new = ldi;
2006 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2007 setString(&leveldir_new->name, leveldir_new->filename);
2009 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2011 free(leveldir_new->name);
2012 leveldir_new->name = getStringCopy(leveldir_new->filename);
2016 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2018 if (leveldir_new->identifier == NULL)
2019 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
2021 if (leveldir_new->name_sorting == NULL)
2022 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2024 if (node_parent == NULL) /* top level group */
2026 leveldir_new->basepath = getStringCopy(level_directory);
2027 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
2029 else /* sub level group */
2031 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2032 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2035 if (leveldir_new->levels < 1)
2036 leveldir_new->levels = 1;
2038 leveldir_new->last_level =
2039 leveldir_new->first_level + leveldir_new->levels - 1;
2042 leveldir_new->user_defined =
2043 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2045 leveldir_new->user_defined =
2046 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2049 leveldir_new->color = LEVELCOLOR(leveldir_new);
2051 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2053 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2056 leveldir_new->handicap_level = /* set handicap to default value */
2057 (leveldir_new->user_defined ?
2058 leveldir_new->last_level :
2059 leveldir_new->first_level);
2061 pushTreeInfo(node_first, leveldir_new);
2063 freeSetupFileHash(setup_file_hash);
2065 if (leveldir_new->level_group)
2067 /* create node to link back to current level directory */
2068 createParentTreeInfoNode(leveldir_new);
2070 /* step into sub-directory and look for more level series */
2071 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2072 leveldir_new, directory_path);
2075 free(directory_path);
2081 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2082 TreeInfo *node_parent,
2083 char *level_directory)
2086 struct dirent *dir_entry;
2087 boolean valid_entry_found = FALSE;
2089 if ((dir = opendir(level_directory)) == NULL)
2091 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2095 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2097 struct stat file_status;
2098 char *directory_name = dir_entry->d_name;
2099 char *directory_path = getPath2(level_directory, directory_name);
2101 /* skip entries for current and parent directory */
2102 if (strcmp(directory_name, ".") == 0 ||
2103 strcmp(directory_name, "..") == 0)
2105 free(directory_path);
2109 /* find out if directory entry is itself a directory */
2110 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2111 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2113 free(directory_path);
2117 free(directory_path);
2119 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2120 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2121 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2124 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2131 if (!valid_entry_found)
2133 /* check if this directory directly contains a file "levelinfo.conf" */
2134 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2135 level_directory, ".");
2138 if (!valid_entry_found)
2139 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2143 void LoadLevelInfo()
2145 InitUserLevelDirectory(getLoginName());
2147 DrawInitText("Loading level series:", 120, FC_GREEN);
2149 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2150 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2152 /* before sorting, the first entries will be from the user directory */
2153 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2155 if (leveldir_first == NULL)
2156 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2158 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2161 dumpTreeInfo(leveldir_first, 0);
2165 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2166 TreeInfo *node_parent,
2167 char *base_directory,
2168 char *directory_name, int type)
2170 char *directory_path = getPath2(base_directory, directory_name);
2171 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2172 SetupFileHash *setup_file_hash = NULL;
2173 TreeInfo *artwork_new = NULL;
2176 if (access(filename, F_OK) == 0) /* file exists */
2177 setup_file_hash = loadSetupFileHash(filename);
2179 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2182 struct dirent *dir_entry;
2183 boolean valid_file_found = FALSE;
2185 if ((dir = opendir(directory_path)) != NULL)
2187 while ((dir_entry = readdir(dir)) != NULL)
2189 char *entry_name = dir_entry->d_name;
2191 if (FileIsArtworkType(entry_name, type))
2193 valid_file_found = TRUE;
2201 if (!valid_file_found)
2203 if (strcmp(directory_name, ".") != 0)
2204 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2206 free(directory_path);
2213 artwork_new = newTreeInfo();
2216 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2218 setTreeInfoToDefaults(artwork_new, type);
2220 artwork_new->filename = getStringCopy(directory_name);
2222 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2225 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2228 /* set all structure fields according to the token/value pairs */
2230 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2231 setSetupInfo(levelinfo_tokens, i,
2232 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2236 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2237 setString(&artwork_new->name, artwork_new->filename);
2239 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2241 free(artwork_new->name);
2242 artwork_new->name = getStringCopy(artwork_new->filename);
2247 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2250 if (artwork_new->identifier == NULL)
2251 artwork_new->identifier = getStringCopy(artwork_new->filename);
2253 if (artwork_new->name_sorting == NULL)
2254 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2257 if (node_parent == NULL) /* top level group */
2259 artwork_new->basepath = getStringCopy(base_directory);
2260 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2262 else /* sub level group */
2264 artwork_new->basepath = getStringCopy(node_parent->basepath);
2265 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2269 artwork_new->user_defined =
2270 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2272 artwork_new->user_defined =
2273 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2276 /* (may use ".sort_priority" from "setup_file_hash" above) */
2277 artwork_new->color = ARTWORKCOLOR(artwork_new);
2279 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2281 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2284 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2287 if (artwork_new->name != NULL)
2289 free(artwork_new->name);
2290 artwork_new->name = NULL;
2295 if (artwork_new->identifier != NULL)
2297 free(artwork_new->identifier);
2298 artwork_new->identifier = NULL;
2302 if (strcmp(artwork_new->filename, ".") == 0)
2304 if (artwork_new->user_defined)
2307 setString(&artwork_new->identifier, "private");
2309 artwork_new->identifier = getStringCopy("private");
2311 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2316 setString(&artwork_new->identifier, "classic");
2318 artwork_new->identifier = getStringCopy("classic");
2320 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2323 /* set to new values after changing ".sort_priority" */
2324 artwork_new->color = ARTWORKCOLOR(artwork_new);
2326 setString(&artwork_new->class_desc,
2327 getLevelClassDescription(artwork_new));
2329 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2335 setString(&artwork_new->identifier, artwork_new->filename);
2337 artwork_new->identifier = getStringCopy(artwork_new->filename);
2342 setString(&artwork_new->name, artwork_new->identifier);
2343 setString(&artwork_new->name_sorting, artwork_new->name);
2345 artwork_new->name = getStringCopy(artwork_new->identifier);
2346 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2350 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2352 pushTreeInfo(node_first, artwork_new);
2354 freeSetupFileHash(setup_file_hash);
2356 free(directory_path);
2362 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2363 TreeInfo *node_parent,
2364 char *base_directory, int type)
2367 struct dirent *dir_entry;
2368 boolean valid_entry_found = FALSE;
2370 if ((dir = opendir(base_directory)) == NULL)
2372 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2373 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2377 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2379 struct stat file_status;
2380 char *directory_name = dir_entry->d_name;
2381 char *directory_path = getPath2(base_directory, directory_name);
2383 /* skip entries for current and parent directory */
2384 if (strcmp(directory_name, ".") == 0 ||
2385 strcmp(directory_name, "..") == 0)
2387 free(directory_path);
2391 /* find out if directory entry is itself a directory */
2392 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2393 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2395 free(directory_path);
2399 free(directory_path);
2401 /* check if this directory contains artwork with or without config file */
2402 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2404 directory_name, type);
2409 /* check if this directory directly contains artwork itself */
2410 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2411 base_directory, ".",
2413 if (!valid_entry_found)
2414 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2418 static TreeInfo *getDummyArtworkInfo(int type)
2420 /* this is only needed when there is completely no artwork available */
2421 TreeInfo *artwork_new = newTreeInfo();
2423 setTreeInfoToDefaults(artwork_new, type);
2426 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2427 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2428 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2430 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2431 setString(&artwork_new->name, UNDEFINED_FILENAME);
2432 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2434 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2435 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2436 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2438 if (artwork_new->name != NULL)
2439 free(artwork_new->name);
2441 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2442 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2443 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2449 void LoadArtworkInfo()
2451 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2453 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2454 options.graphics_directory,
2455 TREE_TYPE_GRAPHICS_DIR);
2456 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2457 getUserGraphicsDir(),
2458 TREE_TYPE_GRAPHICS_DIR);
2460 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2461 options.sounds_directory,
2462 TREE_TYPE_SOUNDS_DIR);
2463 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2465 TREE_TYPE_SOUNDS_DIR);
2467 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2468 options.music_directory,
2469 TREE_TYPE_MUSIC_DIR);
2470 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2472 TREE_TYPE_MUSIC_DIR);
2474 if (artwork.gfx_first == NULL)
2475 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2476 if (artwork.snd_first == NULL)
2477 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2478 if (artwork.mus_first == NULL)
2479 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2481 /* before sorting, the first entries will be from the user directory */
2482 artwork.gfx_current =
2483 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2484 if (artwork.gfx_current == NULL)
2485 artwork.gfx_current =
2486 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2487 if (artwork.gfx_current == NULL)
2488 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2490 artwork.snd_current =
2491 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2492 if (artwork.snd_current == NULL)
2493 artwork.snd_current =
2494 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2495 if (artwork.snd_current == NULL)
2496 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2498 artwork.mus_current =
2499 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2500 if (artwork.mus_current == NULL)
2501 artwork.mus_current =
2502 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2503 if (artwork.mus_current == NULL)
2504 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2506 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2507 artwork.snd_current_identifier = artwork.snd_current->identifier;
2508 artwork.mus_current_identifier = artwork.mus_current->identifier;
2511 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2512 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2513 printf("music set == %s\n\n", artwork.mus_current_identifier);
2516 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2517 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2518 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2521 dumpTreeInfo(artwork.gfx_first, 0);
2522 dumpTreeInfo(artwork.snd_first, 0);
2523 dumpTreeInfo(artwork.mus_first, 0);
2527 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2528 LevelDirTree *level_node)
2530 /* recursively check all level directories for artwork sub-directories */
2534 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2535 ARTWORK_DIRECTORY((*artwork_node)->type));
2538 if (!level_node->parent_link)
2539 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2540 level_node->filename, level_node->name);
2543 if (!level_node->parent_link)
2545 TreeInfo *topnode_last = *artwork_node;
2547 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2548 (*artwork_node)->type);
2550 if (topnode_last != *artwork_node)
2552 free((*artwork_node)->identifier);
2553 free((*artwork_node)->name);
2554 free((*artwork_node)->name_sorting);
2556 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2557 (*artwork_node)->name = getStringCopy(level_node->name);
2558 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2560 (*artwork_node)->sort_priority = level_node->sort_priority;
2561 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2567 if (level_node->node_group != NULL)
2568 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2570 level_node = level_node->next;
2574 void LoadLevelArtworkInfo()
2576 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2578 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2579 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2580 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2582 /* needed for reloading level artwork not known at ealier stage */
2584 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2586 artwork.gfx_current =
2587 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2588 if (artwork.gfx_current == NULL)
2589 artwork.gfx_current =
2590 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2591 if (artwork.gfx_current == NULL)
2592 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2595 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2597 artwork.snd_current =
2598 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2599 if (artwork.snd_current == NULL)
2600 artwork.snd_current =
2601 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2602 if (artwork.snd_current == NULL)
2603 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2606 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2608 artwork.mus_current =
2609 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2610 if (artwork.mus_current == NULL)
2611 artwork.mus_current =
2612 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2613 if (artwork.mus_current == NULL)
2614 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2617 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2618 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2619 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2622 dumpTreeInfo(artwork.gfx_first, 0);
2623 dumpTreeInfo(artwork.snd_first, 0);
2624 dumpTreeInfo(artwork.mus_first, 0);
2628 static void SaveUserLevelInfo()
2630 LevelDirTree *level_info;
2635 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2637 if (!(file = fopen(filename, MODE_WRITE)))
2639 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2644 level_info = newTreeInfo();
2646 /* always start with reliable default values */
2647 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2650 setString(&level_info->name, getLoginName());
2651 setString(&level_info->author, getRealName());
2652 level_info->levels = 100;
2653 level_info->first_level = 1;
2654 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2655 level_info->readonly = FALSE;
2656 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2657 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2658 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2660 ldi.name = getStringCopy(getLoginName());
2661 ldi.author = getStringCopy(getRealName());
2663 ldi.first_level = 1;
2664 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2665 ldi.readonly = FALSE;
2666 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2667 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2668 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2671 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2672 getCookie("LEVELINFO")));
2675 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2676 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2677 i != LEVELINFO_TOKEN_NAME_SORTING &&
2678 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2679 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2683 SetFilePermissions(filename, PERMS_PRIVATE);
2685 freeTreeInfo(level_info);
2689 char *getSetupValue(int type, void *value)
2691 static char value_string[MAX_LINE_LEN];
2699 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2703 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2707 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2711 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2715 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2719 sprintf(value_string, "%d", *(int *)value);
2723 strcpy(value_string, *(char **)value);
2727 value_string[0] = '\0';
2731 return value_string;
2734 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2738 static char token_string[MAX_LINE_LEN];
2739 int token_type = token_info[token_nr].type;
2740 void *setup_value = token_info[token_nr].value;
2741 char *token_text = token_info[token_nr].text;
2742 char *value_string = getSetupValue(token_type, setup_value);
2744 /* build complete token string */
2745 sprintf(token_string, "%s%s", prefix, token_text);
2747 /* build setup entry line */
2748 line = getFormattedSetupEntry(token_string, value_string);
2750 if (token_type == TYPE_KEY_X11)
2752 Key key = *(Key *)setup_value;
2753 char *keyname = getKeyNameFromKey(key);
2755 /* add comment, if useful */
2756 if (strcmp(keyname, "(undefined)") != 0 &&
2757 strcmp(keyname, "(unknown)") != 0)
2759 /* add at least one whitespace */
2761 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2765 strcat(line, keyname);
2772 void LoadLevelSetup_LastSeries()
2774 /* ----------------------------------------------------------------------- */
2775 /* ~/.<program>/levelsetup.conf */
2776 /* ----------------------------------------------------------------------- */
2778 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2779 SetupFileHash *level_setup_hash = NULL;
2781 /* always start with reliable default values */
2782 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2784 if ((level_setup_hash = loadSetupFileHash(filename)))
2786 char *last_level_series =
2787 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2789 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2791 if (leveldir_current == NULL)
2792 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2794 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2796 freeSetupFileHash(level_setup_hash);
2799 Error(ERR_WARN, "using default setup values");
2804 void SaveLevelSetup_LastSeries()
2806 /* ----------------------------------------------------------------------- */
2807 /* ~/.<program>/levelsetup.conf */
2808 /* ----------------------------------------------------------------------- */
2810 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2811 char *level_subdir = leveldir_current->filename;
2814 InitUserDataDirectory();
2816 if (!(file = fopen(filename, MODE_WRITE)))
2818 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2823 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2824 getCookie("LEVELSETUP")));
2825 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2830 SetFilePermissions(filename, PERMS_PRIVATE);
2835 static void checkSeriesInfo()
2837 static char *level_directory = NULL;
2839 struct dirent *dir_entry;
2841 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2843 level_directory = getPath2((leveldir_current->user_defined ?
2844 getUserLevelDir(NULL) :
2845 options.level_directory),
2846 leveldir_current->fullpath);
2848 if ((dir = opendir(level_directory)) == NULL)
2850 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2854 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2856 if (strlen(dir_entry->d_name) > 4 &&
2857 dir_entry->d_name[3] == '.' &&
2858 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2860 char levelnum_str[4];
2863 strncpy(levelnum_str, dir_entry->d_name, 3);
2864 levelnum_str[3] = '\0';
2866 levelnum_value = atoi(levelnum_str);
2869 if (levelnum_value < leveldir_current->first_level)
2871 Error(ERR_WARN, "additional level %d found", levelnum_value);
2872 leveldir_current->first_level = levelnum_value;
2874 else if (levelnum_value > leveldir_current->last_level)
2876 Error(ERR_WARN, "additional level %d found", levelnum_value);
2877 leveldir_current->last_level = levelnum_value;
2886 void LoadLevelSetup_SeriesInfo()
2889 SetupFileHash *level_setup_hash = NULL;
2890 char *level_subdir = leveldir_current->filename;
2892 /* always start with reliable default values */
2893 level_nr = leveldir_current->first_level;
2895 checkSeriesInfo(leveldir_current);
2897 /* ----------------------------------------------------------------------- */
2898 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2899 /* ----------------------------------------------------------------------- */
2901 level_subdir = leveldir_current->filename;
2903 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2905 if ((level_setup_hash = loadSetupFileHash(filename)))
2909 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2913 level_nr = atoi(token_value);
2915 if (level_nr < leveldir_current->first_level)
2916 level_nr = leveldir_current->first_level;
2917 if (level_nr > leveldir_current->last_level)
2918 level_nr = leveldir_current->last_level;
2921 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2925 int level_nr = atoi(token_value);
2927 if (level_nr < leveldir_current->first_level)
2928 level_nr = leveldir_current->first_level;
2929 if (level_nr > leveldir_current->last_level + 1)
2930 level_nr = leveldir_current->last_level;
2932 if (leveldir_current->user_defined)
2933 level_nr = leveldir_current->last_level;
2935 leveldir_current->handicap_level = level_nr;
2938 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2940 freeSetupFileHash(level_setup_hash);
2943 Error(ERR_WARN, "using default setup values");
2948 void SaveLevelSetup_SeriesInfo()
2951 char *level_subdir = leveldir_current->filename;
2952 char *level_nr_str = int2str(level_nr, 0);
2953 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2956 /* ----------------------------------------------------------------------- */
2957 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2958 /* ----------------------------------------------------------------------- */
2960 InitLevelSetupDirectory(level_subdir);
2962 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2964 if (!(file = fopen(filename, MODE_WRITE)))
2966 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2971 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2972 getCookie("LEVELSETUP")));
2973 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2975 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2976 handicap_level_str));
2980 SetFilePermissions(filename, PERMS_PRIVATE);