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>
26 /* file names and filename extensions */
27 #if !defined(PLATFORM_MSDOS)
28 #define LEVELSETUP_DIRECTORY "levelsetup"
29 #define SETUP_FILENAME "setup.conf"
30 #define LEVELSETUP_FILENAME "levelsetup.conf"
31 #define LEVELINFO_FILENAME "levelinfo.conf"
32 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
33 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
34 #define MUSICINFO_FILENAME "musicinfo.conf"
35 #define LEVELFILE_EXTENSION "level"
36 #define TAPEFILE_EXTENSION "tape"
37 #define SCOREFILE_EXTENSION "score"
39 #define LEVELSETUP_DIRECTORY "lvlsetup"
40 #define SETUP_FILENAME "setup.cnf"
41 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
42 #define LEVELINFO_FILENAME "lvlinfo.cnf"
43 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
44 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
45 #define MUSICINFO_FILENAME "musinfo.cnf"
46 #define LEVELFILE_EXTENSION "lvl"
47 #define TAPEFILE_EXTENSION "tap"
48 #define SCOREFILE_EXTENSION "sco"
51 #define NUM_LEVELCLASS_DESC 8
52 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
64 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
65 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
66 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
67 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
68 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
69 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
70 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
71 IS_LEVELCLASS_USER(n) ? FC_RED : \
74 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
75 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
76 IS_LEVELCLASS_BD(n) ? 2 : \
77 IS_LEVELCLASS_EM(n) ? 3 : \
78 IS_LEVELCLASS_SP(n) ? 4 : \
79 IS_LEVELCLASS_DX(n) ? 5 : \
80 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
81 IS_LEVELCLASS_USER(n) ? 7 : \
84 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
85 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
86 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
87 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
90 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
91 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
92 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
93 IS_ARTWORKCLASS_USER(n) ? 3 : \
96 #define TOKEN_VALUE_POSITION 40
97 #define TOKEN_COMMENT_POSITION 60
99 #define MAX_COOKIE_LEN 256
101 #define ARTWORKINFO_FILENAME(type) ((type) == ARTWORK_TYPE_GRAPHICS ? \
102 GRAPHICSINFO_FILENAME : \
103 (type) == ARTWORK_TYPE_SOUNDS ? \
104 SOUNDSINFO_FILENAME : \
105 (type) == ARTWORK_TYPE_MUSIC ? \
106 MUSICINFO_FILENAME : "")
108 #define ARTWORK_DIRECTORY(type) ((type) == ARTWORK_TYPE_GRAPHICS ? \
109 GRAPHICS_DIRECTORY : \
110 (type) == ARTWORK_TYPE_SOUNDS ? \
112 (type) == ARTWORK_TYPE_MUSIC ? \
113 MUSIC_DIRECTORY : "")
115 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == ARTWORK_TYPE_GRAPHICS ? \
116 options.graphics_directory : \
117 (type) == ARTWORK_TYPE_SOUNDS ? \
118 options.sounds_directory : \
119 (type) == ARTWORK_TYPE_MUSIC ? \
120 options.music_directory : "")
123 /* ------------------------------------------------------------------------- */
125 /* ------------------------------------------------------------------------- */
127 static char *getLevelClassDescription(TreeInfo *ldi)
129 int position = ldi->sort_priority / 100;
131 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
132 return levelclass_desc[position];
134 return "Unknown Level Class";
137 static char *getUserLevelDir(char *level_subdir)
139 static char *userlevel_dir = NULL;
140 char *data_dir = getUserDataDir();
141 char *userlevel_subdir = LEVELS_DIRECTORY;
146 if (level_subdir != NULL)
147 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
149 userlevel_dir = getPath2(data_dir, userlevel_subdir);
151 return userlevel_dir;
154 static char *getTapeDir(char *level_subdir)
156 static char *tape_dir = NULL;
157 char *data_dir = getUserDataDir();
158 char *tape_subdir = TAPES_DIRECTORY;
163 if (level_subdir != NULL)
164 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
166 tape_dir = getPath2(data_dir, tape_subdir);
171 static char *getScoreDir(char *level_subdir)
173 static char *score_dir = NULL;
174 char *data_dir = getCommonDataDir();
175 char *score_subdir = SCORES_DIRECTORY;
180 if (level_subdir != NULL)
181 score_dir = getPath3(data_dir, score_subdir, level_subdir);
183 score_dir = getPath2(data_dir, score_subdir);
188 static char *getLevelSetupDir(char *level_subdir)
190 static char *levelsetup_dir = NULL;
191 char *data_dir = getUserDataDir();
192 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
195 free(levelsetup_dir);
197 if (level_subdir != NULL)
198 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
200 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
202 return levelsetup_dir;
205 static char *getLevelDirFromTreeInfo(TreeInfo *node)
207 static char *level_dir = NULL;
210 return options.level_directory;
215 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
216 options.level_directory), node->fullpath);
221 static char *getCurrentLevelDir()
223 return getLevelDirFromTreeInfo(leveldir_current);
226 static char *getDefaultGraphicsDir(char *graphics_subdir)
228 static char *graphics_dir = NULL;
230 if (graphics_subdir == NULL)
231 return options.graphics_directory;
236 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
241 static char *getDefaultSoundsDir(char *sounds_subdir)
243 static char *sounds_dir = NULL;
245 if (sounds_subdir == NULL)
246 return options.sounds_directory;
251 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
256 static char *getDefaultMusicDir(char *music_subdir)
258 static char *music_dir = NULL;
260 if (music_subdir == NULL)
261 return options.music_directory;
266 music_dir = getPath2(options.music_directory, music_subdir);
271 static char *getDefaultArtworkSet(int type)
273 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
274 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
275 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
278 static char *getDefaultArtworkDir(int type)
280 return (type == TREE_TYPE_GRAPHICS_DIR ?
281 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
282 type == TREE_TYPE_SOUNDS_DIR ?
283 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
284 type == TREE_TYPE_MUSIC_DIR ?
285 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
288 static char *getUserGraphicsDir()
290 static char *usergraphics_dir = NULL;
292 if (usergraphics_dir == NULL)
293 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
295 return usergraphics_dir;
298 static char *getUserSoundsDir()
300 static char *usersounds_dir = NULL;
302 if (usersounds_dir == NULL)
303 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
305 return usersounds_dir;
308 static char *getUserMusicDir()
310 static char *usermusic_dir = NULL;
312 if (usermusic_dir == NULL)
313 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
315 return usermusic_dir;
318 static char *getSetupArtworkDir(TreeInfo *ti)
320 static char *artwork_dir = NULL;
322 if (artwork_dir != NULL)
325 artwork_dir = getPath2(ti->basepath, ti->fullpath);
330 char *setLevelArtworkDir(TreeInfo *ti)
332 char **artwork_path_ptr, **artwork_set_ptr;
333 TreeInfo *level_artwork;
335 if (ti == NULL || leveldir_current == NULL)
338 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
339 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
341 if (*artwork_path_ptr != NULL)
342 free(*artwork_path_ptr);
344 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
345 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
348 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
349 normally result in using the artwork configured in the setup menu. But
350 if an artwork subdirectory exists (which might contain custom artwork
351 or an artwork configuration file), this level artwork must be treated
352 as relative to the default "classic" artwork, not to the artwork that
353 is currently configured in the setup menu. */
355 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
357 if (*artwork_set_ptr != NULL)
358 free(*artwork_set_ptr);
362 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
363 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
367 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
368 *artwork_set_ptr = NULL;
374 return *artwork_set_ptr;
377 inline static char *getLevelArtworkSet(int type)
379 if (leveldir_current == NULL)
382 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
385 inline static char *getLevelArtworkDir(int type)
387 if (leveldir_current == NULL)
388 return UNDEFINED_FILENAME;
390 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
393 char *getLevelFilename(int nr)
395 static char *filename = NULL;
396 char basename[MAX_FILENAME_LEN];
398 if (filename != NULL)
402 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
404 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
406 filename = getPath2(getCurrentLevelDir(), basename);
411 char *getTapeFilename(int nr)
413 static char *filename = NULL;
414 char basename[MAX_FILENAME_LEN];
416 if (filename != NULL)
419 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
420 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
425 char *getScoreFilename(int nr)
427 static char *filename = NULL;
428 char basename[MAX_FILENAME_LEN];
430 if (filename != NULL)
433 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
434 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
439 char *getSetupFilename()
441 static char *filename = NULL;
443 if (filename != NULL)
446 filename = getPath2(getSetupDir(), SETUP_FILENAME);
451 static char *getCorrectedArtworkBasename(char *basename)
453 char *basename_corrected = basename;
455 #if defined(PLATFORM_MSDOS)
456 if (program.filename_prefix != NULL)
458 int prefix_len = strlen(program.filename_prefix);
460 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
461 basename_corrected = &basename[prefix_len];
463 /* if corrected filename is still longer than standard MS-DOS filename
464 size (8 characters + 1 dot + 3 characters file extension), shorten
465 filename by writing file extension after 8th basename character */
466 if (strlen(basename_corrected) > 8 + 1 + 3)
468 static char *msdos_filename = NULL;
470 if (msdos_filename != NULL)
471 free(msdos_filename);
473 msdos_filename = getStringCopy(basename_corrected);
474 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
476 basename_corrected = msdos_filename;
481 return basename_corrected;
484 char *getCustomImageFilename(char *basename)
486 static char *filename = NULL;
487 boolean skip_setup_artwork = FALSE;
489 if (filename != NULL)
492 basename = getCorrectedArtworkBasename(basename);
494 if (!setup.override_level_graphics)
496 /* 1st try: look for special artwork in current level series directory */
497 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
498 if (fileExists(filename))
503 /* check if there is special artwork configured in level series config */
504 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
506 /* 2nd try: look for special artwork configured in level series config */
507 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
508 if (fileExists(filename))
513 /* take missing artwork configured in level set config from default */
514 skip_setup_artwork = TRUE;
518 if (!skip_setup_artwork)
520 /* 3rd try: look for special artwork in configured artwork directory */
521 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
522 if (fileExists(filename))
528 /* 4th try: look for default artwork in new default artwork directory */
529 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
530 if (fileExists(filename))
535 /* 5th try: look for default artwork in old default artwork directory */
536 filename = getPath2(options.graphics_directory, basename);
537 if (fileExists(filename))
540 return NULL; /* cannot find specified artwork file anywhere */
543 char *getCustomSoundFilename(char *basename)
545 static char *filename = NULL;
546 boolean skip_setup_artwork = FALSE;
548 if (filename != NULL)
551 basename = getCorrectedArtworkBasename(basename);
553 if (!setup.override_level_sounds)
555 /* 1st try: look for special artwork in current level series directory */
556 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
557 if (fileExists(filename))
562 /* check if there is special artwork configured in level series config */
563 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
565 /* 2nd try: look for special artwork configured in level series config */
566 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
567 if (fileExists(filename))
572 /* take missing artwork configured in level set config from default */
573 skip_setup_artwork = TRUE;
577 if (!skip_setup_artwork)
579 /* 3rd try: look for special artwork in configured artwork directory */
580 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
581 if (fileExists(filename))
587 /* 4th try: look for default artwork in new default artwork directory */
588 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
589 if (fileExists(filename))
594 /* 5th try: look for default artwork in old default artwork directory */
595 filename = getPath2(options.sounds_directory, basename);
596 if (fileExists(filename))
599 return NULL; /* cannot find specified artwork file anywhere */
602 char *getCustomArtworkFilename(char *basename, int type)
604 if (type == ARTWORK_TYPE_GRAPHICS)
605 return getCustomImageFilename(basename);
606 else if (type == ARTWORK_TYPE_SOUNDS)
607 return getCustomSoundFilename(basename);
609 return UNDEFINED_FILENAME;
612 char *getCustomArtworkConfigFilename(int type)
614 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
617 char *getCustomArtworkLevelConfigFilename(int type)
619 static char *filename = NULL;
621 if (filename != NULL)
624 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
629 char *getCustomMusicDirectory(void)
631 static char *directory = NULL;
632 boolean skip_setup_artwork = FALSE;
634 if (directory != NULL)
637 if (!setup.override_level_music)
639 /* 1st try: look for special artwork in current level series directory */
640 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
641 if (fileExists(directory))
646 /* check if there is special artwork configured in level series config */
647 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
649 /* 2nd try: look for special artwork configured in level series config */
650 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
651 if (fileExists(directory))
656 /* take missing artwork configured in level set config from default */
657 skip_setup_artwork = TRUE;
661 if (!skip_setup_artwork)
663 /* 3rd try: look for special artwork in configured artwork directory */
664 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
665 if (fileExists(directory))
671 /* 4th try: look for default artwork in new default artwork directory */
672 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
673 if (fileExists(directory))
678 /* 5th try: look for default artwork in old default artwork directory */
679 directory = getStringCopy(options.music_directory);
680 if (fileExists(directory))
683 return NULL; /* cannot find specified artwork file anywhere */
686 void InitTapeDirectory(char *level_subdir)
688 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
689 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
690 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
693 void InitScoreDirectory(char *level_subdir)
695 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
696 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
697 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
700 static void SaveUserLevelInfo();
702 void InitUserLevelDirectory(char *level_subdir)
704 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
706 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
707 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
708 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
714 void InitLevelSetupDirectory(char *level_subdir)
716 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
717 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
718 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
722 /* ------------------------------------------------------------------------- */
723 /* some functions to handle lists of level directories */
724 /* ------------------------------------------------------------------------- */
726 TreeInfo *newTreeInfo()
728 return checked_calloc(sizeof(TreeInfo));
731 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
733 node_new->next = *node_first;
734 *node_first = node_new;
737 int numTreeInfo(TreeInfo *node)
750 boolean validLevelSeries(TreeInfo *node)
752 return (node != NULL && !node->node_group && !node->parent_link);
755 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
760 if (node->node_group) /* enter level group (step down into tree) */
761 return getFirstValidTreeInfoEntry(node->node_group);
762 else if (node->parent_link) /* skip start entry of level group */
764 if (node->next) /* get first real level series entry */
765 return getFirstValidTreeInfoEntry(node->next);
766 else /* leave empty level group and go on */
767 return getFirstValidTreeInfoEntry(node->node_parent->next);
769 else /* this seems to be a regular level series */
773 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
778 if (node->node_parent == NULL) /* top level group */
779 return *node->node_top;
780 else /* sub level group */
781 return node->node_parent->node_group;
784 int numTreeInfoInGroup(TreeInfo *node)
786 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
789 int posTreeInfo(TreeInfo *node)
791 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
796 if (node_cmp == node)
800 node_cmp = node_cmp->next;
806 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
808 TreeInfo *node_default = node;
823 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
825 if (identifier == NULL)
830 if (node->node_group)
832 TreeInfo *node_group;
834 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
839 else if (!node->parent_link)
841 if (strcmp(identifier, node->identifier) == 0)
851 void dumpTreeInfo(TreeInfo *node, int depth)
855 printf("Dumping TreeInfo:\n");
859 for (i=0; i<(depth + 1) * 3; i++)
862 printf("filename == '%s' (%s) [%s] (%d)\n",
863 node->filename, node->name, node->identifier, node->sort_priority);
865 if (node->node_group != NULL)
866 dumpTreeInfo(node->node_group, depth + 1);
872 void sortTreeInfo(TreeInfo **node_first,
873 int (*compare_function)(const void *, const void *))
875 int num_nodes = numTreeInfo(*node_first);
876 TreeInfo **sort_array;
877 TreeInfo *node = *node_first;
883 /* allocate array for sorting structure pointers */
884 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
886 /* writing structure pointers to sorting array */
887 while (i < num_nodes && node) /* double boundary check... */
889 sort_array[i] = node;
895 /* sorting the structure pointers in the sorting array */
896 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
899 /* update the linkage of list elements with the sorted node array */
900 for (i=0; i<num_nodes - 1; i++)
901 sort_array[i]->next = sort_array[i + 1];
902 sort_array[num_nodes - 1]->next = NULL;
904 /* update the linkage of the main list anchor pointer */
905 *node_first = sort_array[0];
909 /* now recursively sort the level group structures */
913 if (node->node_group != NULL)
914 sortTreeInfo(&node->node_group, compare_function);
921 /* ========================================================================= */
922 /* some stuff from "files.c" */
923 /* ========================================================================= */
925 #if defined(PLATFORM_WIN32)
927 #define S_IRGRP S_IRUSR
930 #define S_IROTH S_IRUSR
933 #define S_IWGRP S_IWUSR
936 #define S_IWOTH S_IWUSR
939 #define S_IXGRP S_IXUSR
942 #define S_IXOTH S_IXUSR
945 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
950 #endif /* PLATFORM_WIN32 */
952 /* file permissions for newly written files */
953 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
954 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
955 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
957 #define MODE_W_PRIVATE (S_IWUSR)
958 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
959 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
961 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
962 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
964 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
965 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
967 char *getUserDataDir(void)
969 static char *userdata_dir = NULL;
971 if (userdata_dir == NULL)
972 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
977 char *getCommonDataDir(void)
979 static char *common_data_dir = NULL;
981 #if defined(PLATFORM_WIN32)
982 if (common_data_dir == NULL)
984 char *dir = checked_malloc(MAX_PATH + 1);
986 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
987 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
988 common_data_dir = getPath2(dir, program.userdata_directory);
990 common_data_dir = options.rw_base_directory;
993 if (common_data_dir == NULL)
994 common_data_dir = options.rw_base_directory;
997 return common_data_dir;
1002 return getUserDataDir();
1005 static mode_t posix_umask(mode_t mask)
1007 #if defined(PLATFORM_UNIX)
1014 static int posix_mkdir(const char *pathname, mode_t mode)
1016 #if defined(PLATFORM_WIN32)
1017 return mkdir(pathname);
1019 return mkdir(pathname, mode);
1023 void createDirectory(char *dir, char *text, int permission_class)
1025 /* leave "other" permissions in umask untouched, but ensure group parts
1026 of USERDATA_DIR_MODE are not masked */
1027 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1028 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1029 mode_t normal_umask = posix_umask(0);
1030 mode_t group_umask = ~(dir_mode & S_IRWXG);
1031 posix_umask(normal_umask & group_umask);
1033 if (access(dir, F_OK) != 0)
1034 if (posix_mkdir(dir, dir_mode) != 0)
1035 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1037 posix_umask(normal_umask); /* reset normal umask */
1040 void InitUserDataDirectory()
1042 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1045 void SetFilePermissions(char *filename, int permission_class)
1047 chmod(filename, (permission_class == PERMS_PRIVATE ?
1048 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1051 char *getCookie(char *file_type)
1053 static char cookie[MAX_COOKIE_LEN + 1];
1055 if (strlen(program.cookie_prefix) + 1 +
1056 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1057 return "[COOKIE ERROR]"; /* should never happen */
1059 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1060 program.cookie_prefix, file_type,
1061 program.version_major, program.version_minor);
1066 int getFileVersionFromCookieString(const char *cookie)
1068 const char *ptr_cookie1, *ptr_cookie2;
1069 const char *pattern1 = "_FILE_VERSION_";
1070 const char *pattern2 = "?.?";
1071 const int len_cookie = strlen(cookie);
1072 const int len_pattern1 = strlen(pattern1);
1073 const int len_pattern2 = strlen(pattern2);
1074 const int len_pattern = len_pattern1 + len_pattern2;
1075 int version_major, version_minor;
1077 if (len_cookie <= len_pattern)
1080 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1081 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1083 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1086 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1087 ptr_cookie2[1] != '.' ||
1088 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1091 version_major = ptr_cookie2[0] - '0';
1092 version_minor = ptr_cookie2[2] - '0';
1094 return VERSION_IDENT(version_major, version_minor, 0);
1097 boolean checkCookieString(const char *cookie, const char *template)
1099 const char *pattern = "_FILE_VERSION_?.?";
1100 const int len_cookie = strlen(cookie);
1101 const int len_template = strlen(template);
1102 const int len_pattern = strlen(pattern);
1104 if (len_cookie != len_template)
1107 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1113 /* ------------------------------------------------------------------------- */
1114 /* setup file list and hash handling functions */
1115 /* ------------------------------------------------------------------------- */
1117 char *getFormattedSetupEntry(char *token, char *value)
1120 static char entry[MAX_LINE_LEN];
1122 /* start with the token and some spaces to format output line */
1123 sprintf(entry, "%s:", token);
1124 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1127 /* continue with the token's value */
1128 strcat(entry, value);
1133 SetupFileList *newSetupFileList(char *token, char *value)
1135 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1137 new->token = getStringCopy(token);
1138 new->value = getStringCopy(value);
1145 void freeSetupFileList(SetupFileList *list)
1155 freeSetupFileList(list->next);
1159 char *getListEntry(SetupFileList *list, char *token)
1164 if (strcmp(list->token, token) == 0)
1167 return getListEntry(list->next, token);
1170 void setListEntry(SetupFileList *list, char *token, char *value)
1175 if (strcmp(list->token, token) == 0)
1180 list->value = getStringCopy(value);
1182 else if (list->next == NULL)
1183 list->next = newSetupFileList(token, value);
1185 setListEntry(list->next, token, value);
1189 static void printSetupFileList(SetupFileList *list)
1194 printf("token: '%s'\n", list->token);
1195 printf("value: '%s'\n", list->value);
1197 printSetupFileList(list->next);
1202 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1203 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1204 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1205 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1207 #define insert_hash_entry hashtable_insert
1208 #define search_hash_entry hashtable_search
1209 #define change_hash_entry hashtable_change
1210 #define remove_hash_entry hashtable_remove
1213 static unsigned int get_hash_from_key(void *key)
1218 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1219 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1220 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1221 it works better than many other constants, prime or not) has never been
1222 adequately explained.
1224 If you just want to have a good hash function, and cannot wait, djb2
1225 is one of the best string hash functions i know. It has excellent
1226 distribution and speed on many different sets of keys and table sizes.
1227 You are not likely to do better with one of the "well known" functions
1228 such as PJW, K&R, etc.
1230 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1233 char *str = (char *)key;
1234 unsigned int hash = 5381;
1237 while ((c = *str++))
1238 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1243 static int keys_are_equal(void *key1, void *key2)
1245 return (strcmp((char *)key1, (char *)key2) == 0);
1248 SetupFileHash *newSetupFileHash()
1250 SetupFileHash *new_hash =
1251 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1256 void freeSetupFileHash(SetupFileHash *hash)
1261 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1264 char *getHashEntry(SetupFileHash *hash, char *token)
1269 return search_hash_entry(hash, token);
1272 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1279 value_copy = getStringCopy(value);
1281 /* change value; if it does not exist, insert it as new */
1282 if (!change_hash_entry(hash, token, value_copy))
1283 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1284 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1289 static void printSetupFileHash(SetupFileHash *hash)
1291 BEGIN_HASH_ITERATION(hash, itr)
1293 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1294 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1296 END_HASH_ITERATION(hash, itr)
1301 static void *loadSetupFileData(char *filename, boolean use_hash)
1304 char line[MAX_LINE_LEN];
1305 char *token, *value, *line_ptr;
1306 void *setup_file_data;
1310 setup_file_data = newSetupFileHash();
1312 setup_file_data = newSetupFileList("", "");
1314 if (!(file = fopen(filename, MODE_READ)))
1316 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1322 /* read next line of input file */
1323 if (!fgets(line, MAX_LINE_LEN, file))
1326 /* cut trailing comment or whitespace from input line */
1327 for (line_ptr = line; *line_ptr; line_ptr++)
1329 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1336 /* cut trailing whitespaces from input line */
1337 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1338 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1341 /* ignore empty lines */
1345 line_len = strlen(line);
1347 /* cut leading whitespaces from token */
1348 for (token = line; *token; token++)
1349 if (*token != ' ' && *token != '\t')
1352 /* find end of token */
1353 for (line_ptr = token; *line_ptr; line_ptr++)
1355 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1362 if (line_ptr < line + line_len)
1363 value = line_ptr + 1;
1367 /* cut leading whitespaces from value */
1368 for (; *value; value++)
1369 if (*value != ' ' && *value != '\t')
1372 if (*token && *value)
1375 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1377 setListEntry((SetupFileList *)setup_file_data, token, value);
1385 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1386 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1390 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1391 SetupFileList *first_valid_list_entry = setup_file_list->next;
1393 /* free empty list header */
1394 setup_file_list->next = NULL;
1395 freeSetupFileList(setup_file_list);
1396 setup_file_data = first_valid_list_entry;
1398 if (first_valid_list_entry == NULL)
1399 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1402 return setup_file_data;
1405 SetupFileList *loadSetupFileList(char *filename)
1407 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1410 SetupFileHash *loadSetupFileHash(char *filename)
1412 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1415 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1418 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1421 Error(ERR_WARN, "configuration file has no file identifier");
1422 else if (!checkCookieString(value, identifier))
1423 Error(ERR_WARN, "configuration file has wrong file identifier");
1427 /* ========================================================================= */
1428 /* setup file stuff */
1429 /* ========================================================================= */
1431 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1432 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1433 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1435 /* level directory info */
1436 #define LEVELINFO_TOKEN_IDENTIFIER 0
1437 #define LEVELINFO_TOKEN_NAME 1
1438 #define LEVELINFO_TOKEN_NAME_SORTING 2
1439 #define LEVELINFO_TOKEN_AUTHOR 3
1440 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1441 #define LEVELINFO_TOKEN_LEVELS 5
1442 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1443 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1444 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1445 #define LEVELINFO_TOKEN_READONLY 9
1446 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1447 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1448 #define LEVELINFO_TOKEN_MUSIC_SET 12
1450 #define NUM_LEVELINFO_TOKENS 13
1452 static LevelDirTree ldi;
1454 static struct TokenInfo levelinfo_tokens[] =
1456 /* level directory info */
1457 { TYPE_STRING, &ldi.identifier, "identifier" },
1458 { TYPE_STRING, &ldi.name, "name" },
1459 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1460 { TYPE_STRING, &ldi.author, "author" },
1461 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1462 { TYPE_INTEGER, &ldi.levels, "levels" },
1463 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1464 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1465 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1466 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1467 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1468 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1469 { TYPE_STRING, &ldi.music_set, "music_set" }
1472 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1476 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1477 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1478 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1479 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1482 ldi->node_parent = NULL;
1483 ldi->node_group = NULL;
1487 ldi->cl_cursor = -1;
1489 ldi->filename = NULL;
1490 ldi->fullpath = NULL;
1491 ldi->basepath = NULL;
1492 ldi->identifier = NULL;
1493 ldi->name = getStringCopy(ANONYMOUS_NAME);
1494 ldi->name_sorting = NULL;
1495 ldi->author = getStringCopy(ANONYMOUS_NAME);
1497 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1498 ldi->parent_link = FALSE;
1499 ldi->user_defined = FALSE;
1501 ldi->class_desc = NULL;
1503 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1505 ldi->imported_from = NULL;
1507 ldi->graphics_set = NULL;
1508 ldi->sounds_set = NULL;
1509 ldi->music_set = NULL;
1510 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1511 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1512 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1515 ldi->first_level = 0;
1516 ldi->last_level = 0;
1517 ldi->level_group = FALSE;
1518 ldi->handicap_level = 0;
1519 ldi->readonly = TRUE;
1523 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1527 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1529 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1535 /* copy all values from the parent structure */
1537 ldi->type = parent->type;
1539 ldi->node_top = parent->node_top;
1540 ldi->node_parent = parent;
1541 ldi->node_group = NULL;
1545 ldi->cl_cursor = -1;
1547 ldi->filename = NULL;
1548 ldi->fullpath = NULL;
1549 ldi->basepath = NULL;
1550 ldi->identifier = NULL;
1551 ldi->name = getStringCopy(ANONYMOUS_NAME);
1552 ldi->name_sorting = NULL;
1553 ldi->author = getStringCopy(parent->author);
1555 ldi->sort_priority = parent->sort_priority;
1556 ldi->parent_link = FALSE;
1557 ldi->user_defined = parent->user_defined;
1558 ldi->color = parent->color;
1559 ldi->class_desc = getStringCopy(parent->class_desc);
1561 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1563 ldi->imported_from = getStringCopy(parent->imported_from);
1565 ldi->graphics_set = NULL;
1566 ldi->sounds_set = NULL;
1567 ldi->music_set = NULL;
1568 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1569 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1570 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1573 ldi->first_level = 0;
1574 ldi->last_level = 0;
1575 ldi->level_group = FALSE;
1576 ldi->handicap_level = 0;
1577 ldi->readonly = TRUE;
1583 /* first copy all values from the parent structure ... */
1586 /* ... then set all fields to default that cannot be inherited from parent.
1587 This is especially important for all those fields that can be set from
1588 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1589 calls 'free()' for all already set token values which requires that no
1590 other structure's pointer may point to them!
1593 ldi->filename = NULL;
1594 ldi->fullpath = NULL;
1595 ldi->basepath = NULL;
1596 ldi->identifier = NULL;
1597 ldi->name = getStringCopy(ANONYMOUS_NAME);
1598 ldi->name_sorting = NULL;
1599 ldi->author = getStringCopy(parent->author);
1601 ldi->imported_from = getStringCopy(parent->imported_from);
1602 ldi->class_desc = getStringCopy(parent->class_desc);
1604 ldi->graphics_set = NULL;
1605 ldi->sounds_set = NULL;
1606 ldi->music_set = NULL;
1607 ldi->graphics_path = NULL;
1608 ldi->sounds_path = NULL;
1609 ldi->music_path = NULL;
1611 ldi->level_group = FALSE;
1612 ldi->parent_link = FALSE;
1614 ldi->node_top = parent->node_top;
1615 ldi->node_parent = parent;
1616 ldi->node_group = NULL;
1622 void setSetupInfo(struct TokenInfo *token_info,
1623 int token_nr, char *token_value)
1625 int token_type = token_info[token_nr].type;
1626 void *setup_value = token_info[token_nr].value;
1628 if (token_value == NULL)
1631 /* set setup field to corresponding token value */
1636 *(boolean *)setup_value = get_boolean_from_string(token_value);
1640 *(Key *)setup_value = getKeyFromKeyName(token_value);
1644 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1648 *(int *)setup_value = get_integer_from_string(token_value);
1652 if (*(char **)setup_value != NULL)
1653 free(*(char **)setup_value);
1654 *(char **)setup_value = getStringCopy(token_value);
1662 static int compareTreeInfoEntries(const void *object1, const void *object2)
1664 const TreeInfo *entry1 = *((TreeInfo **)object1);
1665 const TreeInfo *entry2 = *((TreeInfo **)object2);
1666 int class_sorting1, class_sorting2;
1669 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1671 class_sorting1 = LEVELSORTING(entry1);
1672 class_sorting2 = LEVELSORTING(entry2);
1676 class_sorting1 = ARTWORKSORTING(entry1);
1677 class_sorting2 = ARTWORKSORTING(entry2);
1680 if (entry1->parent_link || entry2->parent_link)
1681 compare_result = (entry1->parent_link ? -1 : +1);
1682 else if (entry1->sort_priority == entry2->sort_priority)
1684 char *name1 = getStringToLower(entry1->name_sorting);
1685 char *name2 = getStringToLower(entry2->name_sorting);
1687 compare_result = strcmp(name1, name2);
1692 else if (class_sorting1 == class_sorting2)
1693 compare_result = entry1->sort_priority - entry2->sort_priority;
1695 compare_result = class_sorting1 - class_sorting2;
1697 return compare_result;
1700 static void createParentTreeInfoNode(TreeInfo *node_parent)
1704 if (node_parent == NULL)
1707 ti_new = newTreeInfo();
1708 setTreeInfoToDefaults(ti_new, node_parent->type);
1710 ti_new->node_parent = node_parent;
1711 ti_new->parent_link = TRUE;
1713 ti_new->identifier = getStringCopy(node_parent->identifier);
1714 ti_new->name = ".. (parent directory)";
1715 ti_new->name_sorting = getStringCopy(ti_new->name);
1717 ti_new->filename = "..";
1718 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1720 ti_new->sort_priority = node_parent->sort_priority;
1721 ti_new->class_desc = getLevelClassDescription(ti_new);
1723 pushTreeInfo(&node_parent->node_group, ti_new);
1726 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1727 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1729 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1730 TreeInfo *node_parent,
1731 char *level_directory,
1732 char *directory_name)
1734 char *directory_path = getPath2(level_directory, directory_name);
1735 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1736 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1737 LevelDirTree *leveldir_new = NULL;
1740 if (setup_file_hash == NULL)
1742 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1744 free(directory_path);
1750 leveldir_new = newTreeInfo();
1753 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1755 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1757 leveldir_new->filename = getStringCopy(directory_name);
1759 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1761 /* set all structure fields according to the token/value pairs */
1762 ldi = *leveldir_new;
1763 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1764 setSetupInfo(levelinfo_tokens, i,
1765 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1766 *leveldir_new = ldi;
1768 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1770 free(leveldir_new->name);
1771 leveldir_new->name = getStringCopy(leveldir_new->filename);
1774 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1776 if (leveldir_new->identifier == NULL)
1777 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1779 if (leveldir_new->name_sorting == NULL)
1780 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1782 if (node_parent == NULL) /* top level group */
1784 leveldir_new->basepath = level_directory;
1785 leveldir_new->fullpath = leveldir_new->filename;
1787 else /* sub level group */
1789 leveldir_new->basepath = node_parent->basepath;
1790 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1793 if (leveldir_new->levels < 1)
1794 leveldir_new->levels = 1;
1796 leveldir_new->last_level =
1797 leveldir_new->first_level + leveldir_new->levels - 1;
1799 leveldir_new->user_defined =
1800 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1802 leveldir_new->color = LEVELCOLOR(leveldir_new);
1803 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1805 leveldir_new->handicap_level = /* set handicap to default value */
1806 (leveldir_new->user_defined ?
1807 leveldir_new->last_level :
1808 leveldir_new->first_level);
1810 pushTreeInfo(node_first, leveldir_new);
1812 freeSetupFileHash(setup_file_hash);
1814 if (leveldir_new->level_group)
1816 /* create node to link back to current level directory */
1817 createParentTreeInfoNode(leveldir_new);
1819 /* step into sub-directory and look for more level series */
1820 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1821 leveldir_new, directory_path);
1824 free(directory_path);
1830 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1831 TreeInfo *node_parent,
1832 char *level_directory)
1835 struct dirent *dir_entry;
1836 boolean valid_entry_found = FALSE;
1838 if ((dir = opendir(level_directory)) == NULL)
1840 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1844 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1846 struct stat file_status;
1847 char *directory_name = dir_entry->d_name;
1848 char *directory_path = getPath2(level_directory, directory_name);
1850 /* skip entries for current and parent directory */
1851 if (strcmp(directory_name, ".") == 0 ||
1852 strcmp(directory_name, "..") == 0)
1854 free(directory_path);
1858 /* find out if directory entry is itself a directory */
1859 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1860 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1862 free(directory_path);
1866 free(directory_path);
1868 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1869 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1870 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1873 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1880 if (!valid_entry_found)
1882 /* check if this directory directly contains a file "levelinfo.conf" */
1883 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1884 level_directory, ".");
1887 if (!valid_entry_found)
1888 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1892 void LoadLevelInfo()
1894 InitUserLevelDirectory(getLoginName());
1896 DrawInitText("Loading level series:", 120, FC_GREEN);
1898 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1899 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1901 /* before sorting, the first entries will be from the user directory */
1902 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1904 if (leveldir_first == NULL)
1905 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1907 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1910 dumpTreeInfo(leveldir_first, 0);
1914 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1915 TreeInfo *node_parent,
1916 char *base_directory,
1917 char *directory_name, int type)
1919 char *directory_path = getPath2(base_directory, directory_name);
1920 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1921 SetupFileHash *setup_file_hash = NULL;
1922 TreeInfo *artwork_new = NULL;
1925 if (access(filename, F_OK) == 0) /* file exists */
1926 setup_file_hash = loadSetupFileHash(filename);
1928 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1931 struct dirent *dir_entry;
1932 boolean valid_file_found = FALSE;
1934 if ((dir = opendir(directory_path)) != NULL)
1936 while ((dir_entry = readdir(dir)) != NULL)
1938 char *entry_name = dir_entry->d_name;
1940 if (FileIsArtworkType(entry_name, type))
1942 valid_file_found = TRUE;
1950 if (!valid_file_found)
1952 if (strcmp(directory_name, ".") != 0)
1953 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1955 free(directory_path);
1962 artwork_new = newTreeInfo();
1965 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1967 setTreeInfoToDefaults(artwork_new, type);
1969 artwork_new->filename = getStringCopy(directory_name);
1971 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1974 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1977 /* set all structure fields according to the token/value pairs */
1979 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1980 setSetupInfo(levelinfo_tokens, i,
1981 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1984 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1986 free(artwork_new->name);
1987 artwork_new->name = getStringCopy(artwork_new->filename);
1991 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1994 if (artwork_new->identifier == NULL)
1995 artwork_new->identifier = getStringCopy(artwork_new->filename);
1997 if (artwork_new->name_sorting == NULL)
1998 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2001 if (node_parent == NULL) /* top level group */
2003 artwork_new->basepath = getStringCopy(base_directory);
2004 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2006 else /* sub level group */
2008 artwork_new->basepath = getStringCopy(node_parent->basepath);
2009 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2012 artwork_new->user_defined =
2013 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2015 /* (may use ".sort_priority" from "setup_file_hash" above) */
2016 artwork_new->color = ARTWORKCOLOR(artwork_new);
2017 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2019 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2021 if (artwork_new->name != NULL)
2022 free(artwork_new->name);
2024 if (strcmp(artwork_new->filename, ".") == 0)
2026 if (artwork_new->user_defined)
2028 artwork_new->identifier = getStringCopy("private");
2029 artwork_new->sort_priority = ARTWORKCLASS_USER;
2033 artwork_new->identifier = getStringCopy("classic");
2034 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2037 /* set to new values after changing ".sort_priority" */
2038 artwork_new->color = ARTWORKCOLOR(artwork_new);
2039 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2043 artwork_new->identifier = getStringCopy(artwork_new->filename);
2046 artwork_new->name = getStringCopy(artwork_new->identifier);
2047 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2050 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2052 pushTreeInfo(node_first, artwork_new);
2054 freeSetupFileHash(setup_file_hash);
2056 free(directory_path);
2062 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2063 TreeInfo *node_parent,
2064 char *base_directory, int type)
2067 struct dirent *dir_entry;
2068 boolean valid_entry_found = FALSE;
2070 if ((dir = opendir(base_directory)) == NULL)
2072 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2073 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2077 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2079 struct stat file_status;
2080 char *directory_name = dir_entry->d_name;
2081 char *directory_path = getPath2(base_directory, directory_name);
2083 /* skip entries for current and parent directory */
2084 if (strcmp(directory_name, ".") == 0 ||
2085 strcmp(directory_name, "..") == 0)
2087 free(directory_path);
2091 /* find out if directory entry is itself a directory */
2092 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2093 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2095 free(directory_path);
2099 free(directory_path);
2101 /* check if this directory contains artwork with or without config file */
2102 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2104 directory_name, type);
2109 /* check if this directory directly contains artwork itself */
2110 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2111 base_directory, ".",
2113 if (!valid_entry_found)
2114 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2118 static TreeInfo *getDummyArtworkInfo(int type)
2120 /* this is only needed when there is completely no artwork available */
2121 TreeInfo *artwork_new = newTreeInfo();
2123 setTreeInfoToDefaults(artwork_new, type);
2125 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2126 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2127 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2129 if (artwork_new->name != NULL)
2130 free(artwork_new->name);
2132 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2133 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2134 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2139 void LoadArtworkInfo()
2141 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2143 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2144 options.graphics_directory,
2145 TREE_TYPE_GRAPHICS_DIR);
2146 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2147 getUserGraphicsDir(),
2148 TREE_TYPE_GRAPHICS_DIR);
2150 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2151 options.sounds_directory,
2152 TREE_TYPE_SOUNDS_DIR);
2153 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2155 TREE_TYPE_SOUNDS_DIR);
2157 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2158 options.music_directory,
2159 TREE_TYPE_MUSIC_DIR);
2160 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2162 TREE_TYPE_MUSIC_DIR);
2164 if (artwork.gfx_first == NULL)
2165 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2166 if (artwork.snd_first == NULL)
2167 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2168 if (artwork.mus_first == NULL)
2169 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2171 /* before sorting, the first entries will be from the user directory */
2172 artwork.gfx_current =
2173 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2174 if (artwork.gfx_current == NULL)
2175 artwork.gfx_current =
2176 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2177 if (artwork.gfx_current == NULL)
2178 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2180 artwork.snd_current =
2181 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2182 if (artwork.snd_current == NULL)
2183 artwork.snd_current =
2184 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2185 if (artwork.snd_current == NULL)
2186 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2188 artwork.mus_current =
2189 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2190 if (artwork.mus_current == NULL)
2191 artwork.mus_current =
2192 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2193 if (artwork.mus_current == NULL)
2194 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2196 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2197 artwork.snd_current_identifier = artwork.snd_current->identifier;
2198 artwork.mus_current_identifier = artwork.mus_current->identifier;
2201 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2202 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2203 printf("music set == %s\n\n", artwork.mus_current_identifier);
2206 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2207 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2208 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2211 dumpTreeInfo(artwork.gfx_first, 0);
2212 dumpTreeInfo(artwork.snd_first, 0);
2213 dumpTreeInfo(artwork.mus_first, 0);
2217 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2218 LevelDirTree *level_node)
2220 /* recursively check all level directories for artwork sub-directories */
2224 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2225 ARTWORK_DIRECTORY((*artwork_node)->type));
2228 if (!level_node->parent_link)
2229 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2230 level_node->filename, level_node->name);
2233 if (!level_node->parent_link)
2235 TreeInfo *topnode_last = *artwork_node;
2237 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2238 (*artwork_node)->type);
2240 if (topnode_last != *artwork_node)
2242 free((*artwork_node)->identifier);
2243 free((*artwork_node)->name);
2244 free((*artwork_node)->name_sorting);
2246 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2247 (*artwork_node)->name = getStringCopy(level_node->name);
2248 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2250 (*artwork_node)->sort_priority = level_node->sort_priority;
2251 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2257 if (level_node->node_group != NULL)
2258 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2260 level_node = level_node->next;
2264 void LoadLevelArtworkInfo()
2266 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2268 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2269 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2270 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2272 /* needed for reloading level artwork not known at ealier stage */
2273 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2275 artwork.gfx_current =
2276 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2277 if (artwork.gfx_current == NULL)
2278 artwork.gfx_current =
2279 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2280 if (artwork.gfx_current == NULL)
2281 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2284 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2286 artwork.snd_current =
2287 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2288 if (artwork.snd_current == NULL)
2289 artwork.snd_current =
2290 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2291 if (artwork.snd_current == NULL)
2292 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2295 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2297 artwork.mus_current =
2298 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2299 if (artwork.mus_current == NULL)
2300 artwork.mus_current =
2301 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2302 if (artwork.mus_current == NULL)
2303 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2306 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2307 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2308 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2311 dumpTreeInfo(artwork.gfx_first, 0);
2312 dumpTreeInfo(artwork.snd_first, 0);
2313 dumpTreeInfo(artwork.mus_first, 0);
2317 static void SaveUserLevelInfo()
2323 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2325 if (!(file = fopen(filename, MODE_WRITE)))
2327 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2332 /* always start with reliable default values */
2333 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2336 /* !!! FIX ME !!! */
2337 setString(&ldi.name, getLoginName());
2338 setString(&ldi.author, getRealName());
2340 ldi.first_level = 1;
2341 ldi.sort_priority = LEVELCLASS_USER_START;
2342 ldi.readonly = FALSE;
2343 setString(&ldi.graphics_set, GFX_CLASSIC_SUBDIR);
2344 setString(&ldi.sounds_set, SND_CLASSIC_SUBDIR);
2345 setString(&ldi.music_set, MUS_CLASSIC_SUBDIR);
2347 ldi.name = getStringCopy(getLoginName());
2348 ldi.author = getStringCopy(getRealName());
2350 ldi.first_level = 1;
2351 ldi.sort_priority = LEVELCLASS_USER_START;
2352 ldi.readonly = FALSE;
2353 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2354 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2355 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2358 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2359 getCookie("LEVELINFO")));
2361 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2362 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2363 i != LEVELINFO_TOKEN_NAME_SORTING &&
2364 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2365 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2370 SetFilePermissions(filename, PERMS_PRIVATE);
2373 char *getSetupValue(int type, void *value)
2375 static char value_string[MAX_LINE_LEN];
2383 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2387 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2391 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2395 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2399 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2403 sprintf(value_string, "%d", *(int *)value);
2407 strcpy(value_string, *(char **)value);
2411 value_string[0] = '\0';
2415 return value_string;
2418 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2422 static char token_string[MAX_LINE_LEN];
2423 int token_type = token_info[token_nr].type;
2424 void *setup_value = token_info[token_nr].value;
2425 char *token_text = token_info[token_nr].text;
2426 char *value_string = getSetupValue(token_type, setup_value);
2428 /* build complete token string */
2429 sprintf(token_string, "%s%s", prefix, token_text);
2431 /* build setup entry line */
2432 line = getFormattedSetupEntry(token_string, value_string);
2434 if (token_type == TYPE_KEY_X11)
2436 Key key = *(Key *)setup_value;
2437 char *keyname = getKeyNameFromKey(key);
2439 /* add comment, if useful */
2440 if (strcmp(keyname, "(undefined)") != 0 &&
2441 strcmp(keyname, "(unknown)") != 0)
2443 /* add at least one whitespace */
2445 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2449 strcat(line, keyname);
2456 void LoadLevelSetup_LastSeries()
2459 SetupFileHash *level_setup_hash = NULL;
2461 /* always start with reliable default values */
2462 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2464 /* ----------------------------------------------------------------------- */
2465 /* ~/.<program>/levelsetup.conf */
2466 /* ----------------------------------------------------------------------- */
2468 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2470 if ((level_setup_hash = loadSetupFileHash(filename)))
2472 char *last_level_series =
2473 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2475 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2477 if (leveldir_current == NULL)
2478 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2480 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2482 freeSetupFileHash(level_setup_hash);
2485 Error(ERR_WARN, "using default setup values");
2490 void SaveLevelSetup_LastSeries()
2493 char *level_subdir = leveldir_current->filename;
2496 /* ----------------------------------------------------------------------- */
2497 /* ~/.<program>/levelsetup.conf */
2498 /* ----------------------------------------------------------------------- */
2500 InitUserDataDirectory();
2502 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2504 if (!(file = fopen(filename, MODE_WRITE)))
2506 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2511 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2512 getCookie("LEVELSETUP")));
2513 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2519 SetFilePermissions(filename, PERMS_PRIVATE);
2522 static void checkSeriesInfo()
2524 static char *level_directory = NULL;
2526 struct dirent *dir_entry;
2528 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2530 level_directory = getPath2((leveldir_current->user_defined ?
2531 getUserLevelDir(NULL) :
2532 options.level_directory),
2533 leveldir_current->fullpath);
2535 if ((dir = opendir(level_directory)) == NULL)
2537 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2541 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2543 if (strlen(dir_entry->d_name) > 4 &&
2544 dir_entry->d_name[3] == '.' &&
2545 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2547 char levelnum_str[4];
2550 strncpy(levelnum_str, dir_entry->d_name, 3);
2551 levelnum_str[3] = '\0';
2553 levelnum_value = atoi(levelnum_str);
2556 if (levelnum_value < leveldir_current->first_level)
2558 Error(ERR_WARN, "additional level %d found", levelnum_value);
2559 leveldir_current->first_level = levelnum_value;
2561 else if (levelnum_value > leveldir_current->last_level)
2563 Error(ERR_WARN, "additional level %d found", levelnum_value);
2564 leveldir_current->last_level = levelnum_value;
2573 void LoadLevelSetup_SeriesInfo()
2576 SetupFileHash *level_setup_hash = NULL;
2577 char *level_subdir = leveldir_current->filename;
2579 /* always start with reliable default values */
2580 level_nr = leveldir_current->first_level;
2582 checkSeriesInfo(leveldir_current);
2584 /* ----------------------------------------------------------------------- */
2585 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2586 /* ----------------------------------------------------------------------- */
2588 level_subdir = leveldir_current->filename;
2590 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2592 if ((level_setup_hash = loadSetupFileHash(filename)))
2596 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2600 level_nr = atoi(token_value);
2602 if (level_nr < leveldir_current->first_level)
2603 level_nr = leveldir_current->first_level;
2604 if (level_nr > leveldir_current->last_level)
2605 level_nr = leveldir_current->last_level;
2608 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2612 int level_nr = atoi(token_value);
2614 if (level_nr < leveldir_current->first_level)
2615 level_nr = leveldir_current->first_level;
2616 if (level_nr > leveldir_current->last_level + 1)
2617 level_nr = leveldir_current->last_level;
2619 if (leveldir_current->user_defined)
2620 level_nr = leveldir_current->last_level;
2622 leveldir_current->handicap_level = level_nr;
2625 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2627 freeSetupFileHash(level_setup_hash);
2630 Error(ERR_WARN, "using default setup values");
2635 void SaveLevelSetup_SeriesInfo()
2638 char *level_subdir = leveldir_current->filename;
2639 char *level_nr_str = int2str(level_nr, 0);
2640 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2643 /* ----------------------------------------------------------------------- */
2644 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2645 /* ----------------------------------------------------------------------- */
2647 InitLevelSetupDirectory(level_subdir);
2649 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2651 if (!(file = fopen(filename, MODE_WRITE)))
2653 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2658 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2659 getCookie("LEVELSETUP")));
2660 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2662 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2663 handicap_level_str));
2668 SetFilePermissions(filename, PERMS_PRIVATE);