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_PTR(leveldir_current, ti->type);
306 artwork_set_ptr = LEVELDIR_ARTWORK_SET_PTR( 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 (!fileExists(getUserLevelDir(level_subdir)))
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 (!fileExists(dir))
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;
1420 if (!(file = fopen(filename, MODE_READ)))
1422 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1428 setup_file_data = newSetupFileHash();
1430 insert_ptr = setup_file_data = newSetupFileList("", "");
1434 /* read next line of input file */
1435 if (!fgets(line, MAX_LINE_LEN, file))
1438 /* cut trailing newline or carriage return */
1439 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1440 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1443 if (read_continued_line)
1445 /* cut leading whitespaces from input line */
1446 for (line_ptr = line; *line_ptr; line_ptr++)
1447 if (*line_ptr != ' ' && *line_ptr != '\t')
1450 /* append new line to existing line, if there is enough space */
1451 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1452 strcat(previous_line, line_ptr);
1454 strcpy(line, previous_line); /* copy storage buffer to line */
1456 read_continued_line = FALSE;
1459 /* if the last character is '\', continue at next line */
1460 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1462 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1463 strcpy(previous_line, line); /* copy line to storage buffer */
1465 read_continued_line = TRUE;
1470 /* cut trailing comment from input line */
1471 for (line_ptr = line; *line_ptr; line_ptr++)
1473 if (*line_ptr == '#')
1480 /* cut trailing whitespaces from input line */
1481 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1482 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1485 /* ignore empty lines */
1489 /* cut leading whitespaces from token */
1490 for (token = line; *token; token++)
1491 if (*token != ' ' && *token != '\t')
1494 /* start with empty value as reliable default */
1497 /* find end of token to determine start of value */
1498 for (line_ptr = token; *line_ptr; line_ptr++)
1500 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1502 *line_ptr = '\0'; /* terminate token string */
1503 value = line_ptr + 1; /* set beginning of value */
1509 /* cut leading whitespaces from value */
1510 for (; *value; value++)
1511 if (*value != ' ' && *value != '\t')
1516 value = "true"; /* treat tokens without value as "true" */
1522 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1524 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1532 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1533 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1537 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1538 SetupFileList *first_valid_list_entry = setup_file_list->next;
1540 /* free empty list header */
1541 setup_file_list->next = NULL;
1542 freeSetupFileList(setup_file_list);
1543 setup_file_data = first_valid_list_entry;
1545 if (first_valid_list_entry == NULL)
1546 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1549 return setup_file_data;
1552 SetupFileList *loadSetupFileList(char *filename)
1554 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1557 SetupFileHash *loadSetupFileHash(char *filename)
1559 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1562 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1565 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1568 Error(ERR_WARN, "configuration file has no file identifier");
1569 else if (!checkCookieString(value, identifier))
1570 Error(ERR_WARN, "configuration file has wrong file identifier");
1574 /* ========================================================================= */
1575 /* setup file stuff */
1576 /* ========================================================================= */
1578 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1579 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1580 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1582 /* level directory info */
1583 #define LEVELINFO_TOKEN_IDENTIFIER 0
1584 #define LEVELINFO_TOKEN_NAME 1
1585 #define LEVELINFO_TOKEN_NAME_SORTING 2
1586 #define LEVELINFO_TOKEN_AUTHOR 3
1587 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1588 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1589 #define LEVELINFO_TOKEN_LEVELS 6
1590 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1591 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1592 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1593 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1594 #define LEVELINFO_TOKEN_READONLY 11
1595 #define LEVELINFO_TOKEN_GRAPHICS_SET 12
1596 #define LEVELINFO_TOKEN_SOUNDS_SET 13
1597 #define LEVELINFO_TOKEN_MUSIC_SET 14
1598 #define LEVELINFO_TOKEN_FILENAME 15
1599 #define LEVELINFO_TOKEN_FILETYPE 16
1600 #define LEVELINFO_TOKEN_HANDICAP 17
1601 #define LEVELINFO_TOKEN_SKIP_LEVELS 18
1603 #define NUM_LEVELINFO_TOKENS 19
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_STRING, &ldi.imported_by, "imported_by" },
1616 { TYPE_INTEGER, &ldi.levels, "levels" },
1617 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1618 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1619 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1620 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1621 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1622 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1623 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1624 { TYPE_STRING, &ldi.music_set, "music_set" },
1625 { TYPE_STRING, &ldi.level_filename, "filename" },
1626 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1627 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1628 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1631 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1635 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1636 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1637 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1638 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1641 ldi->node_parent = NULL;
1642 ldi->node_group = NULL;
1646 ldi->cl_cursor = -1;
1649 ldi->fullpath = NULL;
1650 ldi->basepath = NULL;
1651 ldi->identifier = NULL;
1652 ldi->name = getStringCopy(ANONYMOUS_NAME);
1653 ldi->name_sorting = NULL;
1654 ldi->author = getStringCopy(ANONYMOUS_NAME);
1656 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1657 ldi->latest_engine = FALSE; /* default: get from level */
1658 ldi->parent_link = FALSE;
1659 ldi->in_user_dir = FALSE;
1660 ldi->user_defined = FALSE;
1662 ldi->class_desc = NULL;
1664 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1666 ldi->imported_from = NULL;
1667 ldi->imported_by = NULL;
1669 ldi->graphics_set = NULL;
1670 ldi->sounds_set = NULL;
1671 ldi->music_set = NULL;
1672 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1673 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1674 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1676 ldi->level_filename = NULL;
1677 ldi->level_filetype = NULL;
1680 ldi->first_level = 0;
1681 ldi->last_level = 0;
1682 ldi->level_group = FALSE;
1683 ldi->handicap_level = 0;
1684 ldi->readonly = TRUE;
1685 ldi->handicap = TRUE;
1686 ldi->skip_levels = FALSE;
1690 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1694 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1696 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1702 /* copy all values from the parent structure */
1704 ldi->type = parent->type;
1706 ldi->node_top = parent->node_top;
1707 ldi->node_parent = parent;
1708 ldi->node_group = NULL;
1712 ldi->cl_cursor = -1;
1715 ldi->fullpath = NULL;
1716 ldi->basepath = NULL;
1717 ldi->identifier = NULL;
1718 ldi->name = getStringCopy(ANONYMOUS_NAME);
1719 ldi->name_sorting = NULL;
1720 ldi->author = getStringCopy(parent->author);
1722 ldi->sort_priority = parent->sort_priority;
1723 ldi->latest_engine = parent->latest_engine;
1724 ldi->parent_link = FALSE;
1725 ldi->in_user_dir = parent->in_user_dir;
1726 ldi->user_defined = parent->user_defined;
1727 ldi->color = parent->color;
1728 ldi->class_desc = getStringCopy(parent->class_desc);
1730 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1732 ldi->imported_from = getStringCopy(parent->imported_from);
1733 ldi->imported_by = getStringCopy(parent->imported_by);
1735 ldi->graphics_set = NULL;
1736 ldi->sounds_set = NULL;
1737 ldi->music_set = NULL;
1738 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1739 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1740 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1742 ldi->level_filename = NULL;
1743 ldi->level_filetype = NULL;
1746 ldi->first_level = 0;
1747 ldi->last_level = 0;
1748 ldi->level_group = FALSE;
1749 ldi->handicap_level = 0;
1750 ldi->readonly = TRUE;
1751 ldi->handicap = TRUE;
1752 ldi->skip_levels = FALSE;
1757 /* first copy all values from the parent structure ... */
1760 /* ... then set all fields to default that cannot be inherited from parent.
1761 This is especially important for all those fields that can be set from
1762 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1763 calls 'free()' for all already set token values which requires that no
1764 other structure's pointer may point to them!
1768 ldi->fullpath = NULL;
1769 ldi->basepath = NULL;
1770 ldi->identifier = NULL;
1771 ldi->name = getStringCopy(ANONYMOUS_NAME);
1772 ldi->name_sorting = NULL;
1773 ldi->author = getStringCopy(parent->author);
1775 ldi->imported_from = getStringCopy(parent->imported_from);
1776 ldi->imported_by = getStringCopy(parent->imported_by);
1777 ldi->class_desc = getStringCopy(parent->class_desc);
1779 ldi->graphics_set = NULL;
1780 ldi->sounds_set = NULL;
1781 ldi->music_set = NULL;
1782 ldi->graphics_path = NULL;
1783 ldi->sounds_path = NULL;
1784 ldi->music_path = NULL;
1786 ldi->level_group = FALSE;
1787 ldi->parent_link = FALSE;
1789 ldi->node_top = parent->node_top;
1790 ldi->node_parent = parent;
1791 ldi->node_group = NULL;
1797 static void freeTreeInfo(TreeInfo *ldi)
1799 checked_free(ldi->subdir);
1800 checked_free(ldi->fullpath);
1801 checked_free(ldi->basepath);
1802 checked_free(ldi->identifier);
1804 checked_free(ldi->name);
1805 checked_free(ldi->name_sorting);
1806 checked_free(ldi->author);
1808 checked_free(ldi->class_desc);
1810 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1812 checked_free(ldi->imported_from);
1813 checked_free(ldi->imported_by);
1815 checked_free(ldi->graphics_set);
1816 checked_free(ldi->sounds_set);
1817 checked_free(ldi->music_set);
1819 checked_free(ldi->graphics_path);
1820 checked_free(ldi->sounds_path);
1821 checked_free(ldi->music_path);
1823 checked_free(ldi->level_filename);
1824 checked_free(ldi->level_filetype);
1828 void setSetupInfo(struct TokenInfo *token_info,
1829 int token_nr, char *token_value)
1831 int token_type = token_info[token_nr].type;
1832 void *setup_value = token_info[token_nr].value;
1834 if (token_value == NULL)
1837 /* set setup field to corresponding token value */
1842 *(boolean *)setup_value = get_boolean_from_string(token_value);
1846 *(Key *)setup_value = getKeyFromKeyName(token_value);
1850 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1854 *(int *)setup_value = get_integer_from_string(token_value);
1858 checked_free(*(char **)setup_value);
1859 *(char **)setup_value = getStringCopy(token_value);
1867 static int compareTreeInfoEntries(const void *object1, const void *object2)
1869 const TreeInfo *entry1 = *((TreeInfo **)object1);
1870 const TreeInfo *entry2 = *((TreeInfo **)object2);
1871 int class_sorting1, class_sorting2;
1874 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1876 class_sorting1 = LEVELSORTING(entry1);
1877 class_sorting2 = LEVELSORTING(entry2);
1881 class_sorting1 = ARTWORKSORTING(entry1);
1882 class_sorting2 = ARTWORKSORTING(entry2);
1885 if (entry1->parent_link || entry2->parent_link)
1886 compare_result = (entry1->parent_link ? -1 : +1);
1887 else if (entry1->sort_priority == entry2->sort_priority)
1889 char *name1 = getStringToLower(entry1->name_sorting);
1890 char *name2 = getStringToLower(entry2->name_sorting);
1892 compare_result = strcmp(name1, name2);
1897 else if (class_sorting1 == class_sorting2)
1898 compare_result = entry1->sort_priority - entry2->sort_priority;
1900 compare_result = class_sorting1 - class_sorting2;
1902 return compare_result;
1905 static void createParentTreeInfoNode(TreeInfo *node_parent)
1909 if (node_parent == NULL)
1912 ti_new = newTreeInfo();
1913 setTreeInfoToDefaults(ti_new, node_parent->type);
1915 ti_new->node_parent = node_parent;
1916 ti_new->parent_link = TRUE;
1919 setString(&ti_new->identifier, node_parent->identifier);
1920 setString(&ti_new->name, ".. (parent directory)");
1921 setString(&ti_new->name_sorting, ti_new->name);
1923 setString(&ti_new->subdir, "..");
1924 setString(&ti_new->fullpath, node_parent->fullpath);
1926 ti_new->sort_priority = node_parent->sort_priority;
1927 ti_new->latest_engine = node_parent->latest_engine;
1929 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1931 ti_new->identifier = getStringCopy(node_parent->identifier);
1932 ti_new->name = ".. (parent directory)";
1933 ti_new->name_sorting = getStringCopy(ti_new->name);
1935 ti_new->subdir = "..";
1936 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1938 ti_new->sort_priority = node_parent->sort_priority;
1939 ti_new->latest_engine = node_parent->latest_engine;
1941 ti_new->class_desc = getLevelClassDescription(ti_new);
1944 pushTreeInfo(&node_parent->node_group, ti_new);
1947 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1948 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1950 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1951 TreeInfo *node_parent,
1952 char *level_directory,
1953 char *directory_name)
1955 char *directory_path = getPath2(level_directory, directory_name);
1956 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1957 SetupFileHash *setup_file_hash;
1958 LevelDirTree *leveldir_new = NULL;
1961 /* unless debugging, silently ignore directories without "levelinfo.conf" */
1962 if (!options.debug && !fileExists(filename))
1964 free(directory_path);
1970 setup_file_hash = loadSetupFileHash(filename);
1972 if (setup_file_hash == NULL)
1974 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1976 free(directory_path);
1982 leveldir_new = newTreeInfo();
1985 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1987 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1989 leveldir_new->subdir = getStringCopy(directory_name);
1991 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1993 /* set all structure fields according to the token/value pairs */
1994 ldi = *leveldir_new;
1995 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1996 setSetupInfo(levelinfo_tokens, i,
1997 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1998 *leveldir_new = ldi;
2001 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2002 setString(&leveldir_new->name, leveldir_new->subdir);
2004 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2006 free(leveldir_new->name);
2007 leveldir_new->name = getStringCopy(leveldir_new->subdir);
2011 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2013 if (leveldir_new->identifier == NULL)
2014 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2016 if (leveldir_new->name_sorting == NULL)
2017 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2019 if (node_parent == NULL) /* top level group */
2021 leveldir_new->basepath = getStringCopy(level_directory);
2022 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2024 else /* sub level group */
2026 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2027 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2030 if (leveldir_new->levels < 1)
2031 leveldir_new->levels = 1;
2033 leveldir_new->last_level =
2034 leveldir_new->first_level + leveldir_new->levels - 1;
2037 leveldir_new->in_user_dir =
2038 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2040 leveldir_new->in_user_dir =
2041 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2045 /* adjust sort priority if user's private level directory was detected */
2046 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2047 leveldir_new->in_user_dir &&
2048 strcmp(leveldir_new->subdir, getLoginName()) == 0)
2049 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2052 leveldir_new->user_defined =
2053 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2055 leveldir_new->color = LEVELCOLOR(leveldir_new);
2057 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2059 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2062 leveldir_new->handicap_level = /* set handicap to default value */
2063 (leveldir_new->user_defined || !leveldir_new->handicap ?
2064 leveldir_new->last_level : leveldir_new->first_level);
2066 pushTreeInfo(node_first, leveldir_new);
2068 freeSetupFileHash(setup_file_hash);
2070 if (leveldir_new->level_group)
2072 /* create node to link back to current level directory */
2073 createParentTreeInfoNode(leveldir_new);
2075 /* step into sub-directory and look for more level series */
2076 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2077 leveldir_new, directory_path);
2080 free(directory_path);
2086 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2087 TreeInfo *node_parent,
2088 char *level_directory)
2091 struct dirent *dir_entry;
2092 boolean valid_entry_found = FALSE;
2094 if ((dir = opendir(level_directory)) == NULL)
2096 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2100 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2102 struct stat file_status;
2103 char *directory_name = dir_entry->d_name;
2104 char *directory_path = getPath2(level_directory, directory_name);
2106 /* skip entries for current and parent directory */
2107 if (strcmp(directory_name, ".") == 0 ||
2108 strcmp(directory_name, "..") == 0)
2110 free(directory_path);
2114 /* find out if directory entry is itself a directory */
2115 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2116 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2118 free(directory_path);
2122 free(directory_path);
2124 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2125 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2126 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2129 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2136 if (!valid_entry_found)
2138 /* check if this directory directly contains a file "levelinfo.conf" */
2139 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2140 level_directory, ".");
2143 if (!valid_entry_found)
2144 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2148 void LoadLevelInfo()
2150 InitUserLevelDirectory(getLoginName());
2152 DrawInitText("Loading level series:", 120, FC_GREEN);
2154 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2155 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2157 /* before sorting, the first entries will be from the user directory */
2158 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2160 if (leveldir_first == NULL)
2161 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2163 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2166 dumpTreeInfo(leveldir_first, 0);
2170 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2171 TreeInfo *node_parent,
2172 char *base_directory,
2173 char *directory_name, int type)
2175 char *directory_path = getPath2(base_directory, directory_name);
2176 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2177 SetupFileHash *setup_file_hash = NULL;
2178 TreeInfo *artwork_new = NULL;
2181 if (fileExists(filename))
2182 setup_file_hash = loadSetupFileHash(filename);
2184 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2187 struct dirent *dir_entry;
2188 boolean valid_file_found = FALSE;
2190 if ((dir = opendir(directory_path)) != NULL)
2192 while ((dir_entry = readdir(dir)) != NULL)
2194 char *entry_name = dir_entry->d_name;
2196 if (FileIsArtworkType(entry_name, type))
2198 valid_file_found = TRUE;
2206 if (!valid_file_found)
2208 if (strcmp(directory_name, ".") != 0)
2209 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2211 free(directory_path);
2218 artwork_new = newTreeInfo();
2221 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2223 setTreeInfoToDefaults(artwork_new, type);
2225 artwork_new->subdir = getStringCopy(directory_name);
2227 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2230 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2233 /* set all structure fields according to the token/value pairs */
2235 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2236 setSetupInfo(levelinfo_tokens, i,
2237 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2241 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2242 setString(&artwork_new->name, artwork_new->subdir);
2244 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2246 free(artwork_new->name);
2247 artwork_new->name = getStringCopy(artwork_new->subdir);
2252 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2255 if (artwork_new->identifier == NULL)
2256 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2258 if (artwork_new->name_sorting == NULL)
2259 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2262 if (node_parent == NULL) /* top level group */
2264 artwork_new->basepath = getStringCopy(base_directory);
2265 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2267 else /* sub level group */
2269 artwork_new->basepath = getStringCopy(node_parent->basepath);
2270 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2274 artwork_new->in_user_dir =
2275 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2277 artwork_new->in_user_dir =
2278 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2281 /* (may use ".sort_priority" from "setup_file_hash" above) */
2282 artwork_new->color = ARTWORKCOLOR(artwork_new);
2284 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2286 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2289 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2292 if (artwork_new->name != NULL)
2294 free(artwork_new->name);
2295 artwork_new->name = NULL;
2300 if (artwork_new->identifier != NULL)
2302 free(artwork_new->identifier);
2303 artwork_new->identifier = NULL;
2307 if (strcmp(artwork_new->subdir, ".") == 0)
2309 if (artwork_new->user_defined)
2312 setString(&artwork_new->identifier, "private");
2314 artwork_new->identifier = getStringCopy("private");
2316 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2321 setString(&artwork_new->identifier, "classic");
2323 artwork_new->identifier = getStringCopy("classic");
2325 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2328 /* set to new values after changing ".sort_priority" */
2329 artwork_new->color = ARTWORKCOLOR(artwork_new);
2331 setString(&artwork_new->class_desc,
2332 getLevelClassDescription(artwork_new));
2334 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2340 setString(&artwork_new->identifier, artwork_new->subdir);
2342 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2347 setString(&artwork_new->name, artwork_new->identifier);
2348 setString(&artwork_new->name_sorting, artwork_new->name);
2350 artwork_new->name = getStringCopy(artwork_new->identifier);
2351 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2355 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2357 pushTreeInfo(node_first, artwork_new);
2359 freeSetupFileHash(setup_file_hash);
2361 free(directory_path);
2367 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2368 TreeInfo *node_parent,
2369 char *base_directory, int type)
2372 struct dirent *dir_entry;
2373 boolean valid_entry_found = FALSE;
2375 if ((dir = opendir(base_directory)) == NULL)
2377 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2378 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2382 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2384 struct stat file_status;
2385 char *directory_name = dir_entry->d_name;
2386 char *directory_path = getPath2(base_directory, directory_name);
2388 /* skip entries for current and parent directory */
2389 if (strcmp(directory_name, ".") == 0 ||
2390 strcmp(directory_name, "..") == 0)
2392 free(directory_path);
2396 /* find out if directory entry is itself a directory */
2397 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2398 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2400 free(directory_path);
2404 free(directory_path);
2406 /* check if this directory contains artwork with or without config file */
2407 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2409 directory_name, type);
2414 /* check if this directory directly contains artwork itself */
2415 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2416 base_directory, ".",
2418 if (!valid_entry_found)
2419 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2423 static TreeInfo *getDummyArtworkInfo(int type)
2425 /* this is only needed when there is completely no artwork available */
2426 TreeInfo *artwork_new = newTreeInfo();
2428 setTreeInfoToDefaults(artwork_new, type);
2431 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2432 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2433 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2435 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2436 setString(&artwork_new->name, UNDEFINED_FILENAME);
2437 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2439 artwork_new->subdir = getStringCopy(UNDEFINED_FILENAME);
2440 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2441 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2443 checked_free(artwork_new->name);
2445 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2446 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2447 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2453 void LoadArtworkInfo()
2455 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2457 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2458 options.graphics_directory,
2459 TREE_TYPE_GRAPHICS_DIR);
2460 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2461 getUserGraphicsDir(),
2462 TREE_TYPE_GRAPHICS_DIR);
2464 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2465 options.sounds_directory,
2466 TREE_TYPE_SOUNDS_DIR);
2467 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2469 TREE_TYPE_SOUNDS_DIR);
2471 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2472 options.music_directory,
2473 TREE_TYPE_MUSIC_DIR);
2474 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2476 TREE_TYPE_MUSIC_DIR);
2478 if (artwork.gfx_first == NULL)
2479 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2480 if (artwork.snd_first == NULL)
2481 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2482 if (artwork.mus_first == NULL)
2483 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2485 /* before sorting, the first entries will be from the user directory */
2486 artwork.gfx_current =
2487 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2488 if (artwork.gfx_current == NULL)
2489 artwork.gfx_current =
2490 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2491 if (artwork.gfx_current == NULL)
2492 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2494 artwork.snd_current =
2495 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2496 if (artwork.snd_current == NULL)
2497 artwork.snd_current =
2498 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2499 if (artwork.snd_current == NULL)
2500 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2502 artwork.mus_current =
2503 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2504 if (artwork.mus_current == NULL)
2505 artwork.mus_current =
2506 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2507 if (artwork.mus_current == NULL)
2508 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2510 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2511 artwork.snd_current_identifier = artwork.snd_current->identifier;
2512 artwork.mus_current_identifier = artwork.mus_current->identifier;
2515 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2516 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2517 printf("music set == %s\n\n", artwork.mus_current_identifier);
2520 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2521 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2522 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2525 dumpTreeInfo(artwork.gfx_first, 0);
2526 dumpTreeInfo(artwork.snd_first, 0);
2527 dumpTreeInfo(artwork.mus_first, 0);
2531 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2532 LevelDirTree *level_node)
2534 /* recursively check all level directories for artwork sub-directories */
2538 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2539 ARTWORK_DIRECTORY((*artwork_node)->type));
2542 if (!level_node->parent_link)
2543 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2544 level_node->subdir, level_node->name);
2547 if (!level_node->parent_link)
2549 TreeInfo *topnode_last = *artwork_node;
2551 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2552 (*artwork_node)->type);
2554 if (topnode_last != *artwork_node)
2556 free((*artwork_node)->identifier);
2557 free((*artwork_node)->name);
2558 free((*artwork_node)->name_sorting);
2560 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2561 (*artwork_node)->name = getStringCopy(level_node->name);
2562 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2564 (*artwork_node)->sort_priority = level_node->sort_priority;
2565 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2571 if (level_node->node_group != NULL)
2572 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2574 level_node = level_node->next;
2578 void LoadLevelArtworkInfo()
2580 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2582 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2583 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2584 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2586 /* needed for reloading level artwork not known at ealier stage */
2588 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2590 artwork.gfx_current =
2591 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2592 if (artwork.gfx_current == NULL)
2593 artwork.gfx_current =
2594 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2595 if (artwork.gfx_current == NULL)
2596 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2599 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2601 artwork.snd_current =
2602 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2603 if (artwork.snd_current == NULL)
2604 artwork.snd_current =
2605 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2606 if (artwork.snd_current == NULL)
2607 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2610 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2612 artwork.mus_current =
2613 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2614 if (artwork.mus_current == NULL)
2615 artwork.mus_current =
2616 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2617 if (artwork.mus_current == NULL)
2618 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2621 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2622 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2623 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2626 dumpTreeInfo(artwork.gfx_first, 0);
2627 dumpTreeInfo(artwork.snd_first, 0);
2628 dumpTreeInfo(artwork.mus_first, 0);
2632 static void SaveUserLevelInfo()
2634 LevelDirTree *level_info;
2639 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2641 if (!(file = fopen(filename, MODE_WRITE)))
2643 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2648 level_info = newTreeInfo();
2650 /* always start with reliable default values */
2651 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2654 setString(&level_info->name, getLoginName());
2655 setString(&level_info->author, getRealName());
2656 level_info->levels = 100;
2657 level_info->first_level = 1;
2659 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2660 level_info->readonly = FALSE;
2661 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2662 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2663 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2666 ldi.name = getStringCopy(getLoginName());
2667 ldi.author = getStringCopy(getRealName());
2669 ldi.first_level = 1;
2670 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2671 ldi.readonly = FALSE;
2672 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2673 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2674 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2677 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2679 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2680 getCookie("LEVELINFO")));
2683 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2686 if (i == LEVELINFO_TOKEN_NAME ||
2687 i == LEVELINFO_TOKEN_AUTHOR ||
2688 i == LEVELINFO_TOKEN_LEVELS ||
2689 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2690 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2692 /* just to make things nicer :) */
2693 if (i == LEVELINFO_TOKEN_AUTHOR)
2694 fprintf(file, "\n");
2696 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2697 i != LEVELINFO_TOKEN_NAME_SORTING &&
2698 i != LEVELINFO_TOKEN_IMPORTED_FROM &&
2699 i != LEVELINFO_TOKEN_IMPORTED_BY &&
2700 i != LEVELINFO_TOKEN_FILENAME &&
2701 i != LEVELINFO_TOKEN_FILETYPE)
2702 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2706 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2710 SetFilePermissions(filename, PERMS_PRIVATE);
2712 freeTreeInfo(level_info);
2716 char *getSetupValue(int type, void *value)
2718 static char value_string[MAX_LINE_LEN];
2726 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2730 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2734 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2738 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2742 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2746 sprintf(value_string, "%d", *(int *)value);
2750 strcpy(value_string, *(char **)value);
2754 value_string[0] = '\0';
2758 return value_string;
2761 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2765 static char token_string[MAX_LINE_LEN];
2766 int token_type = token_info[token_nr].type;
2767 void *setup_value = token_info[token_nr].value;
2768 char *token_text = token_info[token_nr].text;
2769 char *value_string = getSetupValue(token_type, setup_value);
2771 /* build complete token string */
2772 sprintf(token_string, "%s%s", prefix, token_text);
2774 /* build setup entry line */
2775 line = getFormattedSetupEntry(token_string, value_string);
2777 if (token_type == TYPE_KEY_X11)
2779 Key key = *(Key *)setup_value;
2780 char *keyname = getKeyNameFromKey(key);
2782 /* add comment, if useful */
2783 if (strcmp(keyname, "(undefined)") != 0 &&
2784 strcmp(keyname, "(unknown)") != 0)
2786 /* add at least one whitespace */
2788 for (i = strlen(line); i < token_comment_position; i++)
2792 strcat(line, keyname);
2799 void LoadLevelSetup_LastSeries()
2801 /* ----------------------------------------------------------------------- */
2802 /* ~/.<program>/levelsetup.conf */
2803 /* ----------------------------------------------------------------------- */
2805 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2806 SetupFileHash *level_setup_hash = NULL;
2808 /* always start with reliable default values */
2809 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2811 if ((level_setup_hash = loadSetupFileHash(filename)))
2813 char *last_level_series =
2814 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2816 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2818 if (leveldir_current == NULL)
2819 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2821 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2823 freeSetupFileHash(level_setup_hash);
2826 Error(ERR_WARN, "using default setup values");
2831 void SaveLevelSetup_LastSeries()
2833 /* ----------------------------------------------------------------------- */
2834 /* ~/.<program>/levelsetup.conf */
2835 /* ----------------------------------------------------------------------- */
2837 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2838 char *level_subdir = leveldir_current->subdir;
2841 InitUserDataDirectory();
2843 if (!(file = fopen(filename, MODE_WRITE)))
2845 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2850 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2851 getCookie("LEVELSETUP")));
2852 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2857 SetFilePermissions(filename, PERMS_PRIVATE);
2862 static void checkSeriesInfo()
2864 static char *level_directory = NULL;
2866 struct dirent *dir_entry;
2868 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2870 level_directory = getPath2((leveldir_current->in_user_dir ?
2871 getUserLevelDir(NULL) :
2872 options.level_directory),
2873 leveldir_current->fullpath);
2875 if ((dir = opendir(level_directory)) == NULL)
2877 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2881 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2883 if (strlen(dir_entry->d_name) > 4 &&
2884 dir_entry->d_name[3] == '.' &&
2885 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2887 char levelnum_str[4];
2890 strncpy(levelnum_str, dir_entry->d_name, 3);
2891 levelnum_str[3] = '\0';
2893 levelnum_value = atoi(levelnum_str);
2896 if (levelnum_value < leveldir_current->first_level)
2898 Error(ERR_WARN, "additional level %d found", levelnum_value);
2899 leveldir_current->first_level = levelnum_value;
2901 else if (levelnum_value > leveldir_current->last_level)
2903 Error(ERR_WARN, "additional level %d found", levelnum_value);
2904 leveldir_current->last_level = levelnum_value;
2913 void LoadLevelSetup_SeriesInfo()
2916 SetupFileHash *level_setup_hash = NULL;
2917 char *level_subdir = leveldir_current->subdir;
2919 /* always start with reliable default values */
2920 level_nr = leveldir_current->first_level;
2922 checkSeriesInfo(leveldir_current);
2924 /* ----------------------------------------------------------------------- */
2925 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2926 /* ----------------------------------------------------------------------- */
2928 level_subdir = leveldir_current->subdir;
2930 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2932 if ((level_setup_hash = loadSetupFileHash(filename)))
2936 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2940 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)
2945 level_nr = leveldir_current->last_level;
2948 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2952 int level_nr = atoi(token_value);
2954 if (level_nr < leveldir_current->first_level)
2955 level_nr = leveldir_current->first_level;
2956 if (level_nr > leveldir_current->last_level + 1)
2957 level_nr = leveldir_current->last_level;
2959 if (leveldir_current->user_defined || !leveldir_current->handicap)
2960 level_nr = leveldir_current->last_level;
2962 leveldir_current->handicap_level = level_nr;
2965 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2967 freeSetupFileHash(level_setup_hash);
2970 Error(ERR_WARN, "using default setup values");
2975 void SaveLevelSetup_SeriesInfo()
2978 char *level_subdir = leveldir_current->subdir;
2979 char *level_nr_str = int2str(level_nr, 0);
2980 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2983 /* ----------------------------------------------------------------------- */
2984 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2985 /* ----------------------------------------------------------------------- */
2987 InitLevelSetupDirectory(level_subdir);
2989 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2991 if (!(file = fopen(filename, MODE_WRITE)))
2993 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2998 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2999 getCookie("LEVELSETUP")));
3000 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3002 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3003 handicap_level_str));
3007 SetFilePermissions(filename, PERMS_PRIVATE);