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_YELLOW : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_YELLOW : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_SB(n) ? FC_YELLOW : \
49 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
50 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
53 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
54 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
55 IS_LEVELCLASS_BD(n) ? 2 : \
56 IS_LEVELCLASS_EM(n) ? 3 : \
57 IS_LEVELCLASS_SP(n) ? 4 : \
58 IS_LEVELCLASS_DX(n) ? 5 : \
59 IS_LEVELCLASS_SB(n) ? 6 : \
60 IS_LEVELCLASS_CONTRIB(n) ? 7 : \
61 IS_LEVELCLASS_PRIVATE(n) ? 8 : \
64 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
65 IS_ARTWORKCLASS_CONTRIB(n) ? FC_GREEN : \
66 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
67 IS_ARTWORKCLASS_LEVEL(n) ? FC_YELLOW : \
70 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
71 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
72 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
73 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
76 #define TOKEN_VALUE_POSITION_SHORT 32
77 #define TOKEN_VALUE_POSITION_DEFAULT 40
78 #define TOKEN_COMMENT_POSITION_DEFAULT 60
80 #define MAX_COOKIE_LEN 256
82 static int token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
83 static int token_comment_position = TOKEN_COMMENT_POSITION_DEFAULT;
86 /* ------------------------------------------------------------------------- */
88 /* ------------------------------------------------------------------------- */
90 static char *getLevelClassDescription(TreeInfo *ldi)
92 int position = ldi->sort_priority / 100;
94 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
95 return levelclass_desc[position];
97 return "Unknown Level Class";
100 static char *getUserLevelDir(char *level_subdir)
102 static char *userlevel_dir = NULL;
103 char *data_dir = getUserDataDir();
104 char *userlevel_subdir = LEVELS_DIRECTORY;
106 checked_free(userlevel_dir);
108 if (level_subdir != NULL)
109 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
111 userlevel_dir = getPath2(data_dir, userlevel_subdir);
113 return userlevel_dir;
116 static char *getScoreDir(char *level_subdir)
118 static char *score_dir = NULL;
119 char *data_dir = getCommonDataDir();
120 char *score_subdir = SCORES_DIRECTORY;
122 checked_free(score_dir);
124 if (level_subdir != NULL)
125 score_dir = getPath3(data_dir, score_subdir, level_subdir);
127 score_dir = getPath2(data_dir, score_subdir);
132 static char *getLevelSetupDir(char *level_subdir)
134 static char *levelsetup_dir = NULL;
135 char *data_dir = getUserDataDir();
136 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
138 checked_free(levelsetup_dir);
140 if (level_subdir != NULL)
141 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
143 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
145 return levelsetup_dir;
148 static char *getLevelDirFromTreeInfo(TreeInfo *node)
150 static char *level_dir = NULL;
153 return options.level_directory;
155 checked_free(level_dir);
157 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
158 options.level_directory), node->fullpath);
163 char *getCurrentLevelDir()
165 return getLevelDirFromTreeInfo(leveldir_current);
168 static char *getTapeDir(char *level_subdir)
170 static char *tape_dir = NULL;
171 char *data_dir = getUserDataDir();
172 char *tape_subdir = TAPES_DIRECTORY;
174 checked_free(tape_dir);
176 if (level_subdir != NULL)
177 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
179 tape_dir = getPath2(data_dir, tape_subdir);
184 static char *getSolutionTapeDir()
186 static char *tape_dir = NULL;
187 char *data_dir = getCurrentLevelDir();
188 char *tape_subdir = TAPES_DIRECTORY;
190 checked_free(tape_dir);
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;
204 checked_free(graphics_dir);
206 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
211 static char *getDefaultSoundsDir(char *sounds_subdir)
213 static char *sounds_dir = NULL;
215 if (sounds_subdir == NULL)
216 return options.sounds_directory;
218 checked_free(sounds_dir);
220 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
225 static char *getDefaultMusicDir(char *music_subdir)
227 static char *music_dir = NULL;
229 if (music_subdir == NULL)
230 return options.music_directory;
232 checked_free(music_dir);
234 music_dir = getPath2(options.music_directory, music_subdir);
239 static char *getDefaultArtworkSet(int type)
241 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
242 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
243 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
246 static char *getDefaultArtworkDir(int type)
248 return (type == TREE_TYPE_GRAPHICS_DIR ?
249 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
250 type == TREE_TYPE_SOUNDS_DIR ?
251 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
252 type == TREE_TYPE_MUSIC_DIR ?
253 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
256 static char *getUserGraphicsDir()
258 static char *usergraphics_dir = NULL;
260 if (usergraphics_dir == NULL)
261 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
263 return usergraphics_dir;
266 static char *getUserSoundsDir()
268 static char *usersounds_dir = NULL;
270 if (usersounds_dir == NULL)
271 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
273 return usersounds_dir;
276 static char *getUserMusicDir()
278 static char *usermusic_dir = NULL;
280 if (usermusic_dir == NULL)
281 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
283 return usermusic_dir;
286 static char *getSetupArtworkDir(TreeInfo *ti)
288 static char *artwork_dir = NULL;
290 checked_free(artwork_dir);
292 artwork_dir = getPath2(ti->basepath, ti->fullpath);
297 char *setLevelArtworkDir(TreeInfo *ti)
299 char **artwork_path_ptr, **artwork_set_ptr;
300 TreeInfo *level_artwork;
302 if (ti == NULL || leveldir_current == NULL)
305 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
306 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
308 checked_free(*artwork_path_ptr);
310 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
311 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
314 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
315 normally result in using the artwork configured in the setup menu. But
316 if an artwork subdirectory exists (which might contain custom artwork
317 or an artwork configuration file), this level artwork must be treated
318 as relative to the default "classic" artwork, not to the artwork that
319 is currently configured in the setup menu. */
321 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
323 checked_free(*artwork_set_ptr);
327 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
328 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
332 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
333 *artwork_set_ptr = NULL;
339 return *artwork_set_ptr;
342 inline static char *getLevelArtworkSet(int type)
344 if (leveldir_current == NULL)
347 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
350 inline static char *getLevelArtworkDir(int type)
352 if (leveldir_current == NULL)
353 return UNDEFINED_FILENAME;
355 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
358 char *getTapeFilename(int nr)
360 static char *filename = NULL;
361 char basename[MAX_FILENAME_LEN];
363 checked_free(filename);
365 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
366 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
371 char *getSolutionTapeFilename(int nr)
373 static char *filename = NULL;
374 char basename[MAX_FILENAME_LEN];
376 checked_free(filename);
378 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
379 filename = getPath2(getSolutionTapeDir(), basename);
384 char *getScoreFilename(int nr)
386 static char *filename = NULL;
387 char basename[MAX_FILENAME_LEN];
389 checked_free(filename);
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 checked_free(filename);
403 filename = getPath2(getSetupDir(), SETUP_FILENAME);
408 char *getEditorSetupFilename()
410 static char *filename = NULL;
412 checked_free(filename);
413 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
415 if (fileExists(filename))
418 checked_free(filename);
419 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
424 char *getHelpAnimFilename()
426 static char *filename = NULL;
428 checked_free(filename);
430 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
435 char *getHelpTextFilename()
437 static char *filename = NULL;
439 checked_free(filename);
441 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
446 char *getLevelSetInfoFilename()
448 static char *filename = NULL;
463 for (i = 0; basenames[i] != NULL; i++)
465 checked_free(filename);
466 filename = getPath2(getCurrentLevelDir(), basenames[i]);
468 if (fileExists(filename))
475 static char *getCorrectedArtworkBasename(char *basename)
477 char *basename_corrected = basename;
479 #if defined(PLATFORM_MSDOS)
480 if (program.filename_prefix != NULL)
482 int prefix_len = strlen(program.filename_prefix);
484 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
485 basename_corrected = &basename[prefix_len];
487 /* if corrected filename is still longer than standard MS-DOS filename
488 size (8 characters + 1 dot + 3 characters file extension), shorten
489 filename by writing file extension after 8th basename character */
490 if (strlen(basename_corrected) > 8 + 1 + 3)
492 static char *msdos_filename = NULL;
494 checked_free(msdos_filename);
496 msdos_filename = getStringCopy(basename_corrected);
497 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
499 basename_corrected = msdos_filename;
504 return basename_corrected;
507 char *getCustomImageFilename(char *basename)
509 static char *filename = NULL;
510 boolean skip_setup_artwork = FALSE;
512 checked_free(filename);
514 basename = getCorrectedArtworkBasename(basename);
516 if (!setup.override_level_graphics)
518 /* 1st try: look for special artwork in current level series directory */
519 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
520 if (fileExists(filename))
525 /* check if there is special artwork configured in level series config */
526 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
528 /* 2nd try: look for special artwork configured in level series config */
529 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
530 if (fileExists(filename))
535 /* take missing artwork configured in level set config from default */
536 skip_setup_artwork = TRUE;
540 if (!skip_setup_artwork)
542 /* 3rd try: look for special artwork in configured artwork directory */
543 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
544 if (fileExists(filename))
550 /* 4th try: look for default artwork in new default artwork directory */
551 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
552 if (fileExists(filename))
557 /* 5th try: look for default artwork in old default artwork directory */
558 filename = getPath2(options.graphics_directory, basename);
559 if (fileExists(filename))
562 return NULL; /* cannot find specified artwork file anywhere */
565 char *getCustomSoundFilename(char *basename)
567 static char *filename = NULL;
568 boolean skip_setup_artwork = FALSE;
570 checked_free(filename);
572 basename = getCorrectedArtworkBasename(basename);
574 if (!setup.override_level_sounds)
576 /* 1st try: look for special artwork in current level series directory */
577 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
578 if (fileExists(filename))
583 /* check if there is special artwork configured in level series config */
584 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
586 /* 2nd try: look for special artwork configured in level series config */
587 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
588 if (fileExists(filename))
593 /* take missing artwork configured in level set config from default */
594 skip_setup_artwork = TRUE;
598 if (!skip_setup_artwork)
600 /* 3rd try: look for special artwork in configured artwork directory */
601 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
602 if (fileExists(filename))
608 /* 4th try: look for default artwork in new default artwork directory */
609 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
610 if (fileExists(filename))
615 /* 5th try: look for default artwork in old default artwork directory */
616 filename = getPath2(options.sounds_directory, basename);
617 if (fileExists(filename))
620 return NULL; /* cannot find specified artwork file anywhere */
623 char *getCustomMusicFilename(char *basename)
625 static char *filename = NULL;
626 boolean skip_setup_artwork = FALSE;
628 checked_free(filename);
630 basename = getCorrectedArtworkBasename(basename);
632 if (!setup.override_level_music)
634 /* 1st try: look for special artwork in current level series directory */
635 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
636 if (fileExists(filename))
641 /* check if there is special artwork configured in level series config */
642 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
644 /* 2nd try: look for special artwork configured in level series config */
645 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
646 if (fileExists(filename))
651 /* take missing artwork configured in level set config from default */
652 skip_setup_artwork = TRUE;
656 if (!skip_setup_artwork)
658 /* 3rd try: look for special artwork in configured artwork directory */
659 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
660 if (fileExists(filename))
666 /* 4th try: look for default artwork in new default artwork directory */
667 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
668 if (fileExists(filename))
673 /* 5th try: look for default artwork in old default artwork directory */
674 filename = getPath2(options.music_directory, basename);
675 if (fileExists(filename))
678 return NULL; /* cannot find specified artwork file anywhere */
681 char *getCustomArtworkFilename(char *basename, int type)
683 if (type == ARTWORK_TYPE_GRAPHICS)
684 return getCustomImageFilename(basename);
685 else if (type == ARTWORK_TYPE_SOUNDS)
686 return getCustomSoundFilename(basename);
687 else if (type == ARTWORK_TYPE_MUSIC)
688 return getCustomMusicFilename(basename);
690 return UNDEFINED_FILENAME;
693 char *getCustomArtworkConfigFilename(int type)
695 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
698 char *getCustomArtworkLevelConfigFilename(int type)
700 static char *filename = NULL;
702 checked_free(filename);
704 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
709 char *getCustomMusicDirectory(void)
711 static char *directory = NULL;
712 boolean skip_setup_artwork = FALSE;
714 checked_free(directory);
716 if (!setup.override_level_music)
718 /* 1st try: look for special artwork in current level series directory */
719 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
720 if (fileExists(directory))
725 /* check if there is special artwork configured in level series config */
726 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
728 /* 2nd try: look for special artwork configured in level series config */
729 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
730 if (fileExists(directory))
735 /* take missing artwork configured in level set config from default */
736 skip_setup_artwork = TRUE;
740 if (!skip_setup_artwork)
742 /* 3rd try: look for special artwork in configured artwork directory */
743 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
744 if (fileExists(directory))
750 /* 4th try: look for default artwork in new default artwork directory */
751 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
752 if (fileExists(directory))
757 /* 5th try: look for default artwork in old default artwork directory */
758 directory = getStringCopy(options.music_directory);
759 if (fileExists(directory))
762 return NULL; /* cannot find specified artwork file anywhere */
765 void InitTapeDirectory(char *level_subdir)
767 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
768 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
769 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
772 void InitScoreDirectory(char *level_subdir)
774 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
775 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
776 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
779 static void SaveUserLevelInfo();
781 void InitUserLevelDirectory(char *level_subdir)
783 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
785 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
786 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
787 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
793 void InitLevelSetupDirectory(char *level_subdir)
795 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
796 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
797 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
801 /* ------------------------------------------------------------------------- */
802 /* some functions to handle lists of level directories */
803 /* ------------------------------------------------------------------------- */
805 TreeInfo *newTreeInfo()
807 return checked_calloc(sizeof(TreeInfo));
810 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
812 node_new->next = *node_first;
813 *node_first = node_new;
816 int numTreeInfo(TreeInfo *node)
829 boolean validLevelSeries(TreeInfo *node)
831 return (node != NULL && !node->node_group && !node->parent_link);
834 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
839 if (node->node_group) /* enter level group (step down into tree) */
840 return getFirstValidTreeInfoEntry(node->node_group);
841 else if (node->parent_link) /* skip start entry of level group */
843 if (node->next) /* get first real level series entry */
844 return getFirstValidTreeInfoEntry(node->next);
845 else /* leave empty level group and go on */
846 return getFirstValidTreeInfoEntry(node->node_parent->next);
848 else /* this seems to be a regular level series */
852 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
857 if (node->node_parent == NULL) /* top level group */
858 return *node->node_top;
859 else /* sub level group */
860 return node->node_parent->node_group;
863 int numTreeInfoInGroup(TreeInfo *node)
865 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
868 int posTreeInfo(TreeInfo *node)
870 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
875 if (node_cmp == node)
879 node_cmp = node_cmp->next;
885 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
887 TreeInfo *node_default = node;
902 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
904 if (identifier == NULL)
909 if (node->node_group)
911 TreeInfo *node_group;
913 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
918 else if (!node->parent_link)
920 if (strcmp(identifier, node->identifier) == 0)
930 void dumpTreeInfo(TreeInfo *node, int depth)
934 printf("Dumping TreeInfo:\n");
938 for (i = 0; i < (depth + 1) * 3; i++)
942 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
943 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
945 printf("subdir == '%s' (%s) [%s] (%d)\n",
946 node->subdir, node->name, node->identifier, node->sort_priority);
949 if (node->node_group != NULL)
950 dumpTreeInfo(node->node_group, depth + 1);
956 void sortTreeInfo(TreeInfo **node_first,
957 int (*compare_function)(const void *, const void *))
959 int num_nodes = numTreeInfo(*node_first);
960 TreeInfo **sort_array;
961 TreeInfo *node = *node_first;
967 /* allocate array for sorting structure pointers */
968 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
970 /* writing structure pointers to sorting array */
971 while (i < num_nodes && node) /* double boundary check... */
973 sort_array[i] = node;
979 /* sorting the structure pointers in the sorting array */
980 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
983 /* update the linkage of list elements with the sorted node array */
984 for (i = 0; i < num_nodes - 1; i++)
985 sort_array[i]->next = sort_array[i + 1];
986 sort_array[num_nodes - 1]->next = NULL;
988 /* update the linkage of the main list anchor pointer */
989 *node_first = sort_array[0];
993 /* now recursively sort the level group structures */
997 if (node->node_group != NULL)
998 sortTreeInfo(&node->node_group, compare_function);
1005 /* ========================================================================= */
1006 /* some stuff from "files.c" */
1007 /* ========================================================================= */
1009 #if defined(PLATFORM_WIN32)
1011 #define S_IRGRP S_IRUSR
1014 #define S_IROTH S_IRUSR
1017 #define S_IWGRP S_IWUSR
1020 #define S_IWOTH S_IWUSR
1023 #define S_IXGRP S_IXUSR
1026 #define S_IXOTH S_IXUSR
1029 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1034 #endif /* PLATFORM_WIN32 */
1036 /* file permissions for newly written files */
1037 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1038 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1039 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1041 #define MODE_W_PRIVATE (S_IWUSR)
1042 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1043 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1045 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1046 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1048 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1049 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1051 char *getUserDataDir(void)
1053 static char *userdata_dir = NULL;
1055 if (userdata_dir == NULL)
1056 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1058 return userdata_dir;
1061 char *getCommonDataDir(void)
1063 static char *common_data_dir = NULL;
1065 #if defined(PLATFORM_WIN32)
1066 if (common_data_dir == NULL)
1068 char *dir = checked_malloc(MAX_PATH + 1);
1070 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1071 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1072 common_data_dir = getPath2(dir, program.userdata_directory);
1074 common_data_dir = options.rw_base_directory;
1077 if (common_data_dir == NULL)
1078 common_data_dir = options.rw_base_directory;
1081 return common_data_dir;
1086 return getUserDataDir();
1089 static mode_t posix_umask(mode_t mask)
1091 #if defined(PLATFORM_UNIX)
1098 static int posix_mkdir(const char *pathname, mode_t mode)
1100 #if defined(PLATFORM_WIN32)
1101 return mkdir(pathname);
1103 return mkdir(pathname, mode);
1107 void createDirectory(char *dir, char *text, int permission_class)
1109 /* leave "other" permissions in umask untouched, but ensure group parts
1110 of USERDATA_DIR_MODE are not masked */
1111 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1112 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1113 mode_t normal_umask = posix_umask(0);
1114 mode_t group_umask = ~(dir_mode & S_IRWXG);
1115 posix_umask(normal_umask & group_umask);
1117 if (access(dir, F_OK) != 0)
1118 if (posix_mkdir(dir, dir_mode) != 0)
1119 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1121 posix_umask(normal_umask); /* reset normal umask */
1124 void InitUserDataDirectory()
1126 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1129 void SetFilePermissions(char *filename, int permission_class)
1131 chmod(filename, (permission_class == PERMS_PRIVATE ?
1132 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1135 char *getCookie(char *file_type)
1137 static char cookie[MAX_COOKIE_LEN + 1];
1139 if (strlen(program.cookie_prefix) + 1 +
1140 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1141 return "[COOKIE ERROR]"; /* should never happen */
1143 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1144 program.cookie_prefix, file_type,
1145 program.version_major, program.version_minor);
1150 int getFileVersionFromCookieString(const char *cookie)
1152 const char *ptr_cookie1, *ptr_cookie2;
1153 const char *pattern1 = "_FILE_VERSION_";
1154 const char *pattern2 = "?.?";
1155 const int len_cookie = strlen(cookie);
1156 const int len_pattern1 = strlen(pattern1);
1157 const int len_pattern2 = strlen(pattern2);
1158 const int len_pattern = len_pattern1 + len_pattern2;
1159 int version_major, version_minor;
1161 if (len_cookie <= len_pattern)
1164 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1165 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1167 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1170 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1171 ptr_cookie2[1] != '.' ||
1172 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1175 version_major = ptr_cookie2[0] - '0';
1176 version_minor = ptr_cookie2[2] - '0';
1178 return VERSION_IDENT(version_major, version_minor, 0, 0);
1181 boolean checkCookieString(const char *cookie, const char *template)
1183 const char *pattern = "_FILE_VERSION_?.?";
1184 const int len_cookie = strlen(cookie);
1185 const int len_template = strlen(template);
1186 const int len_pattern = strlen(pattern);
1188 if (len_cookie != len_template)
1191 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1197 /* ------------------------------------------------------------------------- */
1198 /* setup file list and hash handling functions */
1199 /* ------------------------------------------------------------------------- */
1201 char *getFormattedSetupEntry(char *token, char *value)
1204 static char entry[MAX_LINE_LEN];
1206 /* if value is an empty string, just return token without value */
1210 /* start with the token and some spaces to format output line */
1211 sprintf(entry, "%s:", token);
1212 for (i = strlen(entry); i < token_value_position; i++)
1215 /* continue with the token's value */
1216 strcat(entry, value);
1221 SetupFileList *newSetupFileList(char *token, char *value)
1223 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1225 new->token = getStringCopy(token);
1226 new->value = getStringCopy(value);
1233 void freeSetupFileList(SetupFileList *list)
1238 checked_free(list->token);
1239 checked_free(list->value);
1242 freeSetupFileList(list->next);
1247 char *getListEntry(SetupFileList *list, char *token)
1252 if (strcmp(list->token, token) == 0)
1255 return getListEntry(list->next, token);
1258 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1263 if (strcmp(list->token, token) == 0)
1265 checked_free(list->value);
1267 list->value = getStringCopy(value);
1271 else if (list->next == NULL)
1272 return (list->next = newSetupFileList(token, value));
1274 return setListEntry(list->next, token, value);
1277 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1282 if (list->next == NULL)
1283 return (list->next = newSetupFileList(token, value));
1285 return addListEntry(list->next, token, value);
1289 static void printSetupFileList(SetupFileList *list)
1294 printf("token: '%s'\n", list->token);
1295 printf("value: '%s'\n", list->value);
1297 printSetupFileList(list->next);
1302 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1303 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1304 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1305 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1307 #define insert_hash_entry hashtable_insert
1308 #define search_hash_entry hashtable_search
1309 #define change_hash_entry hashtable_change
1310 #define remove_hash_entry hashtable_remove
1313 static unsigned int get_hash_from_key(void *key)
1318 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1319 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1320 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1321 it works better than many other constants, prime or not) has never been
1322 adequately explained.
1324 If you just want to have a good hash function, and cannot wait, djb2
1325 is one of the best string hash functions i know. It has excellent
1326 distribution and speed on many different sets of keys and table sizes.
1327 You are not likely to do better with one of the "well known" functions
1328 such as PJW, K&R, etc.
1330 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1333 char *str = (char *)key;
1334 unsigned int hash = 5381;
1337 while ((c = *str++))
1338 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1343 static int keys_are_equal(void *key1, void *key2)
1345 return (strcmp((char *)key1, (char *)key2) == 0);
1348 SetupFileHash *newSetupFileHash()
1350 SetupFileHash *new_hash =
1351 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1353 if (new_hash == NULL)
1354 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1359 void freeSetupFileHash(SetupFileHash *hash)
1364 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1367 char *getHashEntry(SetupFileHash *hash, char *token)
1372 return search_hash_entry(hash, token);
1375 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1382 value_copy = getStringCopy(value);
1384 /* change value; if it does not exist, insert it as new */
1385 if (!change_hash_entry(hash, token, value_copy))
1386 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1387 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1390 char *removeHashEntry(SetupFileHash *hash, char *token)
1395 return remove_hash_entry(hash, token);
1400 static void printSetupFileHash(SetupFileHash *hash)
1402 BEGIN_HASH_ITERATION(hash, itr)
1404 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1405 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1407 END_HASH_ITERATION(hash, itr)
1412 static void *loadSetupFileData(char *filename, boolean use_hash)
1414 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1415 char *token, *value, *line_ptr;
1416 void *setup_file_data, *insert_ptr = NULL;
1417 boolean read_continued_line = FALSE;
1421 setup_file_data = newSetupFileHash();
1423 insert_ptr = setup_file_data = newSetupFileList("", "");
1425 if (!(file = fopen(filename, MODE_READ)))
1427 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1433 /* read next line of input file */
1434 if (!fgets(line, MAX_LINE_LEN, file))
1437 /* cut trailing newline or carriage return */
1438 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1439 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1442 if (read_continued_line)
1444 /* cut leading whitespaces from input line */
1445 for (line_ptr = line; *line_ptr; line_ptr++)
1446 if (*line_ptr != ' ' && *line_ptr != '\t')
1449 /* append new line to existing line, if there is enough space */
1450 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1451 strcat(previous_line, line_ptr);
1453 strcpy(line, previous_line); /* copy storage buffer to line */
1455 read_continued_line = FALSE;
1458 /* if the last character is '\', continue at next line */
1459 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1461 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1462 strcpy(previous_line, line); /* copy line to storage buffer */
1464 read_continued_line = TRUE;
1469 /* cut trailing comment from input line */
1470 for (line_ptr = line; *line_ptr; line_ptr++)
1472 if (*line_ptr == '#')
1479 /* cut trailing whitespaces from input line */
1480 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1481 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1484 /* ignore empty lines */
1488 /* cut leading whitespaces from token */
1489 for (token = line; *token; token++)
1490 if (*token != ' ' && *token != '\t')
1493 /* start with empty value as reliable default */
1496 /* find end of token to determine start of value */
1497 for (line_ptr = token; *line_ptr; line_ptr++)
1499 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1501 *line_ptr = '\0'; /* terminate token string */
1502 value = line_ptr + 1; /* set beginning of value */
1508 /* cut leading whitespaces from value */
1509 for (; *value; value++)
1510 if (*value != ' ' && *value != '\t')
1515 value = "true"; /* treat tokens without value as "true" */
1521 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1523 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1531 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1532 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1536 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1537 SetupFileList *first_valid_list_entry = setup_file_list->next;
1539 /* free empty list header */
1540 setup_file_list->next = NULL;
1541 freeSetupFileList(setup_file_list);
1542 setup_file_data = first_valid_list_entry;
1544 if (first_valid_list_entry == NULL)
1545 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1548 return setup_file_data;
1551 SetupFileList *loadSetupFileList(char *filename)
1553 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1556 SetupFileHash *loadSetupFileHash(char *filename)
1558 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1561 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1564 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1567 Error(ERR_WARN, "configuration file has no file identifier");
1568 else if (!checkCookieString(value, identifier))
1569 Error(ERR_WARN, "configuration file has wrong file identifier");
1573 /* ========================================================================= */
1574 /* setup file stuff */
1575 /* ========================================================================= */
1577 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1578 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1579 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1581 /* level directory info */
1582 #define LEVELINFO_TOKEN_IDENTIFIER 0
1583 #define LEVELINFO_TOKEN_NAME 1
1584 #define LEVELINFO_TOKEN_NAME_SORTING 2
1585 #define LEVELINFO_TOKEN_AUTHOR 3
1586 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1587 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1588 #define LEVELINFO_TOKEN_LEVELS 6
1589 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1590 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1591 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1592 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1593 #define LEVELINFO_TOKEN_READONLY 11
1594 #define LEVELINFO_TOKEN_GRAPHICS_SET 12
1595 #define LEVELINFO_TOKEN_SOUNDS_SET 13
1596 #define LEVELINFO_TOKEN_MUSIC_SET 14
1597 #define LEVELINFO_TOKEN_FILENAME 15
1598 #define LEVELINFO_TOKEN_FILETYPE 16
1599 #define LEVELINFO_TOKEN_HANDICAP 17
1600 #define LEVELINFO_TOKEN_SKIP_LEVELS 18
1602 #define NUM_LEVELINFO_TOKENS 19
1604 static LevelDirTree ldi;
1606 static struct TokenInfo levelinfo_tokens[] =
1608 /* level directory info */
1609 { TYPE_STRING, &ldi.identifier, "identifier" },
1610 { TYPE_STRING, &ldi.name, "name" },
1611 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1612 { TYPE_STRING, &ldi.author, "author" },
1613 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1614 { TYPE_STRING, &ldi.imported_by, "imported_by" },
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" },
1624 { TYPE_STRING, &ldi.level_filename, "filename" },
1625 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1626 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1627 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1630 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1634 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1635 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1636 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1637 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1640 ldi->node_parent = NULL;
1641 ldi->node_group = NULL;
1645 ldi->cl_cursor = -1;
1648 ldi->fullpath = NULL;
1649 ldi->basepath = NULL;
1650 ldi->identifier = NULL;
1651 ldi->name = getStringCopy(ANONYMOUS_NAME);
1652 ldi->name_sorting = NULL;
1653 ldi->author = getStringCopy(ANONYMOUS_NAME);
1655 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1656 ldi->latest_engine = FALSE; /* default: get from level */
1657 ldi->parent_link = FALSE;
1658 ldi->in_user_dir = FALSE;
1659 ldi->user_defined = FALSE;
1661 ldi->class_desc = NULL;
1663 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1665 ldi->imported_from = NULL;
1666 ldi->imported_by = NULL;
1668 ldi->graphics_set = NULL;
1669 ldi->sounds_set = NULL;
1670 ldi->music_set = NULL;
1671 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1672 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1673 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1675 ldi->level_filename = NULL;
1676 ldi->level_filetype = NULL;
1679 ldi->first_level = 0;
1680 ldi->last_level = 0;
1681 ldi->level_group = FALSE;
1682 ldi->handicap_level = 0;
1683 ldi->readonly = TRUE;
1684 ldi->handicap = TRUE;
1685 ldi->skip_levels = FALSE;
1689 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1693 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1695 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1701 /* copy all values from the parent structure */
1703 ldi->type = parent->type;
1705 ldi->node_top = parent->node_top;
1706 ldi->node_parent = parent;
1707 ldi->node_group = NULL;
1711 ldi->cl_cursor = -1;
1714 ldi->fullpath = NULL;
1715 ldi->basepath = NULL;
1716 ldi->identifier = NULL;
1717 ldi->name = getStringCopy(ANONYMOUS_NAME);
1718 ldi->name_sorting = NULL;
1719 ldi->author = getStringCopy(parent->author);
1721 ldi->sort_priority = parent->sort_priority;
1722 ldi->latest_engine = parent->latest_engine;
1723 ldi->parent_link = FALSE;
1724 ldi->in_user_dir = parent->in_user_dir;
1725 ldi->user_defined = parent->user_defined;
1726 ldi->color = parent->color;
1727 ldi->class_desc = getStringCopy(parent->class_desc);
1729 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1731 ldi->imported_from = getStringCopy(parent->imported_from);
1732 ldi->imported_by = getStringCopy(parent->imported_by);
1734 ldi->graphics_set = NULL;
1735 ldi->sounds_set = NULL;
1736 ldi->music_set = NULL;
1737 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1738 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1739 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1741 ldi->level_filename = NULL;
1742 ldi->level_filetype = NULL;
1745 ldi->first_level = 0;
1746 ldi->last_level = 0;
1747 ldi->level_group = FALSE;
1748 ldi->handicap_level = 0;
1749 ldi->readonly = TRUE;
1750 ldi->handicap = TRUE;
1751 ldi->skip_levels = FALSE;
1756 /* first copy all values from the parent structure ... */
1759 /* ... then set all fields to default that cannot be inherited from parent.
1760 This is especially important for all those fields that can be set from
1761 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1762 calls 'free()' for all already set token values which requires that no
1763 other structure's pointer may point to them!
1767 ldi->fullpath = NULL;
1768 ldi->basepath = NULL;
1769 ldi->identifier = NULL;
1770 ldi->name = getStringCopy(ANONYMOUS_NAME);
1771 ldi->name_sorting = NULL;
1772 ldi->author = getStringCopy(parent->author);
1774 ldi->imported_from = getStringCopy(parent->imported_from);
1775 ldi->imported_by = getStringCopy(parent->imported_by);
1776 ldi->class_desc = getStringCopy(parent->class_desc);
1778 ldi->graphics_set = NULL;
1779 ldi->sounds_set = NULL;
1780 ldi->music_set = NULL;
1781 ldi->graphics_path = NULL;
1782 ldi->sounds_path = NULL;
1783 ldi->music_path = NULL;
1785 ldi->level_group = FALSE;
1786 ldi->parent_link = FALSE;
1788 ldi->node_top = parent->node_top;
1789 ldi->node_parent = parent;
1790 ldi->node_group = NULL;
1796 static void freeTreeInfo(TreeInfo *ldi)
1798 checked_free(ldi->subdir);
1799 checked_free(ldi->fullpath);
1800 checked_free(ldi->basepath);
1801 checked_free(ldi->identifier);
1803 checked_free(ldi->name);
1804 checked_free(ldi->name_sorting);
1805 checked_free(ldi->author);
1807 checked_free(ldi->class_desc);
1809 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1811 checked_free(ldi->imported_from);
1812 checked_free(ldi->imported_by);
1814 checked_free(ldi->graphics_set);
1815 checked_free(ldi->sounds_set);
1816 checked_free(ldi->music_set);
1818 checked_free(ldi->graphics_path);
1819 checked_free(ldi->sounds_path);
1820 checked_free(ldi->music_path);
1822 checked_free(ldi->level_filename);
1823 checked_free(ldi->level_filetype);
1827 void setSetupInfo(struct TokenInfo *token_info,
1828 int token_nr, char *token_value)
1830 int token_type = token_info[token_nr].type;
1831 void *setup_value = token_info[token_nr].value;
1833 if (token_value == NULL)
1836 /* set setup field to corresponding token value */
1841 *(boolean *)setup_value = get_boolean_from_string(token_value);
1845 *(Key *)setup_value = getKeyFromKeyName(token_value);
1849 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1853 *(int *)setup_value = get_integer_from_string(token_value);
1857 checked_free(*(char **)setup_value);
1858 *(char **)setup_value = getStringCopy(token_value);
1866 static int compareTreeInfoEntries(const void *object1, const void *object2)
1868 const TreeInfo *entry1 = *((TreeInfo **)object1);
1869 const TreeInfo *entry2 = *((TreeInfo **)object2);
1870 int class_sorting1, class_sorting2;
1873 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1875 class_sorting1 = LEVELSORTING(entry1);
1876 class_sorting2 = LEVELSORTING(entry2);
1880 class_sorting1 = ARTWORKSORTING(entry1);
1881 class_sorting2 = ARTWORKSORTING(entry2);
1884 if (entry1->parent_link || entry2->parent_link)
1885 compare_result = (entry1->parent_link ? -1 : +1);
1886 else if (entry1->sort_priority == entry2->sort_priority)
1888 char *name1 = getStringToLower(entry1->name_sorting);
1889 char *name2 = getStringToLower(entry2->name_sorting);
1891 compare_result = strcmp(name1, name2);
1896 else if (class_sorting1 == class_sorting2)
1897 compare_result = entry1->sort_priority - entry2->sort_priority;
1899 compare_result = class_sorting1 - class_sorting2;
1901 return compare_result;
1904 static void createParentTreeInfoNode(TreeInfo *node_parent)
1908 if (node_parent == NULL)
1911 ti_new = newTreeInfo();
1912 setTreeInfoToDefaults(ti_new, node_parent->type);
1914 ti_new->node_parent = node_parent;
1915 ti_new->parent_link = TRUE;
1918 setString(&ti_new->identifier, node_parent->identifier);
1919 setString(&ti_new->name, ".. (parent directory)");
1920 setString(&ti_new->name_sorting, ti_new->name);
1922 setString(&ti_new->subdir, "..");
1923 setString(&ti_new->fullpath, node_parent->fullpath);
1925 ti_new->sort_priority = node_parent->sort_priority;
1926 ti_new->latest_engine = node_parent->latest_engine;
1928 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1930 ti_new->identifier = getStringCopy(node_parent->identifier);
1931 ti_new->name = ".. (parent directory)";
1932 ti_new->name_sorting = getStringCopy(ti_new->name);
1934 ti_new->subdir = "..";
1935 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1937 ti_new->sort_priority = node_parent->sort_priority;
1938 ti_new->latest_engine = node_parent->latest_engine;
1940 ti_new->class_desc = getLevelClassDescription(ti_new);
1943 pushTreeInfo(&node_parent->node_group, ti_new);
1946 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1947 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1949 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1950 TreeInfo *node_parent,
1951 char *level_directory,
1952 char *directory_name)
1954 char *directory_path = getPath2(level_directory, directory_name);
1955 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1956 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1957 LevelDirTree *leveldir_new = NULL;
1960 if (setup_file_hash == NULL)
1962 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1964 free(directory_path);
1970 leveldir_new = newTreeInfo();
1973 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1975 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1977 leveldir_new->subdir = getStringCopy(directory_name);
1979 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1981 /* set all structure fields according to the token/value pairs */
1982 ldi = *leveldir_new;
1983 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1984 setSetupInfo(levelinfo_tokens, i,
1985 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1986 *leveldir_new = ldi;
1989 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1990 setString(&leveldir_new->name, leveldir_new->subdir);
1992 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1994 free(leveldir_new->name);
1995 leveldir_new->name = getStringCopy(leveldir_new->subdir);
1999 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2001 if (leveldir_new->identifier == NULL)
2002 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2004 if (leveldir_new->name_sorting == NULL)
2005 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2007 if (node_parent == NULL) /* top level group */
2009 leveldir_new->basepath = getStringCopy(level_directory);
2010 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2012 else /* sub level group */
2014 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2015 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2018 if (leveldir_new->levels < 1)
2019 leveldir_new->levels = 1;
2021 leveldir_new->last_level =
2022 leveldir_new->first_level + leveldir_new->levels - 1;
2025 leveldir_new->in_user_dir =
2026 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2028 leveldir_new->in_user_dir =
2029 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2033 /* adjust sort priority if user's private level directory was detected */
2034 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2035 leveldir_new->in_user_dir &&
2036 strcmp(leveldir_new->subdir, getLoginName()) == 0)
2037 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2040 leveldir_new->user_defined =
2041 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2043 leveldir_new->color = LEVELCOLOR(leveldir_new);
2045 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2047 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2050 leveldir_new->handicap_level = /* set handicap to default value */
2051 (leveldir_new->user_defined || !leveldir_new->handicap ?
2052 leveldir_new->last_level : leveldir_new->first_level);
2054 pushTreeInfo(node_first, leveldir_new);
2056 freeSetupFileHash(setup_file_hash);
2058 if (leveldir_new->level_group)
2060 /* create node to link back to current level directory */
2061 createParentTreeInfoNode(leveldir_new);
2063 /* step into sub-directory and look for more level series */
2064 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2065 leveldir_new, directory_path);
2068 free(directory_path);
2074 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2075 TreeInfo *node_parent,
2076 char *level_directory)
2079 struct dirent *dir_entry;
2080 boolean valid_entry_found = FALSE;
2082 if ((dir = opendir(level_directory)) == NULL)
2084 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2088 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2090 struct stat file_status;
2091 char *directory_name = dir_entry->d_name;
2092 char *directory_path = getPath2(level_directory, directory_name);
2094 /* skip entries for current and parent directory */
2095 if (strcmp(directory_name, ".") == 0 ||
2096 strcmp(directory_name, "..") == 0)
2098 free(directory_path);
2102 /* find out if directory entry is itself a directory */
2103 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2104 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2106 free(directory_path);
2110 free(directory_path);
2112 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2113 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2114 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2117 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2124 if (!valid_entry_found)
2126 /* check if this directory directly contains a file "levelinfo.conf" */
2127 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2128 level_directory, ".");
2131 if (!valid_entry_found)
2132 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2136 void LoadLevelInfo()
2138 InitUserLevelDirectory(getLoginName());
2140 DrawInitText("Loading level series:", 120, FC_GREEN);
2142 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2143 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2145 /* before sorting, the first entries will be from the user directory */
2146 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2148 if (leveldir_first == NULL)
2149 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2151 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2154 dumpTreeInfo(leveldir_first, 0);
2158 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2159 TreeInfo *node_parent,
2160 char *base_directory,
2161 char *directory_name, int type)
2163 char *directory_path = getPath2(base_directory, directory_name);
2164 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2165 SetupFileHash *setup_file_hash = NULL;
2166 TreeInfo *artwork_new = NULL;
2169 if (access(filename, F_OK) == 0) /* file exists */
2170 setup_file_hash = loadSetupFileHash(filename);
2172 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2175 struct dirent *dir_entry;
2176 boolean valid_file_found = FALSE;
2178 if ((dir = opendir(directory_path)) != NULL)
2180 while ((dir_entry = readdir(dir)) != NULL)
2182 char *entry_name = dir_entry->d_name;
2184 if (FileIsArtworkType(entry_name, type))
2186 valid_file_found = TRUE;
2194 if (!valid_file_found)
2196 if (strcmp(directory_name, ".") != 0)
2197 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2199 free(directory_path);
2206 artwork_new = newTreeInfo();
2209 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2211 setTreeInfoToDefaults(artwork_new, type);
2213 artwork_new->subdir = getStringCopy(directory_name);
2215 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2218 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2221 /* set all structure fields according to the token/value pairs */
2223 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2224 setSetupInfo(levelinfo_tokens, i,
2225 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2229 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2230 setString(&artwork_new->name, artwork_new->subdir);
2232 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2234 free(artwork_new->name);
2235 artwork_new->name = getStringCopy(artwork_new->subdir);
2240 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2243 if (artwork_new->identifier == NULL)
2244 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2246 if (artwork_new->name_sorting == NULL)
2247 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2250 if (node_parent == NULL) /* top level group */
2252 artwork_new->basepath = getStringCopy(base_directory);
2253 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2255 else /* sub level group */
2257 artwork_new->basepath = getStringCopy(node_parent->basepath);
2258 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2262 artwork_new->in_user_dir =
2263 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2265 artwork_new->in_user_dir =
2266 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2269 /* (may use ".sort_priority" from "setup_file_hash" above) */
2270 artwork_new->color = ARTWORKCOLOR(artwork_new);
2272 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2274 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2277 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2280 if (artwork_new->name != NULL)
2282 free(artwork_new->name);
2283 artwork_new->name = NULL;
2288 if (artwork_new->identifier != NULL)
2290 free(artwork_new->identifier);
2291 artwork_new->identifier = NULL;
2295 if (strcmp(artwork_new->subdir, ".") == 0)
2297 if (artwork_new->user_defined)
2300 setString(&artwork_new->identifier, "private");
2302 artwork_new->identifier = getStringCopy("private");
2304 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2309 setString(&artwork_new->identifier, "classic");
2311 artwork_new->identifier = getStringCopy("classic");
2313 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2316 /* set to new values after changing ".sort_priority" */
2317 artwork_new->color = ARTWORKCOLOR(artwork_new);
2319 setString(&artwork_new->class_desc,
2320 getLevelClassDescription(artwork_new));
2322 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2328 setString(&artwork_new->identifier, artwork_new->subdir);
2330 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2335 setString(&artwork_new->name, artwork_new->identifier);
2336 setString(&artwork_new->name_sorting, artwork_new->name);
2338 artwork_new->name = getStringCopy(artwork_new->identifier);
2339 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2343 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2345 pushTreeInfo(node_first, artwork_new);
2347 freeSetupFileHash(setup_file_hash);
2349 free(directory_path);
2355 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2356 TreeInfo *node_parent,
2357 char *base_directory, int type)
2360 struct dirent *dir_entry;
2361 boolean valid_entry_found = FALSE;
2363 if ((dir = opendir(base_directory)) == NULL)
2365 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2366 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2370 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2372 struct stat file_status;
2373 char *directory_name = dir_entry->d_name;
2374 char *directory_path = getPath2(base_directory, directory_name);
2376 /* skip entries for current and parent directory */
2377 if (strcmp(directory_name, ".") == 0 ||
2378 strcmp(directory_name, "..") == 0)
2380 free(directory_path);
2384 /* find out if directory entry is itself a directory */
2385 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2386 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2388 free(directory_path);
2392 free(directory_path);
2394 /* check if this directory contains artwork with or without config file */
2395 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2397 directory_name, type);
2402 /* check if this directory directly contains artwork itself */
2403 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2404 base_directory, ".",
2406 if (!valid_entry_found)
2407 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2411 static TreeInfo *getDummyArtworkInfo(int type)
2413 /* this is only needed when there is completely no artwork available */
2414 TreeInfo *artwork_new = newTreeInfo();
2416 setTreeInfoToDefaults(artwork_new, type);
2419 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2420 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2421 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2423 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2424 setString(&artwork_new->name, UNDEFINED_FILENAME);
2425 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2427 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2428 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2429 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2431 checked_free(artwork_new->name);
2433 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2434 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2435 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2441 void LoadArtworkInfo()
2443 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2445 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2446 options.graphics_directory,
2447 TREE_TYPE_GRAPHICS_DIR);
2448 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2449 getUserGraphicsDir(),
2450 TREE_TYPE_GRAPHICS_DIR);
2452 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2453 options.sounds_directory,
2454 TREE_TYPE_SOUNDS_DIR);
2455 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2457 TREE_TYPE_SOUNDS_DIR);
2459 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2460 options.music_directory,
2461 TREE_TYPE_MUSIC_DIR);
2462 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2464 TREE_TYPE_MUSIC_DIR);
2466 if (artwork.gfx_first == NULL)
2467 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2468 if (artwork.snd_first == NULL)
2469 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2470 if (artwork.mus_first == NULL)
2471 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2473 /* before sorting, the first entries will be from the user directory */
2474 artwork.gfx_current =
2475 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2476 if (artwork.gfx_current == NULL)
2477 artwork.gfx_current =
2478 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2479 if (artwork.gfx_current == NULL)
2480 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2482 artwork.snd_current =
2483 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2484 if (artwork.snd_current == NULL)
2485 artwork.snd_current =
2486 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2487 if (artwork.snd_current == NULL)
2488 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2490 artwork.mus_current =
2491 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2492 if (artwork.mus_current == NULL)
2493 artwork.mus_current =
2494 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2495 if (artwork.mus_current == NULL)
2496 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2498 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2499 artwork.snd_current_identifier = artwork.snd_current->identifier;
2500 artwork.mus_current_identifier = artwork.mus_current->identifier;
2503 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2504 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2505 printf("music set == %s\n\n", artwork.mus_current_identifier);
2508 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2509 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2510 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2513 dumpTreeInfo(artwork.gfx_first, 0);
2514 dumpTreeInfo(artwork.snd_first, 0);
2515 dumpTreeInfo(artwork.mus_first, 0);
2519 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2520 LevelDirTree *level_node)
2522 /* recursively check all level directories for artwork sub-directories */
2526 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2527 ARTWORK_DIRECTORY((*artwork_node)->type));
2530 if (!level_node->parent_link)
2531 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2532 level_node->subdir, level_node->name);
2535 if (!level_node->parent_link)
2537 TreeInfo *topnode_last = *artwork_node;
2539 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2540 (*artwork_node)->type);
2542 if (topnode_last != *artwork_node)
2544 free((*artwork_node)->identifier);
2545 free((*artwork_node)->name);
2546 free((*artwork_node)->name_sorting);
2548 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2549 (*artwork_node)->name = getStringCopy(level_node->name);
2550 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2552 (*artwork_node)->sort_priority = level_node->sort_priority;
2553 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2559 if (level_node->node_group != NULL)
2560 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2562 level_node = level_node->next;
2566 void LoadLevelArtworkInfo()
2568 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2570 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2571 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2572 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2574 /* needed for reloading level artwork not known at ealier stage */
2576 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2578 artwork.gfx_current =
2579 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2580 if (artwork.gfx_current == NULL)
2581 artwork.gfx_current =
2582 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2583 if (artwork.gfx_current == NULL)
2584 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2587 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2589 artwork.snd_current =
2590 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2591 if (artwork.snd_current == NULL)
2592 artwork.snd_current =
2593 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2594 if (artwork.snd_current == NULL)
2595 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2598 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2600 artwork.mus_current =
2601 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2602 if (artwork.mus_current == NULL)
2603 artwork.mus_current =
2604 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2605 if (artwork.mus_current == NULL)
2606 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2609 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2610 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2611 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2614 dumpTreeInfo(artwork.gfx_first, 0);
2615 dumpTreeInfo(artwork.snd_first, 0);
2616 dumpTreeInfo(artwork.mus_first, 0);
2620 static void SaveUserLevelInfo()
2622 LevelDirTree *level_info;
2627 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2629 if (!(file = fopen(filename, MODE_WRITE)))
2631 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2636 level_info = newTreeInfo();
2638 /* always start with reliable default values */
2639 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2642 setString(&level_info->name, getLoginName());
2643 setString(&level_info->author, getRealName());
2644 level_info->levels = 100;
2645 level_info->first_level = 1;
2647 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2648 level_info->readonly = FALSE;
2649 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2650 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2651 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2654 ldi.name = getStringCopy(getLoginName());
2655 ldi.author = getStringCopy(getRealName());
2657 ldi.first_level = 1;
2658 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2659 ldi.readonly = FALSE;
2660 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2661 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2662 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2665 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2667 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2668 getCookie("LEVELINFO")));
2671 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2674 if (i == LEVELINFO_TOKEN_NAME ||
2675 i == LEVELINFO_TOKEN_AUTHOR ||
2676 i == LEVELINFO_TOKEN_LEVELS ||
2677 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2678 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2680 /* just to make things nicer :) */
2681 if (i == LEVELINFO_TOKEN_AUTHOR)
2682 fprintf(file, "\n");
2684 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2685 i != LEVELINFO_TOKEN_NAME_SORTING &&
2686 i != LEVELINFO_TOKEN_IMPORTED_FROM &&
2687 i != LEVELINFO_TOKEN_IMPORTED_BY &&
2688 i != LEVELINFO_TOKEN_FILENAME &&
2689 i != LEVELINFO_TOKEN_FILETYPE)
2690 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2694 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2698 SetFilePermissions(filename, PERMS_PRIVATE);
2700 freeTreeInfo(level_info);
2704 char *getSetupValue(int type, void *value)
2706 static char value_string[MAX_LINE_LEN];
2714 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2718 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2722 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2726 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2730 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2734 sprintf(value_string, "%d", *(int *)value);
2738 strcpy(value_string, *(char **)value);
2742 value_string[0] = '\0';
2746 return value_string;
2749 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2753 static char token_string[MAX_LINE_LEN];
2754 int token_type = token_info[token_nr].type;
2755 void *setup_value = token_info[token_nr].value;
2756 char *token_text = token_info[token_nr].text;
2757 char *value_string = getSetupValue(token_type, setup_value);
2759 /* build complete token string */
2760 sprintf(token_string, "%s%s", prefix, token_text);
2762 /* build setup entry line */
2763 line = getFormattedSetupEntry(token_string, value_string);
2765 if (token_type == TYPE_KEY_X11)
2767 Key key = *(Key *)setup_value;
2768 char *keyname = getKeyNameFromKey(key);
2770 /* add comment, if useful */
2771 if (strcmp(keyname, "(undefined)") != 0 &&
2772 strcmp(keyname, "(unknown)") != 0)
2774 /* add at least one whitespace */
2776 for (i = strlen(line); i < token_comment_position; i++)
2780 strcat(line, keyname);
2787 void LoadLevelSetup_LastSeries()
2789 /* ----------------------------------------------------------------------- */
2790 /* ~/.<program>/levelsetup.conf */
2791 /* ----------------------------------------------------------------------- */
2793 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2794 SetupFileHash *level_setup_hash = NULL;
2796 /* always start with reliable default values */
2797 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2799 if ((level_setup_hash = loadSetupFileHash(filename)))
2801 char *last_level_series =
2802 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2804 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2806 if (leveldir_current == NULL)
2807 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2809 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2811 freeSetupFileHash(level_setup_hash);
2814 Error(ERR_WARN, "using default setup values");
2819 void SaveLevelSetup_LastSeries()
2821 /* ----------------------------------------------------------------------- */
2822 /* ~/.<program>/levelsetup.conf */
2823 /* ----------------------------------------------------------------------- */
2825 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2826 char *level_subdir = leveldir_current->subdir;
2829 InitUserDataDirectory();
2831 if (!(file = fopen(filename, MODE_WRITE)))
2833 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2838 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2839 getCookie("LEVELSETUP")));
2840 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2845 SetFilePermissions(filename, PERMS_PRIVATE);
2850 static void checkSeriesInfo()
2852 static char *level_directory = NULL;
2854 struct dirent *dir_entry;
2856 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2858 level_directory = getPath2((leveldir_current->in_user_dir ?
2859 getUserLevelDir(NULL) :
2860 options.level_directory),
2861 leveldir_current->fullpath);
2863 if ((dir = opendir(level_directory)) == NULL)
2865 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2869 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2871 if (strlen(dir_entry->d_name) > 4 &&
2872 dir_entry->d_name[3] == '.' &&
2873 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2875 char levelnum_str[4];
2878 strncpy(levelnum_str, dir_entry->d_name, 3);
2879 levelnum_str[3] = '\0';
2881 levelnum_value = atoi(levelnum_str);
2884 if (levelnum_value < leveldir_current->first_level)
2886 Error(ERR_WARN, "additional level %d found", levelnum_value);
2887 leveldir_current->first_level = levelnum_value;
2889 else if (levelnum_value > leveldir_current->last_level)
2891 Error(ERR_WARN, "additional level %d found", levelnum_value);
2892 leveldir_current->last_level = levelnum_value;
2901 void LoadLevelSetup_SeriesInfo()
2904 SetupFileHash *level_setup_hash = NULL;
2905 char *level_subdir = leveldir_current->subdir;
2907 /* always start with reliable default values */
2908 level_nr = leveldir_current->first_level;
2910 checkSeriesInfo(leveldir_current);
2912 /* ----------------------------------------------------------------------- */
2913 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2914 /* ----------------------------------------------------------------------- */
2916 level_subdir = leveldir_current->subdir;
2918 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2920 if ((level_setup_hash = loadSetupFileHash(filename)))
2924 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2928 level_nr = atoi(token_value);
2930 if (level_nr < leveldir_current->first_level)
2931 level_nr = leveldir_current->first_level;
2932 if (level_nr > leveldir_current->last_level)
2933 level_nr = leveldir_current->last_level;
2936 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2940 int level_nr = atoi(token_value);
2942 if (level_nr < leveldir_current->first_level)
2943 level_nr = leveldir_current->first_level;
2944 if (level_nr > leveldir_current->last_level + 1)
2945 level_nr = leveldir_current->last_level;
2947 if (leveldir_current->user_defined || !leveldir_current->handicap)
2948 level_nr = leveldir_current->last_level;
2950 leveldir_current->handicap_level = level_nr;
2953 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2955 freeSetupFileHash(level_setup_hash);
2958 Error(ERR_WARN, "using default setup values");
2963 void SaveLevelSetup_SeriesInfo()
2966 char *level_subdir = leveldir_current->subdir;
2967 char *level_nr_str = int2str(level_nr, 0);
2968 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2971 /* ----------------------------------------------------------------------- */
2972 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2973 /* ----------------------------------------------------------------------- */
2975 InitLevelSetupDirectory(level_subdir);
2977 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2979 if (!(file = fopen(filename, MODE_WRITE)))
2981 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2986 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2987 getCookie("LEVELSETUP")));
2988 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2990 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2991 handicap_level_str));
2995 SetFilePermissions(filename, PERMS_PRIVATE);