1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
27 #define NUM_LEVELCLASS_DESC 8
29 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
42 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
43 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
44 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
49 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
52 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
53 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
54 IS_LEVELCLASS_BD(n) ? 2 : \
55 IS_LEVELCLASS_EM(n) ? 3 : \
56 IS_LEVELCLASS_SP(n) ? 4 : \
57 IS_LEVELCLASS_DX(n) ? 5 : \
58 IS_LEVELCLASS_CONTRIB(n) ? 6 : \
59 IS_LEVELCLASS_PRIVATE(n) ? 7 : \
62 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
63 IS_ARTWORKCLASS_CONTRIB(n) ? FC_YELLOW : \
64 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
65 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
68 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
69 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
70 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
71 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
74 #define TOKEN_VALUE_POSITION 40
75 #define TOKEN_COMMENT_POSITION 60
77 #define MAX_COOKIE_LEN 256
80 /* ------------------------------------------------------------------------- */
82 /* ------------------------------------------------------------------------- */
84 static char *getLevelClassDescription(TreeInfo *ldi)
86 int position = ldi->sort_priority / 100;
88 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
89 return levelclass_desc[position];
91 return "Unknown Level Class";
94 static char *getUserLevelDir(char *level_subdir)
96 static char *userlevel_dir = NULL;
97 char *data_dir = getUserDataDir();
98 char *userlevel_subdir = LEVELS_DIRECTORY;
103 if (level_subdir != NULL)
104 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
106 userlevel_dir = getPath2(data_dir, userlevel_subdir);
108 return userlevel_dir;
111 static char *getTapeDir(char *level_subdir)
113 static char *tape_dir = NULL;
114 char *data_dir = getUserDataDir();
115 char *tape_subdir = TAPES_DIRECTORY;
120 if (level_subdir != NULL)
121 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
123 tape_dir = getPath2(data_dir, tape_subdir);
128 static char *getScoreDir(char *level_subdir)
130 static char *score_dir = NULL;
131 char *data_dir = getCommonDataDir();
132 char *score_subdir = SCORES_DIRECTORY;
137 if (level_subdir != NULL)
138 score_dir = getPath3(data_dir, score_subdir, level_subdir);
140 score_dir = getPath2(data_dir, score_subdir);
145 static char *getLevelSetupDir(char *level_subdir)
147 static char *levelsetup_dir = NULL;
148 char *data_dir = getUserDataDir();
149 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
152 free(levelsetup_dir);
154 if (level_subdir != NULL)
155 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
157 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
159 return levelsetup_dir;
162 static char *getLevelDirFromTreeInfo(TreeInfo *node)
164 static char *level_dir = NULL;
167 return options.level_directory;
172 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
173 options.level_directory), node->fullpath);
178 static char *getCurrentLevelDir()
180 return getLevelDirFromTreeInfo(leveldir_current);
183 static char *getDefaultGraphicsDir(char *graphics_subdir)
185 static char *graphics_dir = NULL;
187 if (graphics_subdir == NULL)
188 return options.graphics_directory;
193 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
198 static char *getDefaultSoundsDir(char *sounds_subdir)
200 static char *sounds_dir = NULL;
202 if (sounds_subdir == NULL)
203 return options.sounds_directory;
208 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
213 static char *getDefaultMusicDir(char *music_subdir)
215 static char *music_dir = NULL;
217 if (music_subdir == NULL)
218 return options.music_directory;
223 music_dir = getPath2(options.music_directory, music_subdir);
228 static char *getDefaultArtworkSet(int type)
230 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
231 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
232 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
235 static char *getDefaultArtworkDir(int type)
237 return (type == TREE_TYPE_GRAPHICS_DIR ?
238 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
239 type == TREE_TYPE_SOUNDS_DIR ?
240 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
241 type == TREE_TYPE_MUSIC_DIR ?
242 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
245 static char *getUserGraphicsDir()
247 static char *usergraphics_dir = NULL;
249 if (usergraphics_dir == NULL)
250 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
252 return usergraphics_dir;
255 static char *getUserSoundsDir()
257 static char *usersounds_dir = NULL;
259 if (usersounds_dir == NULL)
260 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
262 return usersounds_dir;
265 static char *getUserMusicDir()
267 static char *usermusic_dir = NULL;
269 if (usermusic_dir == NULL)
270 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
272 return usermusic_dir;
275 static char *getSetupArtworkDir(TreeInfo *ti)
277 static char *artwork_dir = NULL;
279 if (artwork_dir != NULL)
282 artwork_dir = getPath2(ti->basepath, ti->fullpath);
287 char *setLevelArtworkDir(TreeInfo *ti)
289 char **artwork_path_ptr, **artwork_set_ptr;
290 TreeInfo *level_artwork;
292 if (ti == NULL || leveldir_current == NULL)
295 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
296 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
298 if (*artwork_path_ptr != NULL)
299 free(*artwork_path_ptr);
301 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
302 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
305 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
306 normally result in using the artwork configured in the setup menu. But
307 if an artwork subdirectory exists (which might contain custom artwork
308 or an artwork configuration file), this level artwork must be treated
309 as relative to the default "classic" artwork, not to the artwork that
310 is currently configured in the setup menu. */
312 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
314 if (*artwork_set_ptr != NULL)
315 free(*artwork_set_ptr);
319 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
320 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
324 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
325 *artwork_set_ptr = NULL;
331 return *artwork_set_ptr;
334 inline static char *getLevelArtworkSet(int type)
336 if (leveldir_current == NULL)
339 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
342 inline static char *getLevelArtworkDir(int type)
344 if (leveldir_current == NULL)
345 return UNDEFINED_FILENAME;
347 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
350 char *getLevelFilename(int nr)
352 static char *filename = NULL;
353 char basename[MAX_FILENAME_LEN];
355 if (filename != NULL)
359 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
361 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
363 filename = getPath2(getCurrentLevelDir(), basename);
368 char *getTapeFilename(int nr)
370 static char *filename = NULL;
371 char basename[MAX_FILENAME_LEN];
373 if (filename != NULL)
376 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
377 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
382 char *getScoreFilename(int nr)
384 static char *filename = NULL;
385 char basename[MAX_FILENAME_LEN];
387 if (filename != NULL)
390 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
391 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
396 char *getSetupFilename()
398 static char *filename = NULL;
400 if (filename != NULL)
403 filename = getPath2(getSetupDir(), SETUP_FILENAME);
408 char *getEditorSetupFilename()
410 static char *filename = NULL;
412 if (filename != NULL)
415 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
420 char *getHelpAnimFilename()
422 static char *filename = NULL;
424 if (filename != NULL)
427 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
432 char *getHelpTextFilename()
434 static char *filename = NULL;
436 if (filename != NULL)
439 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
444 char *getLevelSetInfoFilename()
446 static char *filename = NULL;
459 for (i = 0; basenames[i] != NULL; i++)
461 if (filename != NULL)
464 filename = getPath2(getCurrentLevelDir(), basenames[i]);
465 if (fileExists(filename))
472 static char *getCorrectedArtworkBasename(char *basename)
474 char *basename_corrected = basename;
476 #if defined(PLATFORM_MSDOS)
477 if (program.filename_prefix != NULL)
479 int prefix_len = strlen(program.filename_prefix);
481 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
482 basename_corrected = &basename[prefix_len];
484 /* if corrected filename is still longer than standard MS-DOS filename
485 size (8 characters + 1 dot + 3 characters file extension), shorten
486 filename by writing file extension after 8th basename character */
487 if (strlen(basename_corrected) > 8 + 1 + 3)
489 static char *msdos_filename = NULL;
491 if (msdos_filename != NULL)
492 free(msdos_filename);
494 msdos_filename = getStringCopy(basename_corrected);
495 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
497 basename_corrected = msdos_filename;
502 return basename_corrected;
505 char *getCustomImageFilename(char *basename)
507 static char *filename = NULL;
508 boolean skip_setup_artwork = FALSE;
510 if (filename != NULL)
513 basename = getCorrectedArtworkBasename(basename);
515 if (!setup.override_level_graphics)
517 /* 1st try: look for special artwork in current level series directory */
518 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
519 if (fileExists(filename))
524 /* check if there is special artwork configured in level series config */
525 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
527 /* 2nd try: look for special artwork configured in level series config */
528 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
529 if (fileExists(filename))
534 /* take missing artwork configured in level set config from default */
535 skip_setup_artwork = TRUE;
539 if (!skip_setup_artwork)
541 /* 3rd try: look for special artwork in configured artwork directory */
542 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
543 if (fileExists(filename))
549 /* 4th try: look for default artwork in new default artwork directory */
550 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
551 if (fileExists(filename))
556 /* 5th try: look for default artwork in old default artwork directory */
557 filename = getPath2(options.graphics_directory, basename);
558 if (fileExists(filename))
561 return NULL; /* cannot find specified artwork file anywhere */
564 char *getCustomSoundFilename(char *basename)
566 static char *filename = NULL;
567 boolean skip_setup_artwork = FALSE;
569 if (filename != NULL)
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 if (filename != NULL)
631 basename = getCorrectedArtworkBasename(basename);
633 if (!setup.override_level_music)
635 /* 1st try: look for special artwork in current level series directory */
636 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
637 if (fileExists(filename))
642 /* check if there is special artwork configured in level series config */
643 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
645 /* 2nd try: look for special artwork configured in level series config */
646 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
647 if (fileExists(filename))
652 /* take missing artwork configured in level set config from default */
653 skip_setup_artwork = TRUE;
657 if (!skip_setup_artwork)
659 /* 3rd try: look for special artwork in configured artwork directory */
660 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
661 if (fileExists(filename))
667 /* 4th try: look for default artwork in new default artwork directory */
668 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
669 if (fileExists(filename))
674 /* 5th try: look for default artwork in old default artwork directory */
675 filename = getPath2(options.music_directory, basename);
676 if (fileExists(filename))
679 return NULL; /* cannot find specified artwork file anywhere */
682 char *getCustomArtworkFilename(char *basename, int type)
684 if (type == ARTWORK_TYPE_GRAPHICS)
685 return getCustomImageFilename(basename);
686 else if (type == ARTWORK_TYPE_SOUNDS)
687 return getCustomSoundFilename(basename);
688 else if (type == ARTWORK_TYPE_MUSIC)
689 return getCustomMusicFilename(basename);
691 return UNDEFINED_FILENAME;
694 char *getCustomArtworkConfigFilename(int type)
696 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
699 char *getCustomArtworkLevelConfigFilename(int type)
701 static char *filename = NULL;
703 if (filename != NULL)
706 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
711 char *getCustomMusicDirectory(void)
713 static char *directory = NULL;
714 boolean skip_setup_artwork = FALSE;
716 if (directory != NULL)
719 if (!setup.override_level_music)
721 /* 1st try: look for special artwork in current level series directory */
722 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
723 if (fileExists(directory))
728 /* check if there is special artwork configured in level series config */
729 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
731 /* 2nd try: look for special artwork configured in level series config */
732 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
733 if (fileExists(directory))
738 /* take missing artwork configured in level set config from default */
739 skip_setup_artwork = TRUE;
743 if (!skip_setup_artwork)
745 /* 3rd try: look for special artwork in configured artwork directory */
746 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
747 if (fileExists(directory))
753 /* 4th try: look for default artwork in new default artwork directory */
754 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
755 if (fileExists(directory))
760 /* 5th try: look for default artwork in old default artwork directory */
761 directory = getStringCopy(options.music_directory);
762 if (fileExists(directory))
765 return NULL; /* cannot find specified artwork file anywhere */
768 void InitTapeDirectory(char *level_subdir)
770 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
771 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
772 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
775 void InitScoreDirectory(char *level_subdir)
777 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
778 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
779 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
782 static void SaveUserLevelInfo();
784 void InitUserLevelDirectory(char *level_subdir)
786 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
788 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
789 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
790 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
796 void InitLevelSetupDirectory(char *level_subdir)
798 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
799 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
800 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
804 /* ------------------------------------------------------------------------- */
805 /* some functions to handle lists of level directories */
806 /* ------------------------------------------------------------------------- */
808 TreeInfo *newTreeInfo()
810 return checked_calloc(sizeof(TreeInfo));
813 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
815 node_new->next = *node_first;
816 *node_first = node_new;
819 int numTreeInfo(TreeInfo *node)
832 boolean validLevelSeries(TreeInfo *node)
834 return (node != NULL && !node->node_group && !node->parent_link);
837 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
842 if (node->node_group) /* enter level group (step down into tree) */
843 return getFirstValidTreeInfoEntry(node->node_group);
844 else if (node->parent_link) /* skip start entry of level group */
846 if (node->next) /* get first real level series entry */
847 return getFirstValidTreeInfoEntry(node->next);
848 else /* leave empty level group and go on */
849 return getFirstValidTreeInfoEntry(node->node_parent->next);
851 else /* this seems to be a regular level series */
855 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
860 if (node->node_parent == NULL) /* top level group */
861 return *node->node_top;
862 else /* sub level group */
863 return node->node_parent->node_group;
866 int numTreeInfoInGroup(TreeInfo *node)
868 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
871 int posTreeInfo(TreeInfo *node)
873 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
878 if (node_cmp == node)
882 node_cmp = node_cmp->next;
888 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
890 TreeInfo *node_default = node;
905 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
907 if (identifier == NULL)
912 if (node->node_group)
914 TreeInfo *node_group;
916 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
921 else if (!node->parent_link)
923 if (strcmp(identifier, node->identifier) == 0)
933 void dumpTreeInfo(TreeInfo *node, int depth)
937 printf("Dumping TreeInfo:\n");
941 for (i = 0; i < (depth + 1) * 3; i++)
945 printf("filename == '%s' ['%s', '%s'] [%d])\n",
946 node->filename, node->fullpath, node->basepath, node->user_defined);
948 printf("filename == '%s' (%s) [%s] (%d)\n",
949 node->filename, node->name, node->identifier, node->sort_priority);
952 if (node->node_group != NULL)
953 dumpTreeInfo(node->node_group, depth + 1);
959 void sortTreeInfo(TreeInfo **node_first,
960 int (*compare_function)(const void *, const void *))
962 int num_nodes = numTreeInfo(*node_first);
963 TreeInfo **sort_array;
964 TreeInfo *node = *node_first;
970 /* allocate array for sorting structure pointers */
971 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
973 /* writing structure pointers to sorting array */
974 while (i < num_nodes && node) /* double boundary check... */
976 sort_array[i] = node;
982 /* sorting the structure pointers in the sorting array */
983 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
986 /* update the linkage of list elements with the sorted node array */
987 for (i = 0; i < num_nodes - 1; i++)
988 sort_array[i]->next = sort_array[i + 1];
989 sort_array[num_nodes - 1]->next = NULL;
991 /* update the linkage of the main list anchor pointer */
992 *node_first = sort_array[0];
996 /* now recursively sort the level group structures */
1000 if (node->node_group != NULL)
1001 sortTreeInfo(&node->node_group, compare_function);
1008 /* ========================================================================= */
1009 /* some stuff from "files.c" */
1010 /* ========================================================================= */
1012 #if defined(PLATFORM_WIN32)
1014 #define S_IRGRP S_IRUSR
1017 #define S_IROTH S_IRUSR
1020 #define S_IWGRP S_IWUSR
1023 #define S_IWOTH S_IWUSR
1026 #define S_IXGRP S_IXUSR
1029 #define S_IXOTH S_IXUSR
1032 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1037 #endif /* PLATFORM_WIN32 */
1039 /* file permissions for newly written files */
1040 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1041 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1042 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1044 #define MODE_W_PRIVATE (S_IWUSR)
1045 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1046 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1048 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1049 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1051 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1052 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1054 char *getUserDataDir(void)
1056 static char *userdata_dir = NULL;
1058 if (userdata_dir == NULL)
1059 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1061 return userdata_dir;
1064 char *getCommonDataDir(void)
1066 static char *common_data_dir = NULL;
1068 #if defined(PLATFORM_WIN32)
1069 if (common_data_dir == NULL)
1071 char *dir = checked_malloc(MAX_PATH + 1);
1073 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1074 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1075 common_data_dir = getPath2(dir, program.userdata_directory);
1077 common_data_dir = options.rw_base_directory;
1080 if (common_data_dir == NULL)
1081 common_data_dir = options.rw_base_directory;
1084 return common_data_dir;
1089 return getUserDataDir();
1092 static mode_t posix_umask(mode_t mask)
1094 #if defined(PLATFORM_UNIX)
1101 static int posix_mkdir(const char *pathname, mode_t mode)
1103 #if defined(PLATFORM_WIN32)
1104 return mkdir(pathname);
1106 return mkdir(pathname, mode);
1110 void createDirectory(char *dir, char *text, int permission_class)
1112 /* leave "other" permissions in umask untouched, but ensure group parts
1113 of USERDATA_DIR_MODE are not masked */
1114 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1115 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1116 mode_t normal_umask = posix_umask(0);
1117 mode_t group_umask = ~(dir_mode & S_IRWXG);
1118 posix_umask(normal_umask & group_umask);
1120 if (access(dir, F_OK) != 0)
1121 if (posix_mkdir(dir, dir_mode) != 0)
1122 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1124 posix_umask(normal_umask); /* reset normal umask */
1127 void InitUserDataDirectory()
1129 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1132 void SetFilePermissions(char *filename, int permission_class)
1134 chmod(filename, (permission_class == PERMS_PRIVATE ?
1135 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1138 char *getCookie(char *file_type)
1140 static char cookie[MAX_COOKIE_LEN + 1];
1142 if (strlen(program.cookie_prefix) + 1 +
1143 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1144 return "[COOKIE ERROR]"; /* should never happen */
1146 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1147 program.cookie_prefix, file_type,
1148 program.version_major, program.version_minor);
1153 int getFileVersionFromCookieString(const char *cookie)
1155 const char *ptr_cookie1, *ptr_cookie2;
1156 const char *pattern1 = "_FILE_VERSION_";
1157 const char *pattern2 = "?.?";
1158 const int len_cookie = strlen(cookie);
1159 const int len_pattern1 = strlen(pattern1);
1160 const int len_pattern2 = strlen(pattern2);
1161 const int len_pattern = len_pattern1 + len_pattern2;
1162 int version_major, version_minor;
1164 if (len_cookie <= len_pattern)
1167 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1168 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1170 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1173 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1174 ptr_cookie2[1] != '.' ||
1175 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1178 version_major = ptr_cookie2[0] - '0';
1179 version_minor = ptr_cookie2[2] - '0';
1181 return VERSION_IDENT(version_major, version_minor, 0, 0);
1184 boolean checkCookieString(const char *cookie, const char *template)
1186 const char *pattern = "_FILE_VERSION_?.?";
1187 const int len_cookie = strlen(cookie);
1188 const int len_template = strlen(template);
1189 const int len_pattern = strlen(pattern);
1191 if (len_cookie != len_template)
1194 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1200 /* ------------------------------------------------------------------------- */
1201 /* setup file list and hash handling functions */
1202 /* ------------------------------------------------------------------------- */
1204 char *getFormattedSetupEntry(char *token, char *value)
1207 static char entry[MAX_LINE_LEN];
1209 /* if value is an empty string, just return token without value */
1213 /* start with the token and some spaces to format output line */
1214 sprintf(entry, "%s:", token);
1215 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1218 /* continue with the token's value */
1219 strcat(entry, value);
1224 SetupFileList *newSetupFileList(char *token, char *value)
1226 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1228 new->token = getStringCopy(token);
1229 new->value = getStringCopy(value);
1236 void freeSetupFileList(SetupFileList *list)
1246 freeSetupFileList(list->next);
1250 char *getListEntry(SetupFileList *list, char *token)
1255 if (strcmp(list->token, token) == 0)
1258 return getListEntry(list->next, token);
1261 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1266 if (strcmp(list->token, token) == 0)
1271 list->value = getStringCopy(value);
1275 else if (list->next == NULL)
1276 return (list->next = newSetupFileList(token, value));
1278 return setListEntry(list->next, token, value);
1281 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1286 if (list->next == NULL)
1287 return (list->next = newSetupFileList(token, value));
1289 return addListEntry(list->next, token, value);
1293 static void printSetupFileList(SetupFileList *list)
1298 printf("token: '%s'\n", list->token);
1299 printf("value: '%s'\n", list->value);
1301 printSetupFileList(list->next);
1306 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1307 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1308 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1309 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1311 #define insert_hash_entry hashtable_insert
1312 #define search_hash_entry hashtable_search
1313 #define change_hash_entry hashtable_change
1314 #define remove_hash_entry hashtable_remove
1317 static unsigned int get_hash_from_key(void *key)
1322 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1323 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1324 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1325 it works better than many other constants, prime or not) has never been
1326 adequately explained.
1328 If you just want to have a good hash function, and cannot wait, djb2
1329 is one of the best string hash functions i know. It has excellent
1330 distribution and speed on many different sets of keys and table sizes.
1331 You are not likely to do better with one of the "well known" functions
1332 such as PJW, K&R, etc.
1334 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1337 char *str = (char *)key;
1338 unsigned int hash = 5381;
1341 while ((c = *str++))
1342 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1347 static int keys_are_equal(void *key1, void *key2)
1349 return (strcmp((char *)key1, (char *)key2) == 0);
1352 SetupFileHash *newSetupFileHash()
1354 SetupFileHash *new_hash =
1355 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1357 if (new_hash == NULL)
1358 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1363 void freeSetupFileHash(SetupFileHash *hash)
1368 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1371 char *getHashEntry(SetupFileHash *hash, char *token)
1376 return search_hash_entry(hash, token);
1379 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1386 value_copy = getStringCopy(value);
1388 /* change value; if it does not exist, insert it as new */
1389 if (!change_hash_entry(hash, token, value_copy))
1390 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1391 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1394 char *removeHashEntry(SetupFileHash *hash, char *token)
1399 return remove_hash_entry(hash, token);
1404 static void printSetupFileHash(SetupFileHash *hash)
1406 BEGIN_HASH_ITERATION(hash, itr)
1408 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1409 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1411 END_HASH_ITERATION(hash, itr)
1416 static void *loadSetupFileData(char *filename, boolean use_hash)
1418 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1419 char *token, *value, *line_ptr;
1420 void *setup_file_data, *insert_ptr = NULL;
1421 boolean read_continued_line = FALSE;
1425 setup_file_data = newSetupFileHash();
1427 insert_ptr = setup_file_data = newSetupFileList("", "");
1429 if (!(file = fopen(filename, MODE_READ)))
1431 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1437 /* read next line of input file */
1438 if (!fgets(line, MAX_LINE_LEN, file))
1441 /* cut trailing newline or carriage return */
1442 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1443 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1446 if (read_continued_line)
1448 /* cut leading whitespaces from input line */
1449 for (line_ptr = line; *line_ptr; line_ptr++)
1450 if (*line_ptr != ' ' && *line_ptr != '\t')
1453 /* append new line to existing line, if there is enough space */
1454 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1455 strcat(previous_line, line_ptr);
1457 strcpy(line, previous_line); /* copy storage buffer to line */
1459 read_continued_line = FALSE;
1462 /* if the last character is '\', continue at next line */
1463 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1465 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1466 strcpy(previous_line, line); /* copy line to storage buffer */
1468 read_continued_line = TRUE;
1473 /* cut trailing comment from input line */
1474 for (line_ptr = line; *line_ptr; line_ptr++)
1476 if (*line_ptr == '#')
1483 /* cut trailing whitespaces from input line */
1484 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1485 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1488 /* ignore empty lines */
1492 /* cut leading whitespaces from token */
1493 for (token = line; *token; token++)
1494 if (*token != ' ' && *token != '\t')
1497 /* start with empty value as reliable default */
1500 /* find end of token to determine start of value */
1501 for (line_ptr = token; *line_ptr; line_ptr++)
1503 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1505 *line_ptr = '\0'; /* terminate token string */
1506 value = line_ptr + 1; /* set beginning of value */
1512 /* cut leading whitespaces from value */
1513 for (; *value; value++)
1514 if (*value != ' ' && *value != '\t')
1519 value = "true"; /* treat tokens without value as "true" */
1525 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1527 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1535 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1536 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1540 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1541 SetupFileList *first_valid_list_entry = setup_file_list->next;
1543 /* free empty list header */
1544 setup_file_list->next = NULL;
1545 freeSetupFileList(setup_file_list);
1546 setup_file_data = first_valid_list_entry;
1548 if (first_valid_list_entry == NULL)
1549 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1552 return setup_file_data;
1555 SetupFileList *loadSetupFileList(char *filename)
1557 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1560 SetupFileHash *loadSetupFileHash(char *filename)
1562 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1565 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1568 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1571 Error(ERR_WARN, "configuration file has no file identifier");
1572 else if (!checkCookieString(value, identifier))
1573 Error(ERR_WARN, "configuration file has wrong file identifier");
1577 /* ========================================================================= */
1578 /* setup file stuff */
1579 /* ========================================================================= */
1581 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1582 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1583 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1585 /* level directory info */
1586 #define LEVELINFO_TOKEN_IDENTIFIER 0
1587 #define LEVELINFO_TOKEN_NAME 1
1588 #define LEVELINFO_TOKEN_NAME_SORTING 2
1589 #define LEVELINFO_TOKEN_AUTHOR 3
1590 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1591 #define LEVELINFO_TOKEN_LEVELS 5
1592 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1593 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1594 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1595 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1596 #define LEVELINFO_TOKEN_READONLY 10
1597 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1598 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1599 #define LEVELINFO_TOKEN_MUSIC_SET 13
1601 #define NUM_LEVELINFO_TOKENS 14
1603 static LevelDirTree ldi;
1605 static struct TokenInfo levelinfo_tokens[] =
1607 /* level directory info */
1608 { TYPE_STRING, &ldi.identifier, "identifier" },
1609 { TYPE_STRING, &ldi.name, "name" },
1610 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1611 { TYPE_STRING, &ldi.author, "author" },
1612 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1613 { TYPE_INTEGER, &ldi.levels, "levels" },
1614 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1615 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1616 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1617 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1618 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1619 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1620 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1621 { TYPE_STRING, &ldi.music_set, "music_set" }
1624 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1628 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1629 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1630 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1631 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1634 ldi->node_parent = NULL;
1635 ldi->node_group = NULL;
1639 ldi->cl_cursor = -1;
1641 ldi->filename = NULL;
1642 ldi->fullpath = NULL;
1643 ldi->basepath = NULL;
1644 ldi->identifier = NULL;
1645 ldi->name = getStringCopy(ANONYMOUS_NAME);
1646 ldi->name_sorting = NULL;
1647 ldi->author = getStringCopy(ANONYMOUS_NAME);
1649 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1650 ldi->latest_engine = FALSE; /* default: get from level */
1651 ldi->parent_link = FALSE;
1652 ldi->user_defined = FALSE;
1654 ldi->class_desc = NULL;
1656 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1658 ldi->imported_from = NULL;
1660 ldi->graphics_set = NULL;
1661 ldi->sounds_set = NULL;
1662 ldi->music_set = NULL;
1663 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1664 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1665 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1668 ldi->first_level = 0;
1669 ldi->last_level = 0;
1670 ldi->level_group = FALSE;
1671 ldi->handicap_level = 0;
1672 ldi->readonly = TRUE;
1676 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1680 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1682 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1688 /* copy all values from the parent structure */
1690 ldi->type = parent->type;
1692 ldi->node_top = parent->node_top;
1693 ldi->node_parent = parent;
1694 ldi->node_group = NULL;
1698 ldi->cl_cursor = -1;
1700 ldi->filename = NULL;
1701 ldi->fullpath = NULL;
1702 ldi->basepath = NULL;
1703 ldi->identifier = NULL;
1704 ldi->name = getStringCopy(ANONYMOUS_NAME);
1705 ldi->name_sorting = NULL;
1706 ldi->author = getStringCopy(parent->author);
1708 ldi->sort_priority = parent->sort_priority;
1709 ldi->latest_engine = parent->latest_engine;
1710 ldi->parent_link = FALSE;
1711 ldi->user_defined = parent->user_defined;
1712 ldi->color = parent->color;
1713 ldi->class_desc = getStringCopy(parent->class_desc);
1715 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1717 ldi->imported_from = getStringCopy(parent->imported_from);
1719 ldi->graphics_set = NULL;
1720 ldi->sounds_set = NULL;
1721 ldi->music_set = NULL;
1722 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1723 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1724 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1727 ldi->first_level = 0;
1728 ldi->last_level = 0;
1729 ldi->level_group = FALSE;
1730 ldi->handicap_level = 0;
1731 ldi->readonly = TRUE;
1737 /* first copy all values from the parent structure ... */
1740 /* ... then set all fields to default that cannot be inherited from parent.
1741 This is especially important for all those fields that can be set from
1742 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1743 calls 'free()' for all already set token values which requires that no
1744 other structure's pointer may point to them!
1747 ldi->filename = NULL;
1748 ldi->fullpath = NULL;
1749 ldi->basepath = NULL;
1750 ldi->identifier = NULL;
1751 ldi->name = getStringCopy(ANONYMOUS_NAME);
1752 ldi->name_sorting = NULL;
1753 ldi->author = getStringCopy(parent->author);
1755 ldi->imported_from = getStringCopy(parent->imported_from);
1756 ldi->class_desc = getStringCopy(parent->class_desc);
1758 ldi->graphics_set = NULL;
1759 ldi->sounds_set = NULL;
1760 ldi->music_set = NULL;
1761 ldi->graphics_path = NULL;
1762 ldi->sounds_path = NULL;
1763 ldi->music_path = NULL;
1765 ldi->level_group = FALSE;
1766 ldi->parent_link = FALSE;
1768 ldi->node_top = parent->node_top;
1769 ldi->node_parent = parent;
1770 ldi->node_group = NULL;
1776 static void freeTreeInfo(TreeInfo *ldi)
1779 free(ldi->filename);
1781 free(ldi->fullpath);
1783 free(ldi->basepath);
1784 if (ldi->identifier)
1785 free(ldi->identifier);
1789 if (ldi->name_sorting)
1790 free(ldi->name_sorting);
1794 if (ldi->class_desc)
1795 free(ldi->class_desc);
1797 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1799 if (ldi->graphics_set)
1800 free(ldi->graphics_set);
1801 if (ldi->sounds_set)
1802 free(ldi->sounds_set);
1804 free(ldi->music_set);
1806 if (ldi->graphics_path)
1807 free(ldi->graphics_path);
1808 if (ldi->sounds_path)
1809 free(ldi->sounds_path);
1810 if (ldi->music_path)
1811 free(ldi->music_path);
1815 void setSetupInfo(struct TokenInfo *token_info,
1816 int token_nr, char *token_value)
1818 int token_type = token_info[token_nr].type;
1819 void *setup_value = token_info[token_nr].value;
1821 if (token_value == NULL)
1824 /* set setup field to corresponding token value */
1829 *(boolean *)setup_value = get_boolean_from_string(token_value);
1833 *(Key *)setup_value = getKeyFromKeyName(token_value);
1837 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1841 *(int *)setup_value = get_integer_from_string(token_value);
1845 if (*(char **)setup_value != NULL)
1846 free(*(char **)setup_value);
1847 *(char **)setup_value = getStringCopy(token_value);
1855 static int compareTreeInfoEntries(const void *object1, const void *object2)
1857 const TreeInfo *entry1 = *((TreeInfo **)object1);
1858 const TreeInfo *entry2 = *((TreeInfo **)object2);
1859 int class_sorting1, class_sorting2;
1862 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1864 class_sorting1 = LEVELSORTING(entry1);
1865 class_sorting2 = LEVELSORTING(entry2);
1869 class_sorting1 = ARTWORKSORTING(entry1);
1870 class_sorting2 = ARTWORKSORTING(entry2);
1873 if (entry1->parent_link || entry2->parent_link)
1874 compare_result = (entry1->parent_link ? -1 : +1);
1875 else if (entry1->sort_priority == entry2->sort_priority)
1877 char *name1 = getStringToLower(entry1->name_sorting);
1878 char *name2 = getStringToLower(entry2->name_sorting);
1880 compare_result = strcmp(name1, name2);
1885 else if (class_sorting1 == class_sorting2)
1886 compare_result = entry1->sort_priority - entry2->sort_priority;
1888 compare_result = class_sorting1 - class_sorting2;
1890 return compare_result;
1893 static void createParentTreeInfoNode(TreeInfo *node_parent)
1897 if (node_parent == NULL)
1900 ti_new = newTreeInfo();
1901 setTreeInfoToDefaults(ti_new, node_parent->type);
1903 ti_new->node_parent = node_parent;
1904 ti_new->parent_link = TRUE;
1907 setString(&ti_new->identifier, node_parent->identifier);
1908 setString(&ti_new->name, ".. (parent directory)");
1909 setString(&ti_new->name_sorting, ti_new->name);
1911 setString(&ti_new->filename, "..");
1912 setString(&ti_new->fullpath, node_parent->fullpath);
1914 ti_new->sort_priority = node_parent->sort_priority;
1915 ti_new->latest_engine = node_parent->latest_engine;
1917 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1919 ti_new->identifier = getStringCopy(node_parent->identifier);
1920 ti_new->name = ".. (parent directory)";
1921 ti_new->name_sorting = getStringCopy(ti_new->name);
1923 ti_new->filename = "..";
1924 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1926 ti_new->sort_priority = node_parent->sort_priority;
1927 ti_new->latest_engine = node_parent->latest_engine;
1929 ti_new->class_desc = getLevelClassDescription(ti_new);
1932 pushTreeInfo(&node_parent->node_group, ti_new);
1935 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1936 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1938 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1939 TreeInfo *node_parent,
1940 char *level_directory,
1941 char *directory_name)
1943 char *directory_path = getPath2(level_directory, directory_name);
1944 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1945 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1946 LevelDirTree *leveldir_new = NULL;
1949 if (setup_file_hash == NULL)
1951 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1953 free(directory_path);
1959 leveldir_new = newTreeInfo();
1962 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1964 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1966 leveldir_new->filename = getStringCopy(directory_name);
1968 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1970 /* set all structure fields according to the token/value pairs */
1971 ldi = *leveldir_new;
1972 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1973 setSetupInfo(levelinfo_tokens, i,
1974 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1975 *leveldir_new = ldi;
1978 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1979 setString(&leveldir_new->name, leveldir_new->filename);
1981 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1983 free(leveldir_new->name);
1984 leveldir_new->name = getStringCopy(leveldir_new->filename);
1988 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1990 if (leveldir_new->identifier == NULL)
1991 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1993 if (leveldir_new->name_sorting == NULL)
1994 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1996 if (node_parent == NULL) /* top level group */
1998 leveldir_new->basepath = getStringCopy(level_directory);
1999 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
2001 else /* sub level group */
2003 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2004 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2007 if (leveldir_new->levels < 1)
2008 leveldir_new->levels = 1;
2010 leveldir_new->last_level =
2011 leveldir_new->first_level + leveldir_new->levels - 1;
2014 leveldir_new->user_defined =
2015 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2017 leveldir_new->user_defined =
2018 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2021 leveldir_new->color = LEVELCOLOR(leveldir_new);
2023 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2025 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2028 leveldir_new->handicap_level = /* set handicap to default value */
2029 (leveldir_new->user_defined ?
2030 leveldir_new->last_level :
2031 leveldir_new->first_level);
2033 pushTreeInfo(node_first, leveldir_new);
2035 freeSetupFileHash(setup_file_hash);
2037 if (leveldir_new->level_group)
2039 /* create node to link back to current level directory */
2040 createParentTreeInfoNode(leveldir_new);
2042 /* step into sub-directory and look for more level series */
2043 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2044 leveldir_new, directory_path);
2047 free(directory_path);
2053 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2054 TreeInfo *node_parent,
2055 char *level_directory)
2058 struct dirent *dir_entry;
2059 boolean valid_entry_found = FALSE;
2061 if ((dir = opendir(level_directory)) == NULL)
2063 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2067 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2069 struct stat file_status;
2070 char *directory_name = dir_entry->d_name;
2071 char *directory_path = getPath2(level_directory, directory_name);
2073 /* skip entries for current and parent directory */
2074 if (strcmp(directory_name, ".") == 0 ||
2075 strcmp(directory_name, "..") == 0)
2077 free(directory_path);
2081 /* find out if directory entry is itself a directory */
2082 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2083 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2085 free(directory_path);
2089 free(directory_path);
2091 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2092 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2093 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2096 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2103 if (!valid_entry_found)
2105 /* check if this directory directly contains a file "levelinfo.conf" */
2106 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2107 level_directory, ".");
2110 if (!valid_entry_found)
2111 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2115 void LoadLevelInfo()
2117 InitUserLevelDirectory(getLoginName());
2119 DrawInitText("Loading level series:", 120, FC_GREEN);
2121 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2122 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2124 /* before sorting, the first entries will be from the user directory */
2125 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2127 if (leveldir_first == NULL)
2128 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2130 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2133 dumpTreeInfo(leveldir_first, 0);
2137 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2138 TreeInfo *node_parent,
2139 char *base_directory,
2140 char *directory_name, int type)
2142 char *directory_path = getPath2(base_directory, directory_name);
2143 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2144 SetupFileHash *setup_file_hash = NULL;
2145 TreeInfo *artwork_new = NULL;
2148 if (access(filename, F_OK) == 0) /* file exists */
2149 setup_file_hash = loadSetupFileHash(filename);
2151 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2154 struct dirent *dir_entry;
2155 boolean valid_file_found = FALSE;
2157 if ((dir = opendir(directory_path)) != NULL)
2159 while ((dir_entry = readdir(dir)) != NULL)
2161 char *entry_name = dir_entry->d_name;
2163 if (FileIsArtworkType(entry_name, type))
2165 valid_file_found = TRUE;
2173 if (!valid_file_found)
2175 if (strcmp(directory_name, ".") != 0)
2176 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2178 free(directory_path);
2185 artwork_new = newTreeInfo();
2188 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2190 setTreeInfoToDefaults(artwork_new, type);
2192 artwork_new->filename = getStringCopy(directory_name);
2194 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2197 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2200 /* set all structure fields according to the token/value pairs */
2202 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2203 setSetupInfo(levelinfo_tokens, i,
2204 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2208 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2209 setString(&artwork_new->name, artwork_new->filename);
2211 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2213 free(artwork_new->name);
2214 artwork_new->name = getStringCopy(artwork_new->filename);
2219 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2222 if (artwork_new->identifier == NULL)
2223 artwork_new->identifier = getStringCopy(artwork_new->filename);
2225 if (artwork_new->name_sorting == NULL)
2226 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2229 if (node_parent == NULL) /* top level group */
2231 artwork_new->basepath = getStringCopy(base_directory);
2232 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2234 else /* sub level group */
2236 artwork_new->basepath = getStringCopy(node_parent->basepath);
2237 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2241 artwork_new->user_defined =
2242 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2244 artwork_new->user_defined =
2245 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2248 /* (may use ".sort_priority" from "setup_file_hash" above) */
2249 artwork_new->color = ARTWORKCOLOR(artwork_new);
2251 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2253 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2256 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2259 if (artwork_new->name != NULL)
2261 free(artwork_new->name);
2262 artwork_new->name = NULL;
2267 if (artwork_new->identifier != NULL)
2269 free(artwork_new->identifier);
2270 artwork_new->identifier = NULL;
2274 if (strcmp(artwork_new->filename, ".") == 0)
2276 if (artwork_new->user_defined)
2279 setString(&artwork_new->identifier, "private");
2281 artwork_new->identifier = getStringCopy("private");
2283 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2288 setString(&artwork_new->identifier, "classic");
2290 artwork_new->identifier = getStringCopy("classic");
2292 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2295 /* set to new values after changing ".sort_priority" */
2296 artwork_new->color = ARTWORKCOLOR(artwork_new);
2298 setString(&artwork_new->class_desc,
2299 getLevelClassDescription(artwork_new));
2301 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2307 setString(&artwork_new->identifier, artwork_new->filename);
2309 artwork_new->identifier = getStringCopy(artwork_new->filename);
2314 setString(&artwork_new->name, artwork_new->identifier);
2315 setString(&artwork_new->name_sorting, artwork_new->name);
2317 artwork_new->name = getStringCopy(artwork_new->identifier);
2318 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2322 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2324 pushTreeInfo(node_first, artwork_new);
2326 freeSetupFileHash(setup_file_hash);
2328 free(directory_path);
2334 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2335 TreeInfo *node_parent,
2336 char *base_directory, int type)
2339 struct dirent *dir_entry;
2340 boolean valid_entry_found = FALSE;
2342 if ((dir = opendir(base_directory)) == NULL)
2344 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2345 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2349 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2351 struct stat file_status;
2352 char *directory_name = dir_entry->d_name;
2353 char *directory_path = getPath2(base_directory, directory_name);
2355 /* skip entries for current and parent directory */
2356 if (strcmp(directory_name, ".") == 0 ||
2357 strcmp(directory_name, "..") == 0)
2359 free(directory_path);
2363 /* find out if directory entry is itself a directory */
2364 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2365 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2367 free(directory_path);
2371 free(directory_path);
2373 /* check if this directory contains artwork with or without config file */
2374 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2376 directory_name, type);
2381 /* check if this directory directly contains artwork itself */
2382 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2383 base_directory, ".",
2385 if (!valid_entry_found)
2386 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2390 static TreeInfo *getDummyArtworkInfo(int type)
2392 /* this is only needed when there is completely no artwork available */
2393 TreeInfo *artwork_new = newTreeInfo();
2395 setTreeInfoToDefaults(artwork_new, type);
2398 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2399 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2400 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2402 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2403 setString(&artwork_new->name, UNDEFINED_FILENAME);
2404 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2406 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2407 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2408 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2410 if (artwork_new->name != NULL)
2411 free(artwork_new->name);
2413 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2414 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2415 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2421 void LoadArtworkInfo()
2423 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2425 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2426 options.graphics_directory,
2427 TREE_TYPE_GRAPHICS_DIR);
2428 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2429 getUserGraphicsDir(),
2430 TREE_TYPE_GRAPHICS_DIR);
2432 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2433 options.sounds_directory,
2434 TREE_TYPE_SOUNDS_DIR);
2435 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2437 TREE_TYPE_SOUNDS_DIR);
2439 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2440 options.music_directory,
2441 TREE_TYPE_MUSIC_DIR);
2442 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2444 TREE_TYPE_MUSIC_DIR);
2446 if (artwork.gfx_first == NULL)
2447 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2448 if (artwork.snd_first == NULL)
2449 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2450 if (artwork.mus_first == NULL)
2451 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2453 /* before sorting, the first entries will be from the user directory */
2454 artwork.gfx_current =
2455 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2456 if (artwork.gfx_current == NULL)
2457 artwork.gfx_current =
2458 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2459 if (artwork.gfx_current == NULL)
2460 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2462 artwork.snd_current =
2463 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2464 if (artwork.snd_current == NULL)
2465 artwork.snd_current =
2466 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2467 if (artwork.snd_current == NULL)
2468 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2470 artwork.mus_current =
2471 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2472 if (artwork.mus_current == NULL)
2473 artwork.mus_current =
2474 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2475 if (artwork.mus_current == NULL)
2476 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2478 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2479 artwork.snd_current_identifier = artwork.snd_current->identifier;
2480 artwork.mus_current_identifier = artwork.mus_current->identifier;
2483 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2484 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2485 printf("music set == %s\n\n", artwork.mus_current_identifier);
2488 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2489 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2490 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2493 dumpTreeInfo(artwork.gfx_first, 0);
2494 dumpTreeInfo(artwork.snd_first, 0);
2495 dumpTreeInfo(artwork.mus_first, 0);
2499 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2500 LevelDirTree *level_node)
2502 /* recursively check all level directories for artwork sub-directories */
2506 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2507 ARTWORK_DIRECTORY((*artwork_node)->type));
2510 if (!level_node->parent_link)
2511 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2512 level_node->filename, level_node->name);
2515 if (!level_node->parent_link)
2517 TreeInfo *topnode_last = *artwork_node;
2519 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2520 (*artwork_node)->type);
2522 if (topnode_last != *artwork_node)
2524 free((*artwork_node)->identifier);
2525 free((*artwork_node)->name);
2526 free((*artwork_node)->name_sorting);
2528 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2529 (*artwork_node)->name = getStringCopy(level_node->name);
2530 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2532 (*artwork_node)->sort_priority = level_node->sort_priority;
2533 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2539 if (level_node->node_group != NULL)
2540 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2542 level_node = level_node->next;
2546 void LoadLevelArtworkInfo()
2548 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2550 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2551 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2552 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2554 /* needed for reloading level artwork not known at ealier stage */
2556 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2558 artwork.gfx_current =
2559 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2560 if (artwork.gfx_current == NULL)
2561 artwork.gfx_current =
2562 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2563 if (artwork.gfx_current == NULL)
2564 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2567 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2569 artwork.snd_current =
2570 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2571 if (artwork.snd_current == NULL)
2572 artwork.snd_current =
2573 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2574 if (artwork.snd_current == NULL)
2575 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2578 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2580 artwork.mus_current =
2581 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2582 if (artwork.mus_current == NULL)
2583 artwork.mus_current =
2584 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2585 if (artwork.mus_current == NULL)
2586 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2589 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2590 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2591 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2594 dumpTreeInfo(artwork.gfx_first, 0);
2595 dumpTreeInfo(artwork.snd_first, 0);
2596 dumpTreeInfo(artwork.mus_first, 0);
2600 static void SaveUserLevelInfo()
2602 LevelDirTree *level_info;
2607 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2609 if (!(file = fopen(filename, MODE_WRITE)))
2611 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2616 level_info = newTreeInfo();
2618 /* always start with reliable default values */
2619 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2622 setString(&level_info->name, getLoginName());
2623 setString(&level_info->author, getRealName());
2624 level_info->levels = 100;
2625 level_info->first_level = 1;
2626 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2627 level_info->readonly = FALSE;
2628 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2629 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2630 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2632 ldi.name = getStringCopy(getLoginName());
2633 ldi.author = getStringCopy(getRealName());
2635 ldi.first_level = 1;
2636 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2637 ldi.readonly = FALSE;
2638 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2639 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2640 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2643 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2644 getCookie("LEVELINFO")));
2647 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2648 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2649 i != LEVELINFO_TOKEN_NAME_SORTING &&
2650 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2651 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2655 SetFilePermissions(filename, PERMS_PRIVATE);
2657 freeTreeInfo(level_info);
2661 char *getSetupValue(int type, void *value)
2663 static char value_string[MAX_LINE_LEN];
2671 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2675 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2679 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2683 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2687 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2691 sprintf(value_string, "%d", *(int *)value);
2695 strcpy(value_string, *(char **)value);
2699 value_string[0] = '\0';
2703 return value_string;
2706 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2710 static char token_string[MAX_LINE_LEN];
2711 int token_type = token_info[token_nr].type;
2712 void *setup_value = token_info[token_nr].value;
2713 char *token_text = token_info[token_nr].text;
2714 char *value_string = getSetupValue(token_type, setup_value);
2716 /* build complete token string */
2717 sprintf(token_string, "%s%s", prefix, token_text);
2719 /* build setup entry line */
2720 line = getFormattedSetupEntry(token_string, value_string);
2722 if (token_type == TYPE_KEY_X11)
2724 Key key = *(Key *)setup_value;
2725 char *keyname = getKeyNameFromKey(key);
2727 /* add comment, if useful */
2728 if (strcmp(keyname, "(undefined)") != 0 &&
2729 strcmp(keyname, "(unknown)") != 0)
2731 /* add at least one whitespace */
2733 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2737 strcat(line, keyname);
2744 void LoadLevelSetup_LastSeries()
2746 /* ----------------------------------------------------------------------- */
2747 /* ~/.<program>/levelsetup.conf */
2748 /* ----------------------------------------------------------------------- */
2750 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2751 SetupFileHash *level_setup_hash = NULL;
2753 /* always start with reliable default values */
2754 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2756 if ((level_setup_hash = loadSetupFileHash(filename)))
2758 char *last_level_series =
2759 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2761 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2763 if (leveldir_current == NULL)
2764 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2766 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2768 freeSetupFileHash(level_setup_hash);
2771 Error(ERR_WARN, "using default setup values");
2776 void SaveLevelSetup_LastSeries()
2778 /* ----------------------------------------------------------------------- */
2779 /* ~/.<program>/levelsetup.conf */
2780 /* ----------------------------------------------------------------------- */
2782 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2783 char *level_subdir = leveldir_current->filename;
2786 InitUserDataDirectory();
2788 if (!(file = fopen(filename, MODE_WRITE)))
2790 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2795 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2796 getCookie("LEVELSETUP")));
2797 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2802 SetFilePermissions(filename, PERMS_PRIVATE);
2807 static void checkSeriesInfo()
2809 static char *level_directory = NULL;
2811 struct dirent *dir_entry;
2813 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2815 level_directory = getPath2((leveldir_current->user_defined ?
2816 getUserLevelDir(NULL) :
2817 options.level_directory),
2818 leveldir_current->fullpath);
2820 if ((dir = opendir(level_directory)) == NULL)
2822 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2826 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2828 if (strlen(dir_entry->d_name) > 4 &&
2829 dir_entry->d_name[3] == '.' &&
2830 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2832 char levelnum_str[4];
2835 strncpy(levelnum_str, dir_entry->d_name, 3);
2836 levelnum_str[3] = '\0';
2838 levelnum_value = atoi(levelnum_str);
2841 if (levelnum_value < leveldir_current->first_level)
2843 Error(ERR_WARN, "additional level %d found", levelnum_value);
2844 leveldir_current->first_level = levelnum_value;
2846 else if (levelnum_value > leveldir_current->last_level)
2848 Error(ERR_WARN, "additional level %d found", levelnum_value);
2849 leveldir_current->last_level = levelnum_value;
2858 void LoadLevelSetup_SeriesInfo()
2861 SetupFileHash *level_setup_hash = NULL;
2862 char *level_subdir = leveldir_current->filename;
2864 /* always start with reliable default values */
2865 level_nr = leveldir_current->first_level;
2867 checkSeriesInfo(leveldir_current);
2869 /* ----------------------------------------------------------------------- */
2870 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2871 /* ----------------------------------------------------------------------- */
2873 level_subdir = leveldir_current->filename;
2875 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2877 if ((level_setup_hash = loadSetupFileHash(filename)))
2881 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2885 level_nr = atoi(token_value);
2887 if (level_nr < leveldir_current->first_level)
2888 level_nr = leveldir_current->first_level;
2889 if (level_nr > leveldir_current->last_level)
2890 level_nr = leveldir_current->last_level;
2893 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2897 int level_nr = atoi(token_value);
2899 if (level_nr < leveldir_current->first_level)
2900 level_nr = leveldir_current->first_level;
2901 if (level_nr > leveldir_current->last_level + 1)
2902 level_nr = leveldir_current->last_level;
2904 if (leveldir_current->user_defined)
2905 level_nr = leveldir_current->last_level;
2907 leveldir_current->handicap_level = level_nr;
2910 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2912 freeSetupFileHash(level_setup_hash);
2915 Error(ERR_WARN, "using default setup values");
2920 void SaveLevelSetup_SeriesInfo()
2923 char *level_subdir = leveldir_current->filename;
2924 char *level_nr_str = int2str(level_nr, 0);
2925 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2928 /* ----------------------------------------------------------------------- */
2929 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2930 /* ----------------------------------------------------------------------- */
2932 InitLevelSetupDirectory(level_subdir);
2934 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2936 if (!(file = fopen(filename, MODE_WRITE)))
2938 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2943 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2944 getCookie("LEVELSETUP")));
2945 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2947 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2948 handicap_level_str));
2952 SetFilePermissions(filename, PERMS_PRIVATE);