1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
27 #define NUM_LEVELCLASS_DESC 8
29 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
42 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
43 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
44 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
49 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
52 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
53 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
54 IS_LEVELCLASS_BD(n) ? 2 : \
55 IS_LEVELCLASS_EM(n) ? 3 : \
56 IS_LEVELCLASS_SP(n) ? 4 : \
57 IS_LEVELCLASS_DX(n) ? 5 : \
58 IS_LEVELCLASS_CONTRIB(n) ? 6 : \
59 IS_LEVELCLASS_PRIVATE(n) ? 7 : \
62 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
63 IS_ARTWORKCLASS_CONTRIB(n) ? FC_YELLOW : \
64 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
65 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
68 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
69 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
70 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
71 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
74 #define TOKEN_VALUE_POSITION 40
75 #define TOKEN_COMMENT_POSITION 60
77 #define MAX_COOKIE_LEN 256
80 /* ------------------------------------------------------------------------- */
82 /* ------------------------------------------------------------------------- */
84 static char *getLevelClassDescription(TreeInfo *ldi)
86 int position = ldi->sort_priority / 100;
88 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
89 return levelclass_desc[position];
91 return "Unknown Level Class";
94 static char *getUserLevelDir(char *level_subdir)
96 static char *userlevel_dir = NULL;
97 char *data_dir = getUserDataDir();
98 char *userlevel_subdir = LEVELS_DIRECTORY;
100 checked_free(userlevel_dir);
102 if (level_subdir != NULL)
103 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
105 userlevel_dir = getPath2(data_dir, userlevel_subdir);
107 return userlevel_dir;
110 static char *getScoreDir(char *level_subdir)
112 static char *score_dir = NULL;
113 char *data_dir = getCommonDataDir();
114 char *score_subdir = SCORES_DIRECTORY;
116 checked_free(score_dir);
118 if (level_subdir != NULL)
119 score_dir = getPath3(data_dir, score_subdir, level_subdir);
121 score_dir = getPath2(data_dir, score_subdir);
126 static char *getLevelSetupDir(char *level_subdir)
128 static char *levelsetup_dir = NULL;
129 char *data_dir = getUserDataDir();
130 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
132 checked_free(levelsetup_dir);
134 if (level_subdir != NULL)
135 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
137 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
139 return levelsetup_dir;
142 static char *getLevelDirFromTreeInfo(TreeInfo *node)
144 static char *level_dir = NULL;
147 return options.level_directory;
149 checked_free(level_dir);
151 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
152 options.level_directory), node->fullpath);
157 static char *getCurrentLevelDir()
159 return getLevelDirFromTreeInfo(leveldir_current);
162 static char *getTapeDir(char *level_subdir)
164 static char *tape_dir = NULL;
165 char *data_dir = getUserDataDir();
166 char *tape_subdir = TAPES_DIRECTORY;
168 checked_free(tape_dir);
170 if (level_subdir != NULL)
171 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
173 tape_dir = getPath2(data_dir, tape_subdir);
178 static char *getSolutionTapeDir()
180 static char *tape_dir = NULL;
181 char *data_dir = getCurrentLevelDir();
182 char *tape_subdir = TAPES_DIRECTORY;
184 checked_free(tape_dir);
186 tape_dir = getPath2(data_dir, tape_subdir);
191 static char *getDefaultGraphicsDir(char *graphics_subdir)
193 static char *graphics_dir = NULL;
195 if (graphics_subdir == NULL)
196 return options.graphics_directory;
198 checked_free(graphics_dir);
200 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
205 static char *getDefaultSoundsDir(char *sounds_subdir)
207 static char *sounds_dir = NULL;
209 if (sounds_subdir == NULL)
210 return options.sounds_directory;
212 checked_free(sounds_dir);
214 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
219 static char *getDefaultMusicDir(char *music_subdir)
221 static char *music_dir = NULL;
223 if (music_subdir == NULL)
224 return options.music_directory;
226 checked_free(music_dir);
228 music_dir = getPath2(options.music_directory, music_subdir);
233 static char *getDefaultArtworkSet(int type)
235 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
236 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
237 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
240 static char *getDefaultArtworkDir(int type)
242 return (type == TREE_TYPE_GRAPHICS_DIR ?
243 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
244 type == TREE_TYPE_SOUNDS_DIR ?
245 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
246 type == TREE_TYPE_MUSIC_DIR ?
247 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
250 static char *getUserGraphicsDir()
252 static char *usergraphics_dir = NULL;
254 if (usergraphics_dir == NULL)
255 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
257 return usergraphics_dir;
260 static char *getUserSoundsDir()
262 static char *usersounds_dir = NULL;
264 if (usersounds_dir == NULL)
265 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
267 return usersounds_dir;
270 static char *getUserMusicDir()
272 static char *usermusic_dir = NULL;
274 if (usermusic_dir == NULL)
275 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
277 return usermusic_dir;
280 static char *getSetupArtworkDir(TreeInfo *ti)
282 static char *artwork_dir = NULL;
284 checked_free(artwork_dir);
286 artwork_dir = getPath2(ti->basepath, ti->fullpath);
291 char *setLevelArtworkDir(TreeInfo *ti)
293 char **artwork_path_ptr, **artwork_set_ptr;
294 TreeInfo *level_artwork;
296 if (ti == NULL || leveldir_current == NULL)
299 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
300 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
302 checked_free(*artwork_path_ptr);
304 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
305 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
308 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
309 normally result in using the artwork configured in the setup menu. But
310 if an artwork subdirectory exists (which might contain custom artwork
311 or an artwork configuration file), this level artwork must be treated
312 as relative to the default "classic" artwork, not to the artwork that
313 is currently configured in the setup menu. */
315 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
317 checked_free(*artwork_set_ptr);
321 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
322 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
326 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
327 *artwork_set_ptr = NULL;
333 return *artwork_set_ptr;
336 inline static char *getLevelArtworkSet(int type)
338 if (leveldir_current == NULL)
341 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
344 inline static char *getLevelArtworkDir(int type)
346 if (leveldir_current == NULL)
347 return UNDEFINED_FILENAME;
349 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
352 char *getLevelFilename(int nr)
354 static char *filename = NULL;
355 char basename[MAX_FILENAME_LEN];
357 checked_free(filename);
360 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
362 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
364 filename = getPath2(getCurrentLevelDir(), basename);
369 char *getTapeFilename(int nr)
371 static char *filename = NULL;
372 char basename[MAX_FILENAME_LEN];
374 checked_free(filename);
376 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
377 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
382 char *getSolutionTapeFilename(int nr)
384 static char *filename = NULL;
385 char basename[MAX_FILENAME_LEN];
387 checked_free(filename);
389 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
390 filename = getPath2(getSolutionTapeDir(), basename);
395 char *getScoreFilename(int nr)
397 static char *filename = NULL;
398 char basename[MAX_FILENAME_LEN];
400 checked_free(filename);
402 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
403 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
408 char *getSetupFilename()
410 static char *filename = NULL;
412 checked_free(filename);
414 filename = getPath2(getSetupDir(), SETUP_FILENAME);
419 char *getEditorSetupFilename()
421 static char *filename = NULL;
423 checked_free(filename);
425 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
430 char *getHelpAnimFilename()
432 static char *filename = NULL;
434 checked_free(filename);
436 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
441 char *getHelpTextFilename()
443 static char *filename = NULL;
445 checked_free(filename);
447 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
452 char *getLevelSetInfoFilename()
454 static char *filename = NULL;
469 for (i = 0; basenames[i] != NULL; i++)
471 checked_free(filename);
473 filename = getPath2(getCurrentLevelDir(), basenames[i]);
474 if (fileExists(filename))
481 static char *getCorrectedArtworkBasename(char *basename)
483 char *basename_corrected = basename;
485 #if defined(PLATFORM_MSDOS)
486 if (program.filename_prefix != NULL)
488 int prefix_len = strlen(program.filename_prefix);
490 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
491 basename_corrected = &basename[prefix_len];
493 /* if corrected filename is still longer than standard MS-DOS filename
494 size (8 characters + 1 dot + 3 characters file extension), shorten
495 filename by writing file extension after 8th basename character */
496 if (strlen(basename_corrected) > 8 + 1 + 3)
498 static char *msdos_filename = NULL;
500 checked_free(msdos_filename);
502 msdos_filename = getStringCopy(basename_corrected);
503 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
505 basename_corrected = msdos_filename;
510 return basename_corrected;
513 char *getCustomImageFilename(char *basename)
515 static char *filename = NULL;
516 boolean skip_setup_artwork = FALSE;
518 checked_free(filename);
520 basename = getCorrectedArtworkBasename(basename);
522 if (!setup.override_level_graphics)
524 /* 1st try: look for special artwork in current level series directory */
525 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
526 if (fileExists(filename))
531 /* check if there is special artwork configured in level series config */
532 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
534 /* 2nd try: look for special artwork configured in level series config */
535 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
536 if (fileExists(filename))
541 /* take missing artwork configured in level set config from default */
542 skip_setup_artwork = TRUE;
546 if (!skip_setup_artwork)
548 /* 3rd try: look for special artwork in configured artwork directory */
549 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
550 if (fileExists(filename))
556 /* 4th try: look for default artwork in new default artwork directory */
557 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
558 if (fileExists(filename))
563 /* 5th try: look for default artwork in old default artwork directory */
564 filename = getPath2(options.graphics_directory, basename);
565 if (fileExists(filename))
568 return NULL; /* cannot find specified artwork file anywhere */
571 char *getCustomSoundFilename(char *basename)
573 static char *filename = NULL;
574 boolean skip_setup_artwork = FALSE;
576 checked_free(filename);
578 basename = getCorrectedArtworkBasename(basename);
580 if (!setup.override_level_sounds)
582 /* 1st try: look for special artwork in current level series directory */
583 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
584 if (fileExists(filename))
589 /* check if there is special artwork configured in level series config */
590 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
592 /* 2nd try: look for special artwork configured in level series config */
593 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
594 if (fileExists(filename))
599 /* take missing artwork configured in level set config from default */
600 skip_setup_artwork = TRUE;
604 if (!skip_setup_artwork)
606 /* 3rd try: look for special artwork in configured artwork directory */
607 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
608 if (fileExists(filename))
614 /* 4th try: look for default artwork in new default artwork directory */
615 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
616 if (fileExists(filename))
621 /* 5th try: look for default artwork in old default artwork directory */
622 filename = getPath2(options.sounds_directory, basename);
623 if (fileExists(filename))
626 return NULL; /* cannot find specified artwork file anywhere */
629 char *getCustomMusicFilename(char *basename)
631 static char *filename = NULL;
632 boolean skip_setup_artwork = FALSE;
634 checked_free(filename);
636 basename = getCorrectedArtworkBasename(basename);
638 if (!setup.override_level_music)
640 /* 1st try: look for special artwork in current level series directory */
641 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
642 if (fileExists(filename))
647 /* check if there is special artwork configured in level series config */
648 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
650 /* 2nd try: look for special artwork configured in level series config */
651 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
652 if (fileExists(filename))
657 /* take missing artwork configured in level set config from default */
658 skip_setup_artwork = TRUE;
662 if (!skip_setup_artwork)
664 /* 3rd try: look for special artwork in configured artwork directory */
665 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
666 if (fileExists(filename))
672 /* 4th try: look for default artwork in new default artwork directory */
673 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
674 if (fileExists(filename))
679 /* 5th try: look for default artwork in old default artwork directory */
680 filename = getPath2(options.music_directory, basename);
681 if (fileExists(filename))
684 return NULL; /* cannot find specified artwork file anywhere */
687 char *getCustomArtworkFilename(char *basename, int type)
689 if (type == ARTWORK_TYPE_GRAPHICS)
690 return getCustomImageFilename(basename);
691 else if (type == ARTWORK_TYPE_SOUNDS)
692 return getCustomSoundFilename(basename);
693 else if (type == ARTWORK_TYPE_MUSIC)
694 return getCustomMusicFilename(basename);
696 return UNDEFINED_FILENAME;
699 char *getCustomArtworkConfigFilename(int type)
701 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
704 char *getCustomArtworkLevelConfigFilename(int type)
706 static char *filename = NULL;
708 checked_free(filename);
710 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
715 char *getCustomMusicDirectory(void)
717 static char *directory = NULL;
718 boolean skip_setup_artwork = FALSE;
720 checked_free(directory);
722 if (!setup.override_level_music)
724 /* 1st try: look for special artwork in current level series directory */
725 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
726 if (fileExists(directory))
731 /* check if there is special artwork configured in level series config */
732 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
734 /* 2nd try: look for special artwork configured in level series config */
735 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
736 if (fileExists(directory))
741 /* take missing artwork configured in level set config from default */
742 skip_setup_artwork = TRUE;
746 if (!skip_setup_artwork)
748 /* 3rd try: look for special artwork in configured artwork directory */
749 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
750 if (fileExists(directory))
756 /* 4th try: look for default artwork in new default artwork directory */
757 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
758 if (fileExists(directory))
763 /* 5th try: look for default artwork in old default artwork directory */
764 directory = getStringCopy(options.music_directory);
765 if (fileExists(directory))
768 return NULL; /* cannot find specified artwork file anywhere */
771 void InitTapeDirectory(char *level_subdir)
773 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
774 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
775 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
778 void InitScoreDirectory(char *level_subdir)
780 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
781 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
782 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
785 static void SaveUserLevelInfo();
787 void InitUserLevelDirectory(char *level_subdir)
789 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
791 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
792 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
793 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
799 void InitLevelSetupDirectory(char *level_subdir)
801 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
802 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
803 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
807 /* ------------------------------------------------------------------------- */
808 /* some functions to handle lists of level directories */
809 /* ------------------------------------------------------------------------- */
811 TreeInfo *newTreeInfo()
813 return checked_calloc(sizeof(TreeInfo));
816 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
818 node_new->next = *node_first;
819 *node_first = node_new;
822 int numTreeInfo(TreeInfo *node)
835 boolean validLevelSeries(TreeInfo *node)
837 return (node != NULL && !node->node_group && !node->parent_link);
840 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
845 if (node->node_group) /* enter level group (step down into tree) */
846 return getFirstValidTreeInfoEntry(node->node_group);
847 else if (node->parent_link) /* skip start entry of level group */
849 if (node->next) /* get first real level series entry */
850 return getFirstValidTreeInfoEntry(node->next);
851 else /* leave empty level group and go on */
852 return getFirstValidTreeInfoEntry(node->node_parent->next);
854 else /* this seems to be a regular level series */
858 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
863 if (node->node_parent == NULL) /* top level group */
864 return *node->node_top;
865 else /* sub level group */
866 return node->node_parent->node_group;
869 int numTreeInfoInGroup(TreeInfo *node)
871 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
874 int posTreeInfo(TreeInfo *node)
876 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
881 if (node_cmp == node)
885 node_cmp = node_cmp->next;
891 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
893 TreeInfo *node_default = node;
908 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
910 if (identifier == NULL)
915 if (node->node_group)
917 TreeInfo *node_group;
919 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
924 else if (!node->parent_link)
926 if (strcmp(identifier, node->identifier) == 0)
936 void dumpTreeInfo(TreeInfo *node, int depth)
940 printf("Dumping TreeInfo:\n");
944 for (i = 0; i < (depth + 1) * 3; i++)
948 printf("filename == '%s' ['%s', '%s'] [%d])\n",
949 node->filename, node->fullpath, node->basepath, node->user_defined);
951 printf("filename == '%s' (%s) [%s] (%d)\n",
952 node->filename, node->name, node->identifier, node->sort_priority);
955 if (node->node_group != NULL)
956 dumpTreeInfo(node->node_group, depth + 1);
962 void sortTreeInfo(TreeInfo **node_first,
963 int (*compare_function)(const void *, const void *))
965 int num_nodes = numTreeInfo(*node_first);
966 TreeInfo **sort_array;
967 TreeInfo *node = *node_first;
973 /* allocate array for sorting structure pointers */
974 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
976 /* writing structure pointers to sorting array */
977 while (i < num_nodes && node) /* double boundary check... */
979 sort_array[i] = node;
985 /* sorting the structure pointers in the sorting array */
986 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
989 /* update the linkage of list elements with the sorted node array */
990 for (i = 0; i < num_nodes - 1; i++)
991 sort_array[i]->next = sort_array[i + 1];
992 sort_array[num_nodes - 1]->next = NULL;
994 /* update the linkage of the main list anchor pointer */
995 *node_first = sort_array[0];
999 /* now recursively sort the level group structures */
1003 if (node->node_group != NULL)
1004 sortTreeInfo(&node->node_group, compare_function);
1011 /* ========================================================================= */
1012 /* some stuff from "files.c" */
1013 /* ========================================================================= */
1015 #if defined(PLATFORM_WIN32)
1017 #define S_IRGRP S_IRUSR
1020 #define S_IROTH S_IRUSR
1023 #define S_IWGRP S_IWUSR
1026 #define S_IWOTH S_IWUSR
1029 #define S_IXGRP S_IXUSR
1032 #define S_IXOTH S_IXUSR
1035 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1040 #endif /* PLATFORM_WIN32 */
1042 /* file permissions for newly written files */
1043 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1044 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1045 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1047 #define MODE_W_PRIVATE (S_IWUSR)
1048 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1049 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1051 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1052 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1054 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1055 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1057 char *getUserDataDir(void)
1059 static char *userdata_dir = NULL;
1061 if (userdata_dir == NULL)
1062 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1064 return userdata_dir;
1067 char *getCommonDataDir(void)
1069 static char *common_data_dir = NULL;
1071 #if defined(PLATFORM_WIN32)
1072 if (common_data_dir == NULL)
1074 char *dir = checked_malloc(MAX_PATH + 1);
1076 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1077 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1078 common_data_dir = getPath2(dir, program.userdata_directory);
1080 common_data_dir = options.rw_base_directory;
1083 if (common_data_dir == NULL)
1084 common_data_dir = options.rw_base_directory;
1087 return common_data_dir;
1092 return getUserDataDir();
1095 static mode_t posix_umask(mode_t mask)
1097 #if defined(PLATFORM_UNIX)
1104 static int posix_mkdir(const char *pathname, mode_t mode)
1106 #if defined(PLATFORM_WIN32)
1107 return mkdir(pathname);
1109 return mkdir(pathname, mode);
1113 void createDirectory(char *dir, char *text, int permission_class)
1115 /* leave "other" permissions in umask untouched, but ensure group parts
1116 of USERDATA_DIR_MODE are not masked */
1117 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1118 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1119 mode_t normal_umask = posix_umask(0);
1120 mode_t group_umask = ~(dir_mode & S_IRWXG);
1121 posix_umask(normal_umask & group_umask);
1123 if (access(dir, F_OK) != 0)
1124 if (posix_mkdir(dir, dir_mode) != 0)
1125 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1127 posix_umask(normal_umask); /* reset normal umask */
1130 void InitUserDataDirectory()
1132 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1135 void SetFilePermissions(char *filename, int permission_class)
1137 chmod(filename, (permission_class == PERMS_PRIVATE ?
1138 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1141 char *getCookie(char *file_type)
1143 static char cookie[MAX_COOKIE_LEN + 1];
1145 if (strlen(program.cookie_prefix) + 1 +
1146 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1147 return "[COOKIE ERROR]"; /* should never happen */
1149 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1150 program.cookie_prefix, file_type,
1151 program.version_major, program.version_minor);
1156 int getFileVersionFromCookieString(const char *cookie)
1158 const char *ptr_cookie1, *ptr_cookie2;
1159 const char *pattern1 = "_FILE_VERSION_";
1160 const char *pattern2 = "?.?";
1161 const int len_cookie = strlen(cookie);
1162 const int len_pattern1 = strlen(pattern1);
1163 const int len_pattern2 = strlen(pattern2);
1164 const int len_pattern = len_pattern1 + len_pattern2;
1165 int version_major, version_minor;
1167 if (len_cookie <= len_pattern)
1170 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1171 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1173 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1176 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1177 ptr_cookie2[1] != '.' ||
1178 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1181 version_major = ptr_cookie2[0] - '0';
1182 version_minor = ptr_cookie2[2] - '0';
1184 return VERSION_IDENT(version_major, version_minor, 0, 0);
1187 boolean checkCookieString(const char *cookie, const char *template)
1189 const char *pattern = "_FILE_VERSION_?.?";
1190 const int len_cookie = strlen(cookie);
1191 const int len_template = strlen(template);
1192 const int len_pattern = strlen(pattern);
1194 if (len_cookie != len_template)
1197 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1203 /* ------------------------------------------------------------------------- */
1204 /* setup file list and hash handling functions */
1205 /* ------------------------------------------------------------------------- */
1207 char *getFormattedSetupEntry(char *token, char *value)
1210 static char entry[MAX_LINE_LEN];
1212 /* if value is an empty string, just return token without value */
1216 /* start with the token and some spaces to format output line */
1217 sprintf(entry, "%s:", token);
1218 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1221 /* continue with the token's value */
1222 strcat(entry, value);
1227 SetupFileList *newSetupFileList(char *token, char *value)
1229 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1231 new->token = getStringCopy(token);
1232 new->value = getStringCopy(value);
1239 void freeSetupFileList(SetupFileList *list)
1244 checked_free(list->token);
1245 checked_free(list->value);
1248 freeSetupFileList(list->next);
1253 char *getListEntry(SetupFileList *list, char *token)
1258 if (strcmp(list->token, token) == 0)
1261 return getListEntry(list->next, token);
1264 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1269 if (strcmp(list->token, token) == 0)
1271 checked_free(list->value);
1273 list->value = getStringCopy(value);
1277 else if (list->next == NULL)
1278 return (list->next = newSetupFileList(token, value));
1280 return setListEntry(list->next, token, value);
1283 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1288 if (list->next == NULL)
1289 return (list->next = newSetupFileList(token, value));
1291 return addListEntry(list->next, token, value);
1295 static void printSetupFileList(SetupFileList *list)
1300 printf("token: '%s'\n", list->token);
1301 printf("value: '%s'\n", list->value);
1303 printSetupFileList(list->next);
1308 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1309 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1310 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1311 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1313 #define insert_hash_entry hashtable_insert
1314 #define search_hash_entry hashtable_search
1315 #define change_hash_entry hashtable_change
1316 #define remove_hash_entry hashtable_remove
1319 static unsigned int get_hash_from_key(void *key)
1324 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1325 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1326 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1327 it works better than many other constants, prime or not) has never been
1328 adequately explained.
1330 If you just want to have a good hash function, and cannot wait, djb2
1331 is one of the best string hash functions i know. It has excellent
1332 distribution and speed on many different sets of keys and table sizes.
1333 You are not likely to do better with one of the "well known" functions
1334 such as PJW, K&R, etc.
1336 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1339 char *str = (char *)key;
1340 unsigned int hash = 5381;
1343 while ((c = *str++))
1344 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1349 static int keys_are_equal(void *key1, void *key2)
1351 return (strcmp((char *)key1, (char *)key2) == 0);
1354 SetupFileHash *newSetupFileHash()
1356 SetupFileHash *new_hash =
1357 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1359 if (new_hash == NULL)
1360 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1365 void freeSetupFileHash(SetupFileHash *hash)
1370 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1373 char *getHashEntry(SetupFileHash *hash, char *token)
1378 return search_hash_entry(hash, token);
1381 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1388 value_copy = getStringCopy(value);
1390 /* change value; if it does not exist, insert it as new */
1391 if (!change_hash_entry(hash, token, value_copy))
1392 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1393 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1396 char *removeHashEntry(SetupFileHash *hash, char *token)
1401 return remove_hash_entry(hash, token);
1406 static void printSetupFileHash(SetupFileHash *hash)
1408 BEGIN_HASH_ITERATION(hash, itr)
1410 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1411 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1413 END_HASH_ITERATION(hash, itr)
1418 static void *loadSetupFileData(char *filename, boolean use_hash)
1420 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1421 char *token, *value, *line_ptr;
1422 void *setup_file_data, *insert_ptr = NULL;
1423 boolean read_continued_line = FALSE;
1427 setup_file_data = newSetupFileHash();
1429 insert_ptr = setup_file_data = newSetupFileList("", "");
1431 if (!(file = fopen(filename, MODE_READ)))
1433 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1439 /* read next line of input file */
1440 if (!fgets(line, MAX_LINE_LEN, file))
1443 /* cut trailing newline or carriage return */
1444 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1445 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1448 if (read_continued_line)
1450 /* cut leading whitespaces from input line */
1451 for (line_ptr = line; *line_ptr; line_ptr++)
1452 if (*line_ptr != ' ' && *line_ptr != '\t')
1455 /* append new line to existing line, if there is enough space */
1456 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1457 strcat(previous_line, line_ptr);
1459 strcpy(line, previous_line); /* copy storage buffer to line */
1461 read_continued_line = FALSE;
1464 /* if the last character is '\', continue at next line */
1465 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1467 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1468 strcpy(previous_line, line); /* copy line to storage buffer */
1470 read_continued_line = TRUE;
1475 /* cut trailing comment from input line */
1476 for (line_ptr = line; *line_ptr; line_ptr++)
1478 if (*line_ptr == '#')
1485 /* cut trailing whitespaces from input line */
1486 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1487 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1490 /* ignore empty lines */
1494 /* cut leading whitespaces from token */
1495 for (token = line; *token; token++)
1496 if (*token != ' ' && *token != '\t')
1499 /* start with empty value as reliable default */
1502 /* find end of token to determine start of value */
1503 for (line_ptr = token; *line_ptr; line_ptr++)
1505 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1507 *line_ptr = '\0'; /* terminate token string */
1508 value = line_ptr + 1; /* set beginning of value */
1514 /* cut leading whitespaces from value */
1515 for (; *value; value++)
1516 if (*value != ' ' && *value != '\t')
1521 value = "true"; /* treat tokens without value as "true" */
1527 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1529 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1537 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1538 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1542 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1543 SetupFileList *first_valid_list_entry = setup_file_list->next;
1545 /* free empty list header */
1546 setup_file_list->next = NULL;
1547 freeSetupFileList(setup_file_list);
1548 setup_file_data = first_valid_list_entry;
1550 if (first_valid_list_entry == NULL)
1551 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1554 return setup_file_data;
1557 SetupFileList *loadSetupFileList(char *filename)
1559 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1562 SetupFileHash *loadSetupFileHash(char *filename)
1564 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1567 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1570 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1573 Error(ERR_WARN, "configuration file has no file identifier");
1574 else if (!checkCookieString(value, identifier))
1575 Error(ERR_WARN, "configuration file has wrong file identifier");
1579 /* ========================================================================= */
1580 /* setup file stuff */
1581 /* ========================================================================= */
1583 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1584 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1585 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1587 /* level directory info */
1588 #define LEVELINFO_TOKEN_IDENTIFIER 0
1589 #define LEVELINFO_TOKEN_NAME 1
1590 #define LEVELINFO_TOKEN_NAME_SORTING 2
1591 #define LEVELINFO_TOKEN_AUTHOR 3
1592 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1593 #define LEVELINFO_TOKEN_LEVELS 5
1594 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1595 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1596 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1597 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1598 #define LEVELINFO_TOKEN_READONLY 10
1599 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1600 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1601 #define LEVELINFO_TOKEN_MUSIC_SET 13
1603 #define NUM_LEVELINFO_TOKENS 14
1605 static LevelDirTree ldi;
1607 static struct TokenInfo levelinfo_tokens[] =
1609 /* level directory info */
1610 { TYPE_STRING, &ldi.identifier, "identifier" },
1611 { TYPE_STRING, &ldi.name, "name" },
1612 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1613 { TYPE_STRING, &ldi.author, "author" },
1614 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1615 { TYPE_INTEGER, &ldi.levels, "levels" },
1616 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1617 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1618 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1619 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1620 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1621 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1622 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1623 { TYPE_STRING, &ldi.music_set, "music_set" }
1626 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1630 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1631 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1632 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1633 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1636 ldi->node_parent = NULL;
1637 ldi->node_group = NULL;
1641 ldi->cl_cursor = -1;
1643 ldi->filename = NULL;
1644 ldi->fullpath = NULL;
1645 ldi->basepath = NULL;
1646 ldi->identifier = NULL;
1647 ldi->name = getStringCopy(ANONYMOUS_NAME);
1648 ldi->name_sorting = NULL;
1649 ldi->author = getStringCopy(ANONYMOUS_NAME);
1651 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1652 ldi->latest_engine = FALSE; /* default: get from level */
1653 ldi->parent_link = FALSE;
1654 ldi->user_defined = FALSE;
1656 ldi->class_desc = NULL;
1658 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1660 ldi->imported_from = NULL;
1662 ldi->graphics_set = NULL;
1663 ldi->sounds_set = NULL;
1664 ldi->music_set = NULL;
1665 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1666 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1667 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1670 ldi->first_level = 0;
1671 ldi->last_level = 0;
1672 ldi->level_group = FALSE;
1673 ldi->handicap_level = 0;
1674 ldi->readonly = TRUE;
1678 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1682 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1684 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1690 /* copy all values from the parent structure */
1692 ldi->type = parent->type;
1694 ldi->node_top = parent->node_top;
1695 ldi->node_parent = parent;
1696 ldi->node_group = NULL;
1700 ldi->cl_cursor = -1;
1702 ldi->filename = NULL;
1703 ldi->fullpath = NULL;
1704 ldi->basepath = NULL;
1705 ldi->identifier = NULL;
1706 ldi->name = getStringCopy(ANONYMOUS_NAME);
1707 ldi->name_sorting = NULL;
1708 ldi->author = getStringCopy(parent->author);
1710 ldi->sort_priority = parent->sort_priority;
1711 ldi->latest_engine = parent->latest_engine;
1712 ldi->parent_link = FALSE;
1713 ldi->user_defined = parent->user_defined;
1714 ldi->color = parent->color;
1715 ldi->class_desc = getStringCopy(parent->class_desc);
1717 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1719 ldi->imported_from = getStringCopy(parent->imported_from);
1721 ldi->graphics_set = NULL;
1722 ldi->sounds_set = NULL;
1723 ldi->music_set = NULL;
1724 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1725 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1726 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1729 ldi->first_level = 0;
1730 ldi->last_level = 0;
1731 ldi->level_group = FALSE;
1732 ldi->handicap_level = 0;
1733 ldi->readonly = TRUE;
1739 /* first copy all values from the parent structure ... */
1742 /* ... then set all fields to default that cannot be inherited from parent.
1743 This is especially important for all those fields that can be set from
1744 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1745 calls 'free()' for all already set token values which requires that no
1746 other structure's pointer may point to them!
1749 ldi->filename = NULL;
1750 ldi->fullpath = NULL;
1751 ldi->basepath = NULL;
1752 ldi->identifier = NULL;
1753 ldi->name = getStringCopy(ANONYMOUS_NAME);
1754 ldi->name_sorting = NULL;
1755 ldi->author = getStringCopy(parent->author);
1757 ldi->imported_from = getStringCopy(parent->imported_from);
1758 ldi->class_desc = getStringCopy(parent->class_desc);
1760 ldi->graphics_set = NULL;
1761 ldi->sounds_set = NULL;
1762 ldi->music_set = NULL;
1763 ldi->graphics_path = NULL;
1764 ldi->sounds_path = NULL;
1765 ldi->music_path = NULL;
1767 ldi->level_group = FALSE;
1768 ldi->parent_link = FALSE;
1770 ldi->node_top = parent->node_top;
1771 ldi->node_parent = parent;
1772 ldi->node_group = NULL;
1778 static void freeTreeInfo(TreeInfo *ldi)
1780 checked_free(ldi->filename);
1781 checked_free(ldi->fullpath);
1782 checked_free(ldi->basepath);
1783 checked_free(ldi->identifier);
1785 checked_free(ldi->name);
1786 checked_free(ldi->name_sorting);
1787 checked_free(ldi->author);
1789 checked_free(ldi->class_desc);
1791 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1793 checked_free(ldi->graphics_set);
1794 checked_free(ldi->sounds_set);
1795 checked_free(ldi->music_set);
1797 checked_free(ldi->graphics_path);
1798 checked_free(ldi->sounds_path);
1799 checked_free(ldi->music_path);
1803 void setSetupInfo(struct TokenInfo *token_info,
1804 int token_nr, char *token_value)
1806 int token_type = token_info[token_nr].type;
1807 void *setup_value = token_info[token_nr].value;
1809 if (token_value == NULL)
1812 /* set setup field to corresponding token value */
1817 *(boolean *)setup_value = get_boolean_from_string(token_value);
1821 *(Key *)setup_value = getKeyFromKeyName(token_value);
1825 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1829 *(int *)setup_value = get_integer_from_string(token_value);
1833 checked_free(*(char **)setup_value);
1834 *(char **)setup_value = getStringCopy(token_value);
1842 static int compareTreeInfoEntries(const void *object1, const void *object2)
1844 const TreeInfo *entry1 = *((TreeInfo **)object1);
1845 const TreeInfo *entry2 = *((TreeInfo **)object2);
1846 int class_sorting1, class_sorting2;
1849 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1851 class_sorting1 = LEVELSORTING(entry1);
1852 class_sorting2 = LEVELSORTING(entry2);
1856 class_sorting1 = ARTWORKSORTING(entry1);
1857 class_sorting2 = ARTWORKSORTING(entry2);
1860 if (entry1->parent_link || entry2->parent_link)
1861 compare_result = (entry1->parent_link ? -1 : +1);
1862 else if (entry1->sort_priority == entry2->sort_priority)
1864 char *name1 = getStringToLower(entry1->name_sorting);
1865 char *name2 = getStringToLower(entry2->name_sorting);
1867 compare_result = strcmp(name1, name2);
1872 else if (class_sorting1 == class_sorting2)
1873 compare_result = entry1->sort_priority - entry2->sort_priority;
1875 compare_result = class_sorting1 - class_sorting2;
1877 return compare_result;
1880 static void createParentTreeInfoNode(TreeInfo *node_parent)
1884 if (node_parent == NULL)
1887 ti_new = newTreeInfo();
1888 setTreeInfoToDefaults(ti_new, node_parent->type);
1890 ti_new->node_parent = node_parent;
1891 ti_new->parent_link = TRUE;
1894 setString(&ti_new->identifier, node_parent->identifier);
1895 setString(&ti_new->name, ".. (parent directory)");
1896 setString(&ti_new->name_sorting, ti_new->name);
1898 setString(&ti_new->filename, "..");
1899 setString(&ti_new->fullpath, node_parent->fullpath);
1901 ti_new->sort_priority = node_parent->sort_priority;
1902 ti_new->latest_engine = node_parent->latest_engine;
1904 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1906 ti_new->identifier = getStringCopy(node_parent->identifier);
1907 ti_new->name = ".. (parent directory)";
1908 ti_new->name_sorting = getStringCopy(ti_new->name);
1910 ti_new->filename = "..";
1911 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1913 ti_new->sort_priority = node_parent->sort_priority;
1914 ti_new->latest_engine = node_parent->latest_engine;
1916 ti_new->class_desc = getLevelClassDescription(ti_new);
1919 pushTreeInfo(&node_parent->node_group, ti_new);
1922 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1923 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1925 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1926 TreeInfo *node_parent,
1927 char *level_directory,
1928 char *directory_name)
1930 char *directory_path = getPath2(level_directory, directory_name);
1931 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1932 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1933 LevelDirTree *leveldir_new = NULL;
1936 if (setup_file_hash == NULL)
1938 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1940 free(directory_path);
1946 leveldir_new = newTreeInfo();
1949 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1951 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1953 leveldir_new->filename = getStringCopy(directory_name);
1955 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1957 /* set all structure fields according to the token/value pairs */
1958 ldi = *leveldir_new;
1959 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1960 setSetupInfo(levelinfo_tokens, i,
1961 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1962 *leveldir_new = ldi;
1965 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1966 setString(&leveldir_new->name, leveldir_new->filename);
1968 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1970 free(leveldir_new->name);
1971 leveldir_new->name = getStringCopy(leveldir_new->filename);
1975 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1977 if (leveldir_new->identifier == NULL)
1978 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1980 if (leveldir_new->name_sorting == NULL)
1981 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1983 if (node_parent == NULL) /* top level group */
1985 leveldir_new->basepath = getStringCopy(level_directory);
1986 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1988 else /* sub level group */
1990 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1991 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1994 if (leveldir_new->levels < 1)
1995 leveldir_new->levels = 1;
1997 leveldir_new->last_level =
1998 leveldir_new->first_level + leveldir_new->levels - 1;
2001 leveldir_new->user_defined =
2002 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2004 leveldir_new->user_defined =
2005 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2008 leveldir_new->color = LEVELCOLOR(leveldir_new);
2010 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2012 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2015 leveldir_new->handicap_level = /* set handicap to default value */
2016 (leveldir_new->user_defined ?
2017 leveldir_new->last_level :
2018 leveldir_new->first_level);
2020 pushTreeInfo(node_first, leveldir_new);
2022 freeSetupFileHash(setup_file_hash);
2024 if (leveldir_new->level_group)
2026 /* create node to link back to current level directory */
2027 createParentTreeInfoNode(leveldir_new);
2029 /* step into sub-directory and look for more level series */
2030 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2031 leveldir_new, directory_path);
2034 free(directory_path);
2040 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2041 TreeInfo *node_parent,
2042 char *level_directory)
2045 struct dirent *dir_entry;
2046 boolean valid_entry_found = FALSE;
2048 if ((dir = opendir(level_directory)) == NULL)
2050 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2054 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2056 struct stat file_status;
2057 char *directory_name = dir_entry->d_name;
2058 char *directory_path = getPath2(level_directory, directory_name);
2060 /* skip entries for current and parent directory */
2061 if (strcmp(directory_name, ".") == 0 ||
2062 strcmp(directory_name, "..") == 0)
2064 free(directory_path);
2068 /* find out if directory entry is itself a directory */
2069 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2070 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2072 free(directory_path);
2076 free(directory_path);
2078 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2079 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2080 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2083 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2090 if (!valid_entry_found)
2092 /* check if this directory directly contains a file "levelinfo.conf" */
2093 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2094 level_directory, ".");
2097 if (!valid_entry_found)
2098 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2102 void LoadLevelInfo()
2104 InitUserLevelDirectory(getLoginName());
2106 DrawInitText("Loading level series:", 120, FC_GREEN);
2108 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2109 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2111 /* before sorting, the first entries will be from the user directory */
2112 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2114 if (leveldir_first == NULL)
2115 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2117 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2120 dumpTreeInfo(leveldir_first, 0);
2124 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2125 TreeInfo *node_parent,
2126 char *base_directory,
2127 char *directory_name, int type)
2129 char *directory_path = getPath2(base_directory, directory_name);
2130 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2131 SetupFileHash *setup_file_hash = NULL;
2132 TreeInfo *artwork_new = NULL;
2135 if (access(filename, F_OK) == 0) /* file exists */
2136 setup_file_hash = loadSetupFileHash(filename);
2138 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2141 struct dirent *dir_entry;
2142 boolean valid_file_found = FALSE;
2144 if ((dir = opendir(directory_path)) != NULL)
2146 while ((dir_entry = readdir(dir)) != NULL)
2148 char *entry_name = dir_entry->d_name;
2150 if (FileIsArtworkType(entry_name, type))
2152 valid_file_found = TRUE;
2160 if (!valid_file_found)
2162 if (strcmp(directory_name, ".") != 0)
2163 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2165 free(directory_path);
2172 artwork_new = newTreeInfo();
2175 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2177 setTreeInfoToDefaults(artwork_new, type);
2179 artwork_new->filename = getStringCopy(directory_name);
2181 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2184 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2187 /* set all structure fields according to the token/value pairs */
2189 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2190 setSetupInfo(levelinfo_tokens, i,
2191 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2195 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2196 setString(&artwork_new->name, artwork_new->filename);
2198 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2200 free(artwork_new->name);
2201 artwork_new->name = getStringCopy(artwork_new->filename);
2206 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2209 if (artwork_new->identifier == NULL)
2210 artwork_new->identifier = getStringCopy(artwork_new->filename);
2212 if (artwork_new->name_sorting == NULL)
2213 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2216 if (node_parent == NULL) /* top level group */
2218 artwork_new->basepath = getStringCopy(base_directory);
2219 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2221 else /* sub level group */
2223 artwork_new->basepath = getStringCopy(node_parent->basepath);
2224 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2228 artwork_new->user_defined =
2229 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2231 artwork_new->user_defined =
2232 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2235 /* (may use ".sort_priority" from "setup_file_hash" above) */
2236 artwork_new->color = ARTWORKCOLOR(artwork_new);
2238 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2240 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2243 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2246 if (artwork_new->name != NULL)
2248 free(artwork_new->name);
2249 artwork_new->name = NULL;
2254 if (artwork_new->identifier != NULL)
2256 free(artwork_new->identifier);
2257 artwork_new->identifier = NULL;
2261 if (strcmp(artwork_new->filename, ".") == 0)
2263 if (artwork_new->user_defined)
2266 setString(&artwork_new->identifier, "private");
2268 artwork_new->identifier = getStringCopy("private");
2270 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2275 setString(&artwork_new->identifier, "classic");
2277 artwork_new->identifier = getStringCopy("classic");
2279 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2282 /* set to new values after changing ".sort_priority" */
2283 artwork_new->color = ARTWORKCOLOR(artwork_new);
2285 setString(&artwork_new->class_desc,
2286 getLevelClassDescription(artwork_new));
2288 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2294 setString(&artwork_new->identifier, artwork_new->filename);
2296 artwork_new->identifier = getStringCopy(artwork_new->filename);
2301 setString(&artwork_new->name, artwork_new->identifier);
2302 setString(&artwork_new->name_sorting, artwork_new->name);
2304 artwork_new->name = getStringCopy(artwork_new->identifier);
2305 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2309 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2311 pushTreeInfo(node_first, artwork_new);
2313 freeSetupFileHash(setup_file_hash);
2315 free(directory_path);
2321 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2322 TreeInfo *node_parent,
2323 char *base_directory, int type)
2326 struct dirent *dir_entry;
2327 boolean valid_entry_found = FALSE;
2329 if ((dir = opendir(base_directory)) == NULL)
2331 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2332 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2336 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2338 struct stat file_status;
2339 char *directory_name = dir_entry->d_name;
2340 char *directory_path = getPath2(base_directory, directory_name);
2342 /* skip entries for current and parent directory */
2343 if (strcmp(directory_name, ".") == 0 ||
2344 strcmp(directory_name, "..") == 0)
2346 free(directory_path);
2350 /* find out if directory entry is itself a directory */
2351 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2352 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2354 free(directory_path);
2358 free(directory_path);
2360 /* check if this directory contains artwork with or without config file */
2361 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2363 directory_name, type);
2368 /* check if this directory directly contains artwork itself */
2369 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2370 base_directory, ".",
2372 if (!valid_entry_found)
2373 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2377 static TreeInfo *getDummyArtworkInfo(int type)
2379 /* this is only needed when there is completely no artwork available */
2380 TreeInfo *artwork_new = newTreeInfo();
2382 setTreeInfoToDefaults(artwork_new, type);
2385 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2386 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2387 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2389 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2390 setString(&artwork_new->name, UNDEFINED_FILENAME);
2391 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2393 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2394 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2395 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2397 checked_free(artwork_new->name);
2399 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2400 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2401 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2407 void LoadArtworkInfo()
2409 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2411 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2412 options.graphics_directory,
2413 TREE_TYPE_GRAPHICS_DIR);
2414 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2415 getUserGraphicsDir(),
2416 TREE_TYPE_GRAPHICS_DIR);
2418 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2419 options.sounds_directory,
2420 TREE_TYPE_SOUNDS_DIR);
2421 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2423 TREE_TYPE_SOUNDS_DIR);
2425 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2426 options.music_directory,
2427 TREE_TYPE_MUSIC_DIR);
2428 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2430 TREE_TYPE_MUSIC_DIR);
2432 if (artwork.gfx_first == NULL)
2433 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2434 if (artwork.snd_first == NULL)
2435 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2436 if (artwork.mus_first == NULL)
2437 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2439 /* before sorting, the first entries will be from the user directory */
2440 artwork.gfx_current =
2441 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2442 if (artwork.gfx_current == NULL)
2443 artwork.gfx_current =
2444 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2445 if (artwork.gfx_current == NULL)
2446 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2448 artwork.snd_current =
2449 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2450 if (artwork.snd_current == NULL)
2451 artwork.snd_current =
2452 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2453 if (artwork.snd_current == NULL)
2454 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2456 artwork.mus_current =
2457 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2458 if (artwork.mus_current == NULL)
2459 artwork.mus_current =
2460 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2461 if (artwork.mus_current == NULL)
2462 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2464 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2465 artwork.snd_current_identifier = artwork.snd_current->identifier;
2466 artwork.mus_current_identifier = artwork.mus_current->identifier;
2469 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2470 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2471 printf("music set == %s\n\n", artwork.mus_current_identifier);
2474 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2475 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2476 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2479 dumpTreeInfo(artwork.gfx_first, 0);
2480 dumpTreeInfo(artwork.snd_first, 0);
2481 dumpTreeInfo(artwork.mus_first, 0);
2485 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2486 LevelDirTree *level_node)
2488 /* recursively check all level directories for artwork sub-directories */
2492 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2493 ARTWORK_DIRECTORY((*artwork_node)->type));
2496 if (!level_node->parent_link)
2497 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2498 level_node->filename, level_node->name);
2501 if (!level_node->parent_link)
2503 TreeInfo *topnode_last = *artwork_node;
2505 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2506 (*artwork_node)->type);
2508 if (topnode_last != *artwork_node)
2510 free((*artwork_node)->identifier);
2511 free((*artwork_node)->name);
2512 free((*artwork_node)->name_sorting);
2514 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2515 (*artwork_node)->name = getStringCopy(level_node->name);
2516 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2518 (*artwork_node)->sort_priority = level_node->sort_priority;
2519 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2525 if (level_node->node_group != NULL)
2526 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2528 level_node = level_node->next;
2532 void LoadLevelArtworkInfo()
2534 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2536 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2537 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2538 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2540 /* needed for reloading level artwork not known at ealier stage */
2542 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2544 artwork.gfx_current =
2545 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2546 if (artwork.gfx_current == NULL)
2547 artwork.gfx_current =
2548 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2549 if (artwork.gfx_current == NULL)
2550 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2553 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2555 artwork.snd_current =
2556 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2557 if (artwork.snd_current == NULL)
2558 artwork.snd_current =
2559 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2560 if (artwork.snd_current == NULL)
2561 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2564 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2566 artwork.mus_current =
2567 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2568 if (artwork.mus_current == NULL)
2569 artwork.mus_current =
2570 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2571 if (artwork.mus_current == NULL)
2572 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2575 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2576 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2577 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2580 dumpTreeInfo(artwork.gfx_first, 0);
2581 dumpTreeInfo(artwork.snd_first, 0);
2582 dumpTreeInfo(artwork.mus_first, 0);
2586 static void SaveUserLevelInfo()
2588 LevelDirTree *level_info;
2593 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2595 if (!(file = fopen(filename, MODE_WRITE)))
2597 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2602 level_info = newTreeInfo();
2604 /* always start with reliable default values */
2605 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2608 setString(&level_info->name, getLoginName());
2609 setString(&level_info->author, getRealName());
2610 level_info->levels = 100;
2611 level_info->first_level = 1;
2612 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2613 level_info->readonly = FALSE;
2614 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2615 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2616 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2618 ldi.name = getStringCopy(getLoginName());
2619 ldi.author = getStringCopy(getRealName());
2621 ldi.first_level = 1;
2622 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2623 ldi.readonly = FALSE;
2624 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2625 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2626 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2629 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2630 getCookie("LEVELINFO")));
2633 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2634 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2635 i != LEVELINFO_TOKEN_NAME_SORTING &&
2636 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2637 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2641 SetFilePermissions(filename, PERMS_PRIVATE);
2643 freeTreeInfo(level_info);
2647 char *getSetupValue(int type, void *value)
2649 static char value_string[MAX_LINE_LEN];
2657 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2661 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2665 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2669 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2673 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2677 sprintf(value_string, "%d", *(int *)value);
2681 strcpy(value_string, *(char **)value);
2685 value_string[0] = '\0';
2689 return value_string;
2692 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2696 static char token_string[MAX_LINE_LEN];
2697 int token_type = token_info[token_nr].type;
2698 void *setup_value = token_info[token_nr].value;
2699 char *token_text = token_info[token_nr].text;
2700 char *value_string = getSetupValue(token_type, setup_value);
2702 /* build complete token string */
2703 sprintf(token_string, "%s%s", prefix, token_text);
2705 /* build setup entry line */
2706 line = getFormattedSetupEntry(token_string, value_string);
2708 if (token_type == TYPE_KEY_X11)
2710 Key key = *(Key *)setup_value;
2711 char *keyname = getKeyNameFromKey(key);
2713 /* add comment, if useful */
2714 if (strcmp(keyname, "(undefined)") != 0 &&
2715 strcmp(keyname, "(unknown)") != 0)
2717 /* add at least one whitespace */
2719 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2723 strcat(line, keyname);
2730 void LoadLevelSetup_LastSeries()
2732 /* ----------------------------------------------------------------------- */
2733 /* ~/.<program>/levelsetup.conf */
2734 /* ----------------------------------------------------------------------- */
2736 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2737 SetupFileHash *level_setup_hash = NULL;
2739 /* always start with reliable default values */
2740 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2742 if ((level_setup_hash = loadSetupFileHash(filename)))
2744 char *last_level_series =
2745 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2747 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2749 if (leveldir_current == NULL)
2750 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2752 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2754 freeSetupFileHash(level_setup_hash);
2757 Error(ERR_WARN, "using default setup values");
2762 void SaveLevelSetup_LastSeries()
2764 /* ----------------------------------------------------------------------- */
2765 /* ~/.<program>/levelsetup.conf */
2766 /* ----------------------------------------------------------------------- */
2768 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2769 char *level_subdir = leveldir_current->filename;
2772 InitUserDataDirectory();
2774 if (!(file = fopen(filename, MODE_WRITE)))
2776 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2781 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2782 getCookie("LEVELSETUP")));
2783 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2788 SetFilePermissions(filename, PERMS_PRIVATE);
2793 static void checkSeriesInfo()
2795 static char *level_directory = NULL;
2797 struct dirent *dir_entry;
2799 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2801 level_directory = getPath2((leveldir_current->user_defined ?
2802 getUserLevelDir(NULL) :
2803 options.level_directory),
2804 leveldir_current->fullpath);
2806 if ((dir = opendir(level_directory)) == NULL)
2808 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2812 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2814 if (strlen(dir_entry->d_name) > 4 &&
2815 dir_entry->d_name[3] == '.' &&
2816 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2818 char levelnum_str[4];
2821 strncpy(levelnum_str, dir_entry->d_name, 3);
2822 levelnum_str[3] = '\0';
2824 levelnum_value = atoi(levelnum_str);
2827 if (levelnum_value < leveldir_current->first_level)
2829 Error(ERR_WARN, "additional level %d found", levelnum_value);
2830 leveldir_current->first_level = levelnum_value;
2832 else if (levelnum_value > leveldir_current->last_level)
2834 Error(ERR_WARN, "additional level %d found", levelnum_value);
2835 leveldir_current->last_level = levelnum_value;
2844 void LoadLevelSetup_SeriesInfo()
2847 SetupFileHash *level_setup_hash = NULL;
2848 char *level_subdir = leveldir_current->filename;
2850 /* always start with reliable default values */
2851 level_nr = leveldir_current->first_level;
2853 checkSeriesInfo(leveldir_current);
2855 /* ----------------------------------------------------------------------- */
2856 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2857 /* ----------------------------------------------------------------------- */
2859 level_subdir = leveldir_current->filename;
2861 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2863 if ((level_setup_hash = loadSetupFileHash(filename)))
2867 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2871 level_nr = atoi(token_value);
2873 if (level_nr < leveldir_current->first_level)
2874 level_nr = leveldir_current->first_level;
2875 if (level_nr > leveldir_current->last_level)
2876 level_nr = leveldir_current->last_level;
2879 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2883 int level_nr = atoi(token_value);
2885 if (level_nr < leveldir_current->first_level)
2886 level_nr = leveldir_current->first_level;
2887 if (level_nr > leveldir_current->last_level + 1)
2888 level_nr = leveldir_current->last_level;
2890 if (leveldir_current->user_defined)
2891 level_nr = leveldir_current->last_level;
2893 leveldir_current->handicap_level = level_nr;
2896 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2898 freeSetupFileHash(level_setup_hash);
2901 Error(ERR_WARN, "using default setup values");
2906 void SaveLevelSetup_SeriesInfo()
2909 char *level_subdir = leveldir_current->filename;
2910 char *level_nr_str = int2str(level_nr, 0);
2911 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2914 /* ----------------------------------------------------------------------- */
2915 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2916 /* ----------------------------------------------------------------------- */
2918 InitLevelSetupDirectory(level_subdir);
2920 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2922 if (!(file = fopen(filename, MODE_WRITE)))
2924 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2929 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2930 getCookie("LEVELSETUP")));
2931 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2933 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2934 handicap_level_str));
2938 SetFilePermissions(filename, PERMS_PRIVATE);