1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
27 #define NUM_LEVELCLASS_DESC 8
29 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
42 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
43 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
44 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
49 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
52 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
53 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
54 IS_LEVELCLASS_BD(n) ? 2 : \
55 IS_LEVELCLASS_EM(n) ? 3 : \
56 IS_LEVELCLASS_SP(n) ? 4 : \
57 IS_LEVELCLASS_DX(n) ? 5 : \
58 IS_LEVELCLASS_CONTRIB(n) ? 6 : \
59 IS_LEVELCLASS_PRIVATE(n) ? 7 : \
62 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
63 IS_ARTWORKCLASS_CONTRIB(n) ? FC_YELLOW : \
64 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
65 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
68 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
69 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
70 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
71 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
74 #define TOKEN_VALUE_POSITION 40
75 #define TOKEN_COMMENT_POSITION 60
77 #define MAX_COOKIE_LEN 256
80 /* ------------------------------------------------------------------------- */
82 /* ------------------------------------------------------------------------- */
84 static char *getLevelClassDescription(TreeInfo *ldi)
86 int position = ldi->sort_priority / 100;
88 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
89 return levelclass_desc[position];
91 return "Unknown Level Class";
94 static char *getUserLevelDir(char *level_subdir)
96 static char *userlevel_dir = NULL;
97 char *data_dir = getUserDataDir();
98 char *userlevel_subdir = LEVELS_DIRECTORY;
100 checked_free(userlevel_dir);
102 if (level_subdir != NULL)
103 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
105 userlevel_dir = getPath2(data_dir, userlevel_subdir);
107 return userlevel_dir;
110 static char *getScoreDir(char *level_subdir)
112 static char *score_dir = NULL;
113 char *data_dir = getCommonDataDir();
114 char *score_subdir = SCORES_DIRECTORY;
116 checked_free(score_dir);
118 if (level_subdir != NULL)
119 score_dir = getPath3(data_dir, score_subdir, level_subdir);
121 score_dir = getPath2(data_dir, score_subdir);
126 static char *getLevelSetupDir(char *level_subdir)
128 static char *levelsetup_dir = NULL;
129 char *data_dir = getUserDataDir();
130 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
132 checked_free(levelsetup_dir);
134 if (level_subdir != NULL)
135 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
137 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
139 return levelsetup_dir;
142 static char *getLevelDirFromTreeInfo(TreeInfo *node)
144 static char *level_dir = NULL;
147 return options.level_directory;
149 checked_free(level_dir);
151 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
152 options.level_directory), node->fullpath);
157 char *getCurrentLevelDir()
159 return getLevelDirFromTreeInfo(leveldir_current);
162 static char *getTapeDir(char *level_subdir)
164 static char *tape_dir = NULL;
165 char *data_dir = getUserDataDir();
166 char *tape_subdir = TAPES_DIRECTORY;
168 checked_free(tape_dir);
170 if (level_subdir != NULL)
171 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
173 tape_dir = getPath2(data_dir, tape_subdir);
178 static char *getSolutionTapeDir()
180 static char *tape_dir = NULL;
181 char *data_dir = getCurrentLevelDir();
182 char *tape_subdir = TAPES_DIRECTORY;
184 checked_free(tape_dir);
186 tape_dir = getPath2(data_dir, tape_subdir);
191 static char *getDefaultGraphicsDir(char *graphics_subdir)
193 static char *graphics_dir = NULL;
195 if (graphics_subdir == NULL)
196 return options.graphics_directory;
198 checked_free(graphics_dir);
200 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
205 static char *getDefaultSoundsDir(char *sounds_subdir)
207 static char *sounds_dir = NULL;
209 if (sounds_subdir == NULL)
210 return options.sounds_directory;
212 checked_free(sounds_dir);
214 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
219 static char *getDefaultMusicDir(char *music_subdir)
221 static char *music_dir = NULL;
223 if (music_subdir == NULL)
224 return options.music_directory;
226 checked_free(music_dir);
228 music_dir = getPath2(options.music_directory, music_subdir);
233 static char *getDefaultArtworkSet(int type)
235 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
236 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
237 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
240 static char *getDefaultArtworkDir(int type)
242 return (type == TREE_TYPE_GRAPHICS_DIR ?
243 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
244 type == TREE_TYPE_SOUNDS_DIR ?
245 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
246 type == TREE_TYPE_MUSIC_DIR ?
247 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
250 static char *getUserGraphicsDir()
252 static char *usergraphics_dir = NULL;
254 if (usergraphics_dir == NULL)
255 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
257 return usergraphics_dir;
260 static char *getUserSoundsDir()
262 static char *usersounds_dir = NULL;
264 if (usersounds_dir == NULL)
265 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
267 return usersounds_dir;
270 static char *getUserMusicDir()
272 static char *usermusic_dir = NULL;
274 if (usermusic_dir == NULL)
275 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
277 return usermusic_dir;
280 static char *getSetupArtworkDir(TreeInfo *ti)
282 static char *artwork_dir = NULL;
284 checked_free(artwork_dir);
286 artwork_dir = getPath2(ti->basepath, ti->fullpath);
291 char *setLevelArtworkDir(TreeInfo *ti)
293 char **artwork_path_ptr, **artwork_set_ptr;
294 TreeInfo *level_artwork;
296 if (ti == NULL || leveldir_current == NULL)
299 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
300 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
302 checked_free(*artwork_path_ptr);
304 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
305 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
308 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
309 normally result in using the artwork configured in the setup menu. But
310 if an artwork subdirectory exists (which might contain custom artwork
311 or an artwork configuration file), this level artwork must be treated
312 as relative to the default "classic" artwork, not to the artwork that
313 is currently configured in the setup menu. */
315 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
317 checked_free(*artwork_set_ptr);
321 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
322 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
326 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
327 *artwork_set_ptr = NULL;
333 return *artwork_set_ptr;
336 inline static char *getLevelArtworkSet(int type)
338 if (leveldir_current == NULL)
341 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
344 inline static char *getLevelArtworkDir(int type)
346 if (leveldir_current == NULL)
347 return UNDEFINED_FILENAME;
349 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
352 char *getTapeFilename(int nr)
354 static char *filename = NULL;
355 char basename[MAX_FILENAME_LEN];
357 checked_free(filename);
359 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
360 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
365 char *getSolutionTapeFilename(int nr)
367 static char *filename = NULL;
368 char basename[MAX_FILENAME_LEN];
370 checked_free(filename);
372 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
373 filename = getPath2(getSolutionTapeDir(), basename);
378 char *getScoreFilename(int nr)
380 static char *filename = NULL;
381 char basename[MAX_FILENAME_LEN];
383 checked_free(filename);
385 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
386 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
391 char *getSetupFilename()
393 static char *filename = NULL;
395 checked_free(filename);
397 filename = getPath2(getSetupDir(), SETUP_FILENAME);
402 char *getEditorSetupFilename()
404 static char *filename = NULL;
406 checked_free(filename);
408 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
413 char *getHelpAnimFilename()
415 static char *filename = NULL;
417 checked_free(filename);
419 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
424 char *getHelpTextFilename()
426 static char *filename = NULL;
428 checked_free(filename);
430 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
435 char *getLevelSetInfoFilename()
437 static char *filename = NULL;
452 for (i = 0; basenames[i] != NULL; i++)
454 checked_free(filename);
456 filename = getPath2(getCurrentLevelDir(), basenames[i]);
457 if (fileExists(filename))
464 static char *getCorrectedArtworkBasename(char *basename)
466 char *basename_corrected = basename;
468 #if defined(PLATFORM_MSDOS)
469 if (program.filename_prefix != NULL)
471 int prefix_len = strlen(program.filename_prefix);
473 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
474 basename_corrected = &basename[prefix_len];
476 /* if corrected filename is still longer than standard MS-DOS filename
477 size (8 characters + 1 dot + 3 characters file extension), shorten
478 filename by writing file extension after 8th basename character */
479 if (strlen(basename_corrected) > 8 + 1 + 3)
481 static char *msdos_filename = NULL;
483 checked_free(msdos_filename);
485 msdos_filename = getStringCopy(basename_corrected);
486 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
488 basename_corrected = msdos_filename;
493 return basename_corrected;
496 char *getCustomImageFilename(char *basename)
498 static char *filename = NULL;
499 boolean skip_setup_artwork = FALSE;
501 checked_free(filename);
503 basename = getCorrectedArtworkBasename(basename);
505 if (!setup.override_level_graphics)
507 /* 1st try: look for special artwork in current level series directory */
508 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
509 if (fileExists(filename))
514 /* check if there is special artwork configured in level series config */
515 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
517 /* 2nd try: look for special artwork configured in level series config */
518 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
519 if (fileExists(filename))
524 /* take missing artwork configured in level set config from default */
525 skip_setup_artwork = TRUE;
529 if (!skip_setup_artwork)
531 /* 3rd try: look for special artwork in configured artwork directory */
532 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
533 if (fileExists(filename))
539 /* 4th try: look for default artwork in new default artwork directory */
540 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
541 if (fileExists(filename))
546 /* 5th try: look for default artwork in old default artwork directory */
547 filename = getPath2(options.graphics_directory, basename);
548 if (fileExists(filename))
551 return NULL; /* cannot find specified artwork file anywhere */
554 char *getCustomSoundFilename(char *basename)
556 static char *filename = NULL;
557 boolean skip_setup_artwork = FALSE;
559 checked_free(filename);
561 basename = getCorrectedArtworkBasename(basename);
563 if (!setup.override_level_sounds)
565 /* 1st try: look for special artwork in current level series directory */
566 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
567 if (fileExists(filename))
572 /* check if there is special artwork configured in level series config */
573 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
575 /* 2nd try: look for special artwork configured in level series config */
576 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
577 if (fileExists(filename))
582 /* take missing artwork configured in level set config from default */
583 skip_setup_artwork = TRUE;
587 if (!skip_setup_artwork)
589 /* 3rd try: look for special artwork in configured artwork directory */
590 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
591 if (fileExists(filename))
597 /* 4th try: look for default artwork in new default artwork directory */
598 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
599 if (fileExists(filename))
604 /* 5th try: look for default artwork in old default artwork directory */
605 filename = getPath2(options.sounds_directory, basename);
606 if (fileExists(filename))
609 return NULL; /* cannot find specified artwork file anywhere */
612 char *getCustomMusicFilename(char *basename)
614 static char *filename = NULL;
615 boolean skip_setup_artwork = FALSE;
617 checked_free(filename);
619 basename = getCorrectedArtworkBasename(basename);
621 if (!setup.override_level_music)
623 /* 1st try: look for special artwork in current level series directory */
624 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
625 if (fileExists(filename))
630 /* check if there is special artwork configured in level series config */
631 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
633 /* 2nd try: look for special artwork configured in level series config */
634 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
635 if (fileExists(filename))
640 /* take missing artwork configured in level set config from default */
641 skip_setup_artwork = TRUE;
645 if (!skip_setup_artwork)
647 /* 3rd try: look for special artwork in configured artwork directory */
648 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
649 if (fileExists(filename))
655 /* 4th try: look for default artwork in new default artwork directory */
656 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
657 if (fileExists(filename))
662 /* 5th try: look for default artwork in old default artwork directory */
663 filename = getPath2(options.music_directory, basename);
664 if (fileExists(filename))
667 return NULL; /* cannot find specified artwork file anywhere */
670 char *getCustomArtworkFilename(char *basename, int type)
672 if (type == ARTWORK_TYPE_GRAPHICS)
673 return getCustomImageFilename(basename);
674 else if (type == ARTWORK_TYPE_SOUNDS)
675 return getCustomSoundFilename(basename);
676 else if (type == ARTWORK_TYPE_MUSIC)
677 return getCustomMusicFilename(basename);
679 return UNDEFINED_FILENAME;
682 char *getCustomArtworkConfigFilename(int type)
684 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
687 char *getCustomArtworkLevelConfigFilename(int type)
689 static char *filename = NULL;
691 checked_free(filename);
693 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
698 char *getCustomMusicDirectory(void)
700 static char *directory = NULL;
701 boolean skip_setup_artwork = FALSE;
703 checked_free(directory);
705 if (!setup.override_level_music)
707 /* 1st try: look for special artwork in current level series directory */
708 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
709 if (fileExists(directory))
714 /* check if there is special artwork configured in level series config */
715 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
717 /* 2nd try: look for special artwork configured in level series config */
718 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
719 if (fileExists(directory))
724 /* take missing artwork configured in level set config from default */
725 skip_setup_artwork = TRUE;
729 if (!skip_setup_artwork)
731 /* 3rd try: look for special artwork in configured artwork directory */
732 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
733 if (fileExists(directory))
739 /* 4th try: look for default artwork in new default artwork directory */
740 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
741 if (fileExists(directory))
746 /* 5th try: look for default artwork in old default artwork directory */
747 directory = getStringCopy(options.music_directory);
748 if (fileExists(directory))
751 return NULL; /* cannot find specified artwork file anywhere */
754 void InitTapeDirectory(char *level_subdir)
756 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
757 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
758 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
761 void InitScoreDirectory(char *level_subdir)
763 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
764 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
765 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
768 static void SaveUserLevelInfo();
770 void InitUserLevelDirectory(char *level_subdir)
772 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
774 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
775 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
776 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
782 void InitLevelSetupDirectory(char *level_subdir)
784 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
785 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
786 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
790 /* ------------------------------------------------------------------------- */
791 /* some functions to handle lists of level directories */
792 /* ------------------------------------------------------------------------- */
794 TreeInfo *newTreeInfo()
796 return checked_calloc(sizeof(TreeInfo));
799 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
801 node_new->next = *node_first;
802 *node_first = node_new;
805 int numTreeInfo(TreeInfo *node)
818 boolean validLevelSeries(TreeInfo *node)
820 return (node != NULL && !node->node_group && !node->parent_link);
823 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
828 if (node->node_group) /* enter level group (step down into tree) */
829 return getFirstValidTreeInfoEntry(node->node_group);
830 else if (node->parent_link) /* skip start entry of level group */
832 if (node->next) /* get first real level series entry */
833 return getFirstValidTreeInfoEntry(node->next);
834 else /* leave empty level group and go on */
835 return getFirstValidTreeInfoEntry(node->node_parent->next);
837 else /* this seems to be a regular level series */
841 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
846 if (node->node_parent == NULL) /* top level group */
847 return *node->node_top;
848 else /* sub level group */
849 return node->node_parent->node_group;
852 int numTreeInfoInGroup(TreeInfo *node)
854 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
857 int posTreeInfo(TreeInfo *node)
859 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
864 if (node_cmp == node)
868 node_cmp = node_cmp->next;
874 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
876 TreeInfo *node_default = node;
891 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
893 if (identifier == NULL)
898 if (node->node_group)
900 TreeInfo *node_group;
902 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
907 else if (!node->parent_link)
909 if (strcmp(identifier, node->identifier) == 0)
919 void dumpTreeInfo(TreeInfo *node, int depth)
923 printf("Dumping TreeInfo:\n");
927 for (i = 0; i < (depth + 1) * 3; i++)
931 printf("filename == '%s' ['%s', '%s'] [%d])\n",
932 node->filename, node->fullpath, node->basepath, node->user_defined);
934 printf("filename == '%s' (%s) [%s] (%d)\n",
935 node->filename, node->name, node->identifier, node->sort_priority);
938 if (node->node_group != NULL)
939 dumpTreeInfo(node->node_group, depth + 1);
945 void sortTreeInfo(TreeInfo **node_first,
946 int (*compare_function)(const void *, const void *))
948 int num_nodes = numTreeInfo(*node_first);
949 TreeInfo **sort_array;
950 TreeInfo *node = *node_first;
956 /* allocate array for sorting structure pointers */
957 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
959 /* writing structure pointers to sorting array */
960 while (i < num_nodes && node) /* double boundary check... */
962 sort_array[i] = node;
968 /* sorting the structure pointers in the sorting array */
969 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
972 /* update the linkage of list elements with the sorted node array */
973 for (i = 0; i < num_nodes - 1; i++)
974 sort_array[i]->next = sort_array[i + 1];
975 sort_array[num_nodes - 1]->next = NULL;
977 /* update the linkage of the main list anchor pointer */
978 *node_first = sort_array[0];
982 /* now recursively sort the level group structures */
986 if (node->node_group != NULL)
987 sortTreeInfo(&node->node_group, compare_function);
994 /* ========================================================================= */
995 /* some stuff from "files.c" */
996 /* ========================================================================= */
998 #if defined(PLATFORM_WIN32)
1000 #define S_IRGRP S_IRUSR
1003 #define S_IROTH S_IRUSR
1006 #define S_IWGRP S_IWUSR
1009 #define S_IWOTH S_IWUSR
1012 #define S_IXGRP S_IXUSR
1015 #define S_IXOTH S_IXUSR
1018 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1023 #endif /* PLATFORM_WIN32 */
1025 /* file permissions for newly written files */
1026 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1027 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1028 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1030 #define MODE_W_PRIVATE (S_IWUSR)
1031 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1032 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1034 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1035 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1037 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1038 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1040 char *getUserDataDir(void)
1042 static char *userdata_dir = NULL;
1044 if (userdata_dir == NULL)
1045 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1047 return userdata_dir;
1050 char *getCommonDataDir(void)
1052 static char *common_data_dir = NULL;
1054 #if defined(PLATFORM_WIN32)
1055 if (common_data_dir == NULL)
1057 char *dir = checked_malloc(MAX_PATH + 1);
1059 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1060 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1061 common_data_dir = getPath2(dir, program.userdata_directory);
1063 common_data_dir = options.rw_base_directory;
1066 if (common_data_dir == NULL)
1067 common_data_dir = options.rw_base_directory;
1070 return common_data_dir;
1075 return getUserDataDir();
1078 static mode_t posix_umask(mode_t mask)
1080 #if defined(PLATFORM_UNIX)
1087 static int posix_mkdir(const char *pathname, mode_t mode)
1089 #if defined(PLATFORM_WIN32)
1090 return mkdir(pathname);
1092 return mkdir(pathname, mode);
1096 void createDirectory(char *dir, char *text, int permission_class)
1098 /* leave "other" permissions in umask untouched, but ensure group parts
1099 of USERDATA_DIR_MODE are not masked */
1100 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1101 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1102 mode_t normal_umask = posix_umask(0);
1103 mode_t group_umask = ~(dir_mode & S_IRWXG);
1104 posix_umask(normal_umask & group_umask);
1106 if (access(dir, F_OK) != 0)
1107 if (posix_mkdir(dir, dir_mode) != 0)
1108 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1110 posix_umask(normal_umask); /* reset normal umask */
1113 void InitUserDataDirectory()
1115 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1118 void SetFilePermissions(char *filename, int permission_class)
1120 chmod(filename, (permission_class == PERMS_PRIVATE ?
1121 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1124 char *getCookie(char *file_type)
1126 static char cookie[MAX_COOKIE_LEN + 1];
1128 if (strlen(program.cookie_prefix) + 1 +
1129 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1130 return "[COOKIE ERROR]"; /* should never happen */
1132 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1133 program.cookie_prefix, file_type,
1134 program.version_major, program.version_minor);
1139 int getFileVersionFromCookieString(const char *cookie)
1141 const char *ptr_cookie1, *ptr_cookie2;
1142 const char *pattern1 = "_FILE_VERSION_";
1143 const char *pattern2 = "?.?";
1144 const int len_cookie = strlen(cookie);
1145 const int len_pattern1 = strlen(pattern1);
1146 const int len_pattern2 = strlen(pattern2);
1147 const int len_pattern = len_pattern1 + len_pattern2;
1148 int version_major, version_minor;
1150 if (len_cookie <= len_pattern)
1153 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1154 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1156 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1159 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1160 ptr_cookie2[1] != '.' ||
1161 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1164 version_major = ptr_cookie2[0] - '0';
1165 version_minor = ptr_cookie2[2] - '0';
1167 return VERSION_IDENT(version_major, version_minor, 0, 0);
1170 boolean checkCookieString(const char *cookie, const char *template)
1172 const char *pattern = "_FILE_VERSION_?.?";
1173 const int len_cookie = strlen(cookie);
1174 const int len_template = strlen(template);
1175 const int len_pattern = strlen(pattern);
1177 if (len_cookie != len_template)
1180 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1186 /* ------------------------------------------------------------------------- */
1187 /* setup file list and hash handling functions */
1188 /* ------------------------------------------------------------------------- */
1190 char *getFormattedSetupEntry(char *token, char *value)
1193 static char entry[MAX_LINE_LEN];
1195 /* if value is an empty string, just return token without value */
1199 /* start with the token and some spaces to format output line */
1200 sprintf(entry, "%s:", token);
1201 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1204 /* continue with the token's value */
1205 strcat(entry, value);
1210 SetupFileList *newSetupFileList(char *token, char *value)
1212 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1214 new->token = getStringCopy(token);
1215 new->value = getStringCopy(value);
1222 void freeSetupFileList(SetupFileList *list)
1227 checked_free(list->token);
1228 checked_free(list->value);
1231 freeSetupFileList(list->next);
1236 char *getListEntry(SetupFileList *list, char *token)
1241 if (strcmp(list->token, token) == 0)
1244 return getListEntry(list->next, token);
1247 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1252 if (strcmp(list->token, token) == 0)
1254 checked_free(list->value);
1256 list->value = getStringCopy(value);
1260 else if (list->next == NULL)
1261 return (list->next = newSetupFileList(token, value));
1263 return setListEntry(list->next, token, value);
1266 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1271 if (list->next == NULL)
1272 return (list->next = newSetupFileList(token, value));
1274 return addListEntry(list->next, token, value);
1278 static void printSetupFileList(SetupFileList *list)
1283 printf("token: '%s'\n", list->token);
1284 printf("value: '%s'\n", list->value);
1286 printSetupFileList(list->next);
1291 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1292 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1293 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1294 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1296 #define insert_hash_entry hashtable_insert
1297 #define search_hash_entry hashtable_search
1298 #define change_hash_entry hashtable_change
1299 #define remove_hash_entry hashtable_remove
1302 static unsigned int get_hash_from_key(void *key)
1307 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1308 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1309 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1310 it works better than many other constants, prime or not) has never been
1311 adequately explained.
1313 If you just want to have a good hash function, and cannot wait, djb2
1314 is one of the best string hash functions i know. It has excellent
1315 distribution and speed on many different sets of keys and table sizes.
1316 You are not likely to do better with one of the "well known" functions
1317 such as PJW, K&R, etc.
1319 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1322 char *str = (char *)key;
1323 unsigned int hash = 5381;
1326 while ((c = *str++))
1327 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1332 static int keys_are_equal(void *key1, void *key2)
1334 return (strcmp((char *)key1, (char *)key2) == 0);
1337 SetupFileHash *newSetupFileHash()
1339 SetupFileHash *new_hash =
1340 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1342 if (new_hash == NULL)
1343 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1348 void freeSetupFileHash(SetupFileHash *hash)
1353 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1356 char *getHashEntry(SetupFileHash *hash, char *token)
1361 return search_hash_entry(hash, token);
1364 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1371 value_copy = getStringCopy(value);
1373 /* change value; if it does not exist, insert it as new */
1374 if (!change_hash_entry(hash, token, value_copy))
1375 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1376 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1379 char *removeHashEntry(SetupFileHash *hash, char *token)
1384 return remove_hash_entry(hash, token);
1389 static void printSetupFileHash(SetupFileHash *hash)
1391 BEGIN_HASH_ITERATION(hash, itr)
1393 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1394 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1396 END_HASH_ITERATION(hash, itr)
1401 static void *loadSetupFileData(char *filename, boolean use_hash)
1403 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1404 char *token, *value, *line_ptr;
1405 void *setup_file_data, *insert_ptr = NULL;
1406 boolean read_continued_line = FALSE;
1410 setup_file_data = newSetupFileHash();
1412 insert_ptr = setup_file_data = newSetupFileList("", "");
1414 if (!(file = fopen(filename, MODE_READ)))
1416 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1422 /* read next line of input file */
1423 if (!fgets(line, MAX_LINE_LEN, file))
1426 /* cut trailing newline or carriage return */
1427 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1428 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1431 if (read_continued_line)
1433 /* cut leading whitespaces from input line */
1434 for (line_ptr = line; *line_ptr; line_ptr++)
1435 if (*line_ptr != ' ' && *line_ptr != '\t')
1438 /* append new line to existing line, if there is enough space */
1439 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1440 strcat(previous_line, line_ptr);
1442 strcpy(line, previous_line); /* copy storage buffer to line */
1444 read_continued_line = FALSE;
1447 /* if the last character is '\', continue at next line */
1448 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1450 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1451 strcpy(previous_line, line); /* copy line to storage buffer */
1453 read_continued_line = TRUE;
1458 /* cut trailing comment from input line */
1459 for (line_ptr = line; *line_ptr; line_ptr++)
1461 if (*line_ptr == '#')
1468 /* cut trailing whitespaces from input line */
1469 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1470 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1473 /* ignore empty lines */
1477 /* cut leading whitespaces from token */
1478 for (token = line; *token; token++)
1479 if (*token != ' ' && *token != '\t')
1482 /* start with empty value as reliable default */
1485 /* find end of token to determine start of value */
1486 for (line_ptr = token; *line_ptr; line_ptr++)
1488 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1490 *line_ptr = '\0'; /* terminate token string */
1491 value = line_ptr + 1; /* set beginning of value */
1497 /* cut leading whitespaces from value */
1498 for (; *value; value++)
1499 if (*value != ' ' && *value != '\t')
1504 value = "true"; /* treat tokens without value as "true" */
1510 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1512 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1520 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1521 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1525 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1526 SetupFileList *first_valid_list_entry = setup_file_list->next;
1528 /* free empty list header */
1529 setup_file_list->next = NULL;
1530 freeSetupFileList(setup_file_list);
1531 setup_file_data = first_valid_list_entry;
1533 if (first_valid_list_entry == NULL)
1534 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1537 return setup_file_data;
1540 SetupFileList *loadSetupFileList(char *filename)
1542 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1545 SetupFileHash *loadSetupFileHash(char *filename)
1547 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1550 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1553 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1556 Error(ERR_WARN, "configuration file has no file identifier");
1557 else if (!checkCookieString(value, identifier))
1558 Error(ERR_WARN, "configuration file has wrong file identifier");
1562 /* ========================================================================= */
1563 /* setup file stuff */
1564 /* ========================================================================= */
1566 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1567 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1568 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1570 /* level directory info */
1571 #define LEVELINFO_TOKEN_IDENTIFIER 0
1572 #define LEVELINFO_TOKEN_NAME 1
1573 #define LEVELINFO_TOKEN_NAME_SORTING 2
1574 #define LEVELINFO_TOKEN_AUTHOR 3
1575 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1576 #define LEVELINFO_TOKEN_LEVELS 5
1577 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1578 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1579 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1580 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1581 #define LEVELINFO_TOKEN_READONLY 10
1582 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1583 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1584 #define LEVELINFO_TOKEN_MUSIC_SET 13
1586 #define NUM_LEVELINFO_TOKENS 14
1588 static LevelDirTree ldi;
1590 static struct TokenInfo levelinfo_tokens[] =
1592 /* level directory info */
1593 { TYPE_STRING, &ldi.identifier, "identifier" },
1594 { TYPE_STRING, &ldi.name, "name" },
1595 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1596 { TYPE_STRING, &ldi.author, "author" },
1597 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1598 { TYPE_INTEGER, &ldi.levels, "levels" },
1599 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1600 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1601 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1602 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1603 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1604 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1605 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1606 { TYPE_STRING, &ldi.music_set, "music_set" }
1609 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1613 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1614 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1615 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1616 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1619 ldi->node_parent = NULL;
1620 ldi->node_group = NULL;
1624 ldi->cl_cursor = -1;
1626 ldi->filename = NULL;
1627 ldi->fullpath = NULL;
1628 ldi->basepath = NULL;
1629 ldi->identifier = NULL;
1630 ldi->name = getStringCopy(ANONYMOUS_NAME);
1631 ldi->name_sorting = NULL;
1632 ldi->author = getStringCopy(ANONYMOUS_NAME);
1634 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1635 ldi->latest_engine = FALSE; /* default: get from level */
1636 ldi->parent_link = FALSE;
1637 ldi->user_defined = FALSE;
1639 ldi->class_desc = NULL;
1641 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1643 ldi->imported_from = NULL;
1645 ldi->graphics_set = NULL;
1646 ldi->sounds_set = NULL;
1647 ldi->music_set = NULL;
1648 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1649 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1650 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1653 ldi->first_level = 0;
1654 ldi->last_level = 0;
1655 ldi->level_group = FALSE;
1656 ldi->handicap_level = 0;
1657 ldi->readonly = TRUE;
1661 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1665 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1667 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1673 /* copy all values from the parent structure */
1675 ldi->type = parent->type;
1677 ldi->node_top = parent->node_top;
1678 ldi->node_parent = parent;
1679 ldi->node_group = NULL;
1683 ldi->cl_cursor = -1;
1685 ldi->filename = NULL;
1686 ldi->fullpath = NULL;
1687 ldi->basepath = NULL;
1688 ldi->identifier = NULL;
1689 ldi->name = getStringCopy(ANONYMOUS_NAME);
1690 ldi->name_sorting = NULL;
1691 ldi->author = getStringCopy(parent->author);
1693 ldi->sort_priority = parent->sort_priority;
1694 ldi->latest_engine = parent->latest_engine;
1695 ldi->parent_link = FALSE;
1696 ldi->user_defined = parent->user_defined;
1697 ldi->color = parent->color;
1698 ldi->class_desc = getStringCopy(parent->class_desc);
1700 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1702 ldi->imported_from = getStringCopy(parent->imported_from);
1704 ldi->graphics_set = NULL;
1705 ldi->sounds_set = NULL;
1706 ldi->music_set = NULL;
1707 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1708 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1709 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1712 ldi->first_level = 0;
1713 ldi->last_level = 0;
1714 ldi->level_group = FALSE;
1715 ldi->handicap_level = 0;
1716 ldi->readonly = TRUE;
1722 /* first copy all values from the parent structure ... */
1725 /* ... then set all fields to default that cannot be inherited from parent.
1726 This is especially important for all those fields that can be set from
1727 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1728 calls 'free()' for all already set token values which requires that no
1729 other structure's pointer may point to them!
1732 ldi->filename = NULL;
1733 ldi->fullpath = NULL;
1734 ldi->basepath = NULL;
1735 ldi->identifier = NULL;
1736 ldi->name = getStringCopy(ANONYMOUS_NAME);
1737 ldi->name_sorting = NULL;
1738 ldi->author = getStringCopy(parent->author);
1740 ldi->imported_from = getStringCopy(parent->imported_from);
1741 ldi->class_desc = getStringCopy(parent->class_desc);
1743 ldi->graphics_set = NULL;
1744 ldi->sounds_set = NULL;
1745 ldi->music_set = NULL;
1746 ldi->graphics_path = NULL;
1747 ldi->sounds_path = NULL;
1748 ldi->music_path = NULL;
1750 ldi->level_group = FALSE;
1751 ldi->parent_link = FALSE;
1753 ldi->node_top = parent->node_top;
1754 ldi->node_parent = parent;
1755 ldi->node_group = NULL;
1761 static void freeTreeInfo(TreeInfo *ldi)
1763 checked_free(ldi->filename);
1764 checked_free(ldi->fullpath);
1765 checked_free(ldi->basepath);
1766 checked_free(ldi->identifier);
1768 checked_free(ldi->name);
1769 checked_free(ldi->name_sorting);
1770 checked_free(ldi->author);
1772 checked_free(ldi->class_desc);
1774 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1776 checked_free(ldi->graphics_set);
1777 checked_free(ldi->sounds_set);
1778 checked_free(ldi->music_set);
1780 checked_free(ldi->graphics_path);
1781 checked_free(ldi->sounds_path);
1782 checked_free(ldi->music_path);
1786 void setSetupInfo(struct TokenInfo *token_info,
1787 int token_nr, char *token_value)
1789 int token_type = token_info[token_nr].type;
1790 void *setup_value = token_info[token_nr].value;
1792 if (token_value == NULL)
1795 /* set setup field to corresponding token value */
1800 *(boolean *)setup_value = get_boolean_from_string(token_value);
1804 *(Key *)setup_value = getKeyFromKeyName(token_value);
1808 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1812 *(int *)setup_value = get_integer_from_string(token_value);
1816 checked_free(*(char **)setup_value);
1817 *(char **)setup_value = getStringCopy(token_value);
1825 static int compareTreeInfoEntries(const void *object1, const void *object2)
1827 const TreeInfo *entry1 = *((TreeInfo **)object1);
1828 const TreeInfo *entry2 = *((TreeInfo **)object2);
1829 int class_sorting1, class_sorting2;
1832 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1834 class_sorting1 = LEVELSORTING(entry1);
1835 class_sorting2 = LEVELSORTING(entry2);
1839 class_sorting1 = ARTWORKSORTING(entry1);
1840 class_sorting2 = ARTWORKSORTING(entry2);
1843 if (entry1->parent_link || entry2->parent_link)
1844 compare_result = (entry1->parent_link ? -1 : +1);
1845 else if (entry1->sort_priority == entry2->sort_priority)
1847 char *name1 = getStringToLower(entry1->name_sorting);
1848 char *name2 = getStringToLower(entry2->name_sorting);
1850 compare_result = strcmp(name1, name2);
1855 else if (class_sorting1 == class_sorting2)
1856 compare_result = entry1->sort_priority - entry2->sort_priority;
1858 compare_result = class_sorting1 - class_sorting2;
1860 return compare_result;
1863 static void createParentTreeInfoNode(TreeInfo *node_parent)
1867 if (node_parent == NULL)
1870 ti_new = newTreeInfo();
1871 setTreeInfoToDefaults(ti_new, node_parent->type);
1873 ti_new->node_parent = node_parent;
1874 ti_new->parent_link = TRUE;
1877 setString(&ti_new->identifier, node_parent->identifier);
1878 setString(&ti_new->name, ".. (parent directory)");
1879 setString(&ti_new->name_sorting, ti_new->name);
1881 setString(&ti_new->filename, "..");
1882 setString(&ti_new->fullpath, node_parent->fullpath);
1884 ti_new->sort_priority = node_parent->sort_priority;
1885 ti_new->latest_engine = node_parent->latest_engine;
1887 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1889 ti_new->identifier = getStringCopy(node_parent->identifier);
1890 ti_new->name = ".. (parent directory)";
1891 ti_new->name_sorting = getStringCopy(ti_new->name);
1893 ti_new->filename = "..";
1894 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1896 ti_new->sort_priority = node_parent->sort_priority;
1897 ti_new->latest_engine = node_parent->latest_engine;
1899 ti_new->class_desc = getLevelClassDescription(ti_new);
1902 pushTreeInfo(&node_parent->node_group, ti_new);
1905 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1906 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1908 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1909 TreeInfo *node_parent,
1910 char *level_directory,
1911 char *directory_name)
1913 char *directory_path = getPath2(level_directory, directory_name);
1914 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1915 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1916 LevelDirTree *leveldir_new = NULL;
1919 if (setup_file_hash == NULL)
1921 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1923 free(directory_path);
1929 leveldir_new = newTreeInfo();
1932 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1934 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1936 leveldir_new->filename = getStringCopy(directory_name);
1938 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1940 /* set all structure fields according to the token/value pairs */
1941 ldi = *leveldir_new;
1942 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1943 setSetupInfo(levelinfo_tokens, i,
1944 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1945 *leveldir_new = ldi;
1948 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1949 setString(&leveldir_new->name, leveldir_new->filename);
1951 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1953 free(leveldir_new->name);
1954 leveldir_new->name = getStringCopy(leveldir_new->filename);
1958 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1960 if (leveldir_new->identifier == NULL)
1961 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1963 if (leveldir_new->name_sorting == NULL)
1964 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1966 if (node_parent == NULL) /* top level group */
1968 leveldir_new->basepath = getStringCopy(level_directory);
1969 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1971 else /* sub level group */
1973 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1974 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1977 if (leveldir_new->levels < 1)
1978 leveldir_new->levels = 1;
1980 leveldir_new->last_level =
1981 leveldir_new->first_level + leveldir_new->levels - 1;
1984 leveldir_new->user_defined =
1985 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1987 leveldir_new->user_defined =
1988 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1991 leveldir_new->color = LEVELCOLOR(leveldir_new);
1993 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1995 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1998 leveldir_new->handicap_level = /* set handicap to default value */
1999 (leveldir_new->user_defined ?
2000 leveldir_new->last_level :
2001 leveldir_new->first_level);
2003 pushTreeInfo(node_first, leveldir_new);
2005 freeSetupFileHash(setup_file_hash);
2007 if (leveldir_new->level_group)
2009 /* create node to link back to current level directory */
2010 createParentTreeInfoNode(leveldir_new);
2012 /* step into sub-directory and look for more level series */
2013 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2014 leveldir_new, directory_path);
2017 free(directory_path);
2023 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2024 TreeInfo *node_parent,
2025 char *level_directory)
2028 struct dirent *dir_entry;
2029 boolean valid_entry_found = FALSE;
2031 if ((dir = opendir(level_directory)) == NULL)
2033 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2037 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2039 struct stat file_status;
2040 char *directory_name = dir_entry->d_name;
2041 char *directory_path = getPath2(level_directory, directory_name);
2043 /* skip entries for current and parent directory */
2044 if (strcmp(directory_name, ".") == 0 ||
2045 strcmp(directory_name, "..") == 0)
2047 free(directory_path);
2051 /* find out if directory entry is itself a directory */
2052 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2053 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2055 free(directory_path);
2059 free(directory_path);
2061 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2062 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2063 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2066 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2073 if (!valid_entry_found)
2075 /* check if this directory directly contains a file "levelinfo.conf" */
2076 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2077 level_directory, ".");
2080 if (!valid_entry_found)
2081 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2085 void LoadLevelInfo()
2087 InitUserLevelDirectory(getLoginName());
2089 DrawInitText("Loading level series:", 120, FC_GREEN);
2091 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2092 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2094 /* before sorting, the first entries will be from the user directory */
2095 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2097 if (leveldir_first == NULL)
2098 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2100 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2103 dumpTreeInfo(leveldir_first, 0);
2107 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2108 TreeInfo *node_parent,
2109 char *base_directory,
2110 char *directory_name, int type)
2112 char *directory_path = getPath2(base_directory, directory_name);
2113 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2114 SetupFileHash *setup_file_hash = NULL;
2115 TreeInfo *artwork_new = NULL;
2118 if (access(filename, F_OK) == 0) /* file exists */
2119 setup_file_hash = loadSetupFileHash(filename);
2121 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2124 struct dirent *dir_entry;
2125 boolean valid_file_found = FALSE;
2127 if ((dir = opendir(directory_path)) != NULL)
2129 while ((dir_entry = readdir(dir)) != NULL)
2131 char *entry_name = dir_entry->d_name;
2133 if (FileIsArtworkType(entry_name, type))
2135 valid_file_found = TRUE;
2143 if (!valid_file_found)
2145 if (strcmp(directory_name, ".") != 0)
2146 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2148 free(directory_path);
2155 artwork_new = newTreeInfo();
2158 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2160 setTreeInfoToDefaults(artwork_new, type);
2162 artwork_new->filename = getStringCopy(directory_name);
2164 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2167 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2170 /* set all structure fields according to the token/value pairs */
2172 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2173 setSetupInfo(levelinfo_tokens, i,
2174 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2178 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2179 setString(&artwork_new->name, artwork_new->filename);
2181 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2183 free(artwork_new->name);
2184 artwork_new->name = getStringCopy(artwork_new->filename);
2189 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2192 if (artwork_new->identifier == NULL)
2193 artwork_new->identifier = getStringCopy(artwork_new->filename);
2195 if (artwork_new->name_sorting == NULL)
2196 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2199 if (node_parent == NULL) /* top level group */
2201 artwork_new->basepath = getStringCopy(base_directory);
2202 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2204 else /* sub level group */
2206 artwork_new->basepath = getStringCopy(node_parent->basepath);
2207 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2211 artwork_new->user_defined =
2212 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2214 artwork_new->user_defined =
2215 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2218 /* (may use ".sort_priority" from "setup_file_hash" above) */
2219 artwork_new->color = ARTWORKCOLOR(artwork_new);
2221 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2223 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2226 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2229 if (artwork_new->name != NULL)
2231 free(artwork_new->name);
2232 artwork_new->name = NULL;
2237 if (artwork_new->identifier != NULL)
2239 free(artwork_new->identifier);
2240 artwork_new->identifier = NULL;
2244 if (strcmp(artwork_new->filename, ".") == 0)
2246 if (artwork_new->user_defined)
2249 setString(&artwork_new->identifier, "private");
2251 artwork_new->identifier = getStringCopy("private");
2253 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2258 setString(&artwork_new->identifier, "classic");
2260 artwork_new->identifier = getStringCopy("classic");
2262 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2265 /* set to new values after changing ".sort_priority" */
2266 artwork_new->color = ARTWORKCOLOR(artwork_new);
2268 setString(&artwork_new->class_desc,
2269 getLevelClassDescription(artwork_new));
2271 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2277 setString(&artwork_new->identifier, artwork_new->filename);
2279 artwork_new->identifier = getStringCopy(artwork_new->filename);
2284 setString(&artwork_new->name, artwork_new->identifier);
2285 setString(&artwork_new->name_sorting, artwork_new->name);
2287 artwork_new->name = getStringCopy(artwork_new->identifier);
2288 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2292 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2294 pushTreeInfo(node_first, artwork_new);
2296 freeSetupFileHash(setup_file_hash);
2298 free(directory_path);
2304 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2305 TreeInfo *node_parent,
2306 char *base_directory, int type)
2309 struct dirent *dir_entry;
2310 boolean valid_entry_found = FALSE;
2312 if ((dir = opendir(base_directory)) == NULL)
2314 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2315 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2319 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2321 struct stat file_status;
2322 char *directory_name = dir_entry->d_name;
2323 char *directory_path = getPath2(base_directory, directory_name);
2325 /* skip entries for current and parent directory */
2326 if (strcmp(directory_name, ".") == 0 ||
2327 strcmp(directory_name, "..") == 0)
2329 free(directory_path);
2333 /* find out if directory entry is itself a directory */
2334 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2335 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2337 free(directory_path);
2341 free(directory_path);
2343 /* check if this directory contains artwork with or without config file */
2344 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2346 directory_name, type);
2351 /* check if this directory directly contains artwork itself */
2352 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2353 base_directory, ".",
2355 if (!valid_entry_found)
2356 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2360 static TreeInfo *getDummyArtworkInfo(int type)
2362 /* this is only needed when there is completely no artwork available */
2363 TreeInfo *artwork_new = newTreeInfo();
2365 setTreeInfoToDefaults(artwork_new, type);
2368 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2369 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2370 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2372 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2373 setString(&artwork_new->name, UNDEFINED_FILENAME);
2374 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2376 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2377 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2378 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2380 checked_free(artwork_new->name);
2382 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2383 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2384 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2390 void LoadArtworkInfo()
2392 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2394 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2395 options.graphics_directory,
2396 TREE_TYPE_GRAPHICS_DIR);
2397 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2398 getUserGraphicsDir(),
2399 TREE_TYPE_GRAPHICS_DIR);
2401 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2402 options.sounds_directory,
2403 TREE_TYPE_SOUNDS_DIR);
2404 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2406 TREE_TYPE_SOUNDS_DIR);
2408 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2409 options.music_directory,
2410 TREE_TYPE_MUSIC_DIR);
2411 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2413 TREE_TYPE_MUSIC_DIR);
2415 if (artwork.gfx_first == NULL)
2416 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2417 if (artwork.snd_first == NULL)
2418 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2419 if (artwork.mus_first == NULL)
2420 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2422 /* before sorting, the first entries will be from the user directory */
2423 artwork.gfx_current =
2424 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2425 if (artwork.gfx_current == NULL)
2426 artwork.gfx_current =
2427 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2428 if (artwork.gfx_current == NULL)
2429 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2431 artwork.snd_current =
2432 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2433 if (artwork.snd_current == NULL)
2434 artwork.snd_current =
2435 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2436 if (artwork.snd_current == NULL)
2437 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2439 artwork.mus_current =
2440 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2441 if (artwork.mus_current == NULL)
2442 artwork.mus_current =
2443 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2444 if (artwork.mus_current == NULL)
2445 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2447 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2448 artwork.snd_current_identifier = artwork.snd_current->identifier;
2449 artwork.mus_current_identifier = artwork.mus_current->identifier;
2452 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2453 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2454 printf("music set == %s\n\n", artwork.mus_current_identifier);
2457 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2458 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2459 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2462 dumpTreeInfo(artwork.gfx_first, 0);
2463 dumpTreeInfo(artwork.snd_first, 0);
2464 dumpTreeInfo(artwork.mus_first, 0);
2468 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2469 LevelDirTree *level_node)
2471 /* recursively check all level directories for artwork sub-directories */
2475 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2476 ARTWORK_DIRECTORY((*artwork_node)->type));
2479 if (!level_node->parent_link)
2480 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2481 level_node->filename, level_node->name);
2484 if (!level_node->parent_link)
2486 TreeInfo *topnode_last = *artwork_node;
2488 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2489 (*artwork_node)->type);
2491 if (topnode_last != *artwork_node)
2493 free((*artwork_node)->identifier);
2494 free((*artwork_node)->name);
2495 free((*artwork_node)->name_sorting);
2497 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2498 (*artwork_node)->name = getStringCopy(level_node->name);
2499 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2501 (*artwork_node)->sort_priority = level_node->sort_priority;
2502 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2508 if (level_node->node_group != NULL)
2509 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2511 level_node = level_node->next;
2515 void LoadLevelArtworkInfo()
2517 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2519 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2520 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2521 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2523 /* needed for reloading level artwork not known at ealier stage */
2525 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2527 artwork.gfx_current =
2528 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2529 if (artwork.gfx_current == NULL)
2530 artwork.gfx_current =
2531 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2532 if (artwork.gfx_current == NULL)
2533 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2536 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2538 artwork.snd_current =
2539 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2540 if (artwork.snd_current == NULL)
2541 artwork.snd_current =
2542 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2543 if (artwork.snd_current == NULL)
2544 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2547 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2549 artwork.mus_current =
2550 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2551 if (artwork.mus_current == NULL)
2552 artwork.mus_current =
2553 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2554 if (artwork.mus_current == NULL)
2555 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2558 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2559 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2560 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2563 dumpTreeInfo(artwork.gfx_first, 0);
2564 dumpTreeInfo(artwork.snd_first, 0);
2565 dumpTreeInfo(artwork.mus_first, 0);
2569 static void SaveUserLevelInfo()
2571 LevelDirTree *level_info;
2576 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2578 if (!(file = fopen(filename, MODE_WRITE)))
2580 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2585 level_info = newTreeInfo();
2587 /* always start with reliable default values */
2588 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2591 setString(&level_info->name, getLoginName());
2592 setString(&level_info->author, getRealName());
2593 level_info->levels = 100;
2594 level_info->first_level = 1;
2595 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2596 level_info->readonly = FALSE;
2597 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2598 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2599 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2601 ldi.name = getStringCopy(getLoginName());
2602 ldi.author = getStringCopy(getRealName());
2604 ldi.first_level = 1;
2605 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2606 ldi.readonly = FALSE;
2607 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2608 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2609 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2612 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2613 getCookie("LEVELINFO")));
2616 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2617 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2618 i != LEVELINFO_TOKEN_NAME_SORTING &&
2619 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2620 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2624 SetFilePermissions(filename, PERMS_PRIVATE);
2626 freeTreeInfo(level_info);
2630 char *getSetupValue(int type, void *value)
2632 static char value_string[MAX_LINE_LEN];
2640 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2644 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2648 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2652 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2656 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2660 sprintf(value_string, "%d", *(int *)value);
2664 strcpy(value_string, *(char **)value);
2668 value_string[0] = '\0';
2672 return value_string;
2675 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2679 static char token_string[MAX_LINE_LEN];
2680 int token_type = token_info[token_nr].type;
2681 void *setup_value = token_info[token_nr].value;
2682 char *token_text = token_info[token_nr].text;
2683 char *value_string = getSetupValue(token_type, setup_value);
2685 /* build complete token string */
2686 sprintf(token_string, "%s%s", prefix, token_text);
2688 /* build setup entry line */
2689 line = getFormattedSetupEntry(token_string, value_string);
2691 if (token_type == TYPE_KEY_X11)
2693 Key key = *(Key *)setup_value;
2694 char *keyname = getKeyNameFromKey(key);
2696 /* add comment, if useful */
2697 if (strcmp(keyname, "(undefined)") != 0 &&
2698 strcmp(keyname, "(unknown)") != 0)
2700 /* add at least one whitespace */
2702 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2706 strcat(line, keyname);
2713 void LoadLevelSetup_LastSeries()
2715 /* ----------------------------------------------------------------------- */
2716 /* ~/.<program>/levelsetup.conf */
2717 /* ----------------------------------------------------------------------- */
2719 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2720 SetupFileHash *level_setup_hash = NULL;
2722 /* always start with reliable default values */
2723 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2725 if ((level_setup_hash = loadSetupFileHash(filename)))
2727 char *last_level_series =
2728 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2730 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2732 if (leveldir_current == NULL)
2733 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2735 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2737 freeSetupFileHash(level_setup_hash);
2740 Error(ERR_WARN, "using default setup values");
2745 void SaveLevelSetup_LastSeries()
2747 /* ----------------------------------------------------------------------- */
2748 /* ~/.<program>/levelsetup.conf */
2749 /* ----------------------------------------------------------------------- */
2751 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2752 char *level_subdir = leveldir_current->filename;
2755 InitUserDataDirectory();
2757 if (!(file = fopen(filename, MODE_WRITE)))
2759 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2764 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2765 getCookie("LEVELSETUP")));
2766 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2771 SetFilePermissions(filename, PERMS_PRIVATE);
2776 static void checkSeriesInfo()
2778 static char *level_directory = NULL;
2780 struct dirent *dir_entry;
2782 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2784 level_directory = getPath2((leveldir_current->user_defined ?
2785 getUserLevelDir(NULL) :
2786 options.level_directory),
2787 leveldir_current->fullpath);
2789 if ((dir = opendir(level_directory)) == NULL)
2791 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2795 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2797 if (strlen(dir_entry->d_name) > 4 &&
2798 dir_entry->d_name[3] == '.' &&
2799 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2801 char levelnum_str[4];
2804 strncpy(levelnum_str, dir_entry->d_name, 3);
2805 levelnum_str[3] = '\0';
2807 levelnum_value = atoi(levelnum_str);
2810 if (levelnum_value < leveldir_current->first_level)
2812 Error(ERR_WARN, "additional level %d found", levelnum_value);
2813 leveldir_current->first_level = levelnum_value;
2815 else if (levelnum_value > leveldir_current->last_level)
2817 Error(ERR_WARN, "additional level %d found", levelnum_value);
2818 leveldir_current->last_level = levelnum_value;
2827 void LoadLevelSetup_SeriesInfo()
2830 SetupFileHash *level_setup_hash = NULL;
2831 char *level_subdir = leveldir_current->filename;
2833 /* always start with reliable default values */
2834 level_nr = leveldir_current->first_level;
2836 checkSeriesInfo(leveldir_current);
2838 /* ----------------------------------------------------------------------- */
2839 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2840 /* ----------------------------------------------------------------------- */
2842 level_subdir = leveldir_current->filename;
2844 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2846 if ((level_setup_hash = loadSetupFileHash(filename)))
2850 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2854 level_nr = atoi(token_value);
2856 if (level_nr < leveldir_current->first_level)
2857 level_nr = leveldir_current->first_level;
2858 if (level_nr > leveldir_current->last_level)
2859 level_nr = leveldir_current->last_level;
2862 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2866 int level_nr = atoi(token_value);
2868 if (level_nr < leveldir_current->first_level)
2869 level_nr = leveldir_current->first_level;
2870 if (level_nr > leveldir_current->last_level + 1)
2871 level_nr = leveldir_current->last_level;
2873 if (leveldir_current->user_defined)
2874 level_nr = leveldir_current->last_level;
2876 leveldir_current->handicap_level = level_nr;
2879 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2881 freeSetupFileHash(level_setup_hash);
2884 Error(ERR_WARN, "using default setup values");
2889 void SaveLevelSetup_SeriesInfo()
2892 char *level_subdir = leveldir_current->filename;
2893 char *level_nr_str = int2str(level_nr, 0);
2894 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2897 /* ----------------------------------------------------------------------- */
2898 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2899 /* ----------------------------------------------------------------------- */
2901 InitLevelSetupDirectory(level_subdir);
2903 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2905 if (!(file = fopen(filename, MODE_WRITE)))
2907 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2912 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2913 getCookie("LEVELSETUP")));
2914 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2916 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2917 handicap_level_str));
2921 SetFilePermissions(filename, PERMS_PRIVATE);