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 ? GRAPHICS_SUBDIR :
274 type == TREE_TYPE_SOUNDS_DIR ? SOUNDS_SUBDIR :
275 type == TREE_TYPE_MUSIC_DIR ? MUSIC_SUBDIR : "");
278 static char *getDefaultArtworkDir(int type)
280 return (type == TREE_TYPE_GRAPHICS_DIR ?
281 getDefaultGraphicsDir(GRAPHICS_SUBDIR) :
282 type == TREE_TYPE_SOUNDS_DIR ?
283 getDefaultSoundsDir(SOUNDS_SUBDIR) :
284 type == TREE_TYPE_MUSIC_DIR ?
285 getDefaultMusicDir(MUSIC_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 *getCorrectedImageBasename(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);
479 return basename_corrected;
482 char *getCustomImageFilename(char *basename)
484 static char *filename = NULL;
485 boolean skip_setup_artwork = FALSE;
487 if (filename != NULL)
490 basename = getCorrectedImageBasename(basename);
492 if (!setup.override_level_graphics)
494 /* 1st try: look for special artwork in current level series directory */
495 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
496 if (fileExists(filename))
501 /* check if there is special artwork configured in level series config */
502 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
504 /* 2nd try: look for special artwork configured in level series config */
505 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
506 if (fileExists(filename))
511 /* take missing artwork configured in level set config from default */
512 skip_setup_artwork = TRUE;
516 if (!skip_setup_artwork)
518 /* 3rd try: look for special artwork in configured artwork directory */
519 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
520 if (fileExists(filename))
526 /* 4th try: look for default artwork in new default artwork directory */
527 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
528 if (fileExists(filename))
533 /* 5th try: look for default artwork in old default artwork directory */
534 filename = getPath2(options.graphics_directory, basename);
535 if (fileExists(filename))
538 return NULL; /* cannot find specified artwork file anywhere */
541 char *getCustomSoundFilename(char *basename)
543 static char *filename = NULL;
544 boolean skip_setup_artwork = FALSE;
546 if (filename != NULL)
549 if (!setup.override_level_sounds)
551 /* 1st try: look for special artwork in current level series directory */
552 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
553 if (fileExists(filename))
558 /* check if there is special artwork configured in level series config */
559 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
561 /* 2nd try: look for special artwork configured in level series config */
562 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
563 if (fileExists(filename))
568 /* take missing artwork configured in level set config from default */
569 skip_setup_artwork = TRUE;
573 if (!skip_setup_artwork)
575 /* 3rd try: look for special artwork in configured artwork directory */
576 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
577 if (fileExists(filename))
583 /* 4th try: look for default artwork in new default artwork directory */
584 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
585 if (fileExists(filename))
590 /* 5th try: look for default artwork in old default artwork directory */
591 filename = getPath2(options.sounds_directory, basename);
592 if (fileExists(filename))
595 return NULL; /* cannot find specified artwork file anywhere */
598 char *getCustomArtworkFilename(char *basename, int type)
600 if (type == ARTWORK_TYPE_GRAPHICS)
601 return getCustomImageFilename(basename);
602 else if (type == ARTWORK_TYPE_SOUNDS)
603 return getCustomSoundFilename(basename);
605 return UNDEFINED_FILENAME;
608 char *getCustomArtworkConfigFilename(int type)
610 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
613 char *getCustomArtworkLevelConfigFilename(int type)
615 static char *filename = NULL;
617 if (filename != NULL)
620 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
625 char *getCustomMusicDirectory(void)
627 static char *directory = NULL;
628 boolean skip_setup_artwork = FALSE;
630 if (directory != NULL)
633 if (!setup.override_level_music)
635 /* 1st try: look for special artwork in current level series directory */
636 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
637 if (fileExists(directory))
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 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
647 if (fileExists(directory))
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 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
661 if (fileExists(directory))
667 /* 4th try: look for default artwork in new default artwork directory */
668 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
669 if (fileExists(directory))
674 /* 5th try: look for default artwork in old default artwork directory */
675 directory = getStringCopy(options.music_directory);
676 if (fileExists(directory))
679 return NULL; /* cannot find specified artwork file anywhere */
682 void InitTapeDirectory(char *level_subdir)
684 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
685 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
686 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
689 void InitScoreDirectory(char *level_subdir)
691 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
692 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
693 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
696 static void SaveUserLevelInfo();
698 void InitUserLevelDirectory(char *level_subdir)
700 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
702 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
703 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
704 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
710 void InitLevelSetupDirectory(char *level_subdir)
712 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
713 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
714 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
718 /* ------------------------------------------------------------------------- */
719 /* some functions to handle lists of level directories */
720 /* ------------------------------------------------------------------------- */
722 TreeInfo *newTreeInfo()
724 return checked_calloc(sizeof(TreeInfo));
727 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
729 node_new->next = *node_first;
730 *node_first = node_new;
733 int numTreeInfo(TreeInfo *node)
746 boolean validLevelSeries(TreeInfo *node)
748 return (node != NULL && !node->node_group && !node->parent_link);
751 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
756 if (node->node_group) /* enter level group (step down into tree) */
757 return getFirstValidTreeInfoEntry(node->node_group);
758 else if (node->parent_link) /* skip start entry of level group */
760 if (node->next) /* get first real level series entry */
761 return getFirstValidTreeInfoEntry(node->next);
762 else /* leave empty level group and go on */
763 return getFirstValidTreeInfoEntry(node->node_parent->next);
765 else /* this seems to be a regular level series */
769 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
774 if (node->node_parent == NULL) /* top level group */
775 return *node->node_top;
776 else /* sub level group */
777 return node->node_parent->node_group;
780 int numTreeInfoInGroup(TreeInfo *node)
782 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
785 int posTreeInfo(TreeInfo *node)
787 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
792 if (node_cmp == node)
796 node_cmp = node_cmp->next;
802 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
804 TreeInfo *node_default = node;
819 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
821 if (identifier == NULL)
826 if (node->node_group)
828 TreeInfo *node_group;
830 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
835 else if (!node->parent_link)
837 if (strcmp(identifier, node->identifier) == 0)
847 void dumpTreeInfo(TreeInfo *node, int depth)
851 printf("Dumping TreeInfo:\n");
855 for (i=0; i<(depth + 1) * 3; i++)
858 printf("filename == '%s' (%s) [%s] (%d)\n",
859 node->filename, node->name, node->identifier, node->sort_priority);
861 if (node->node_group != NULL)
862 dumpTreeInfo(node->node_group, depth + 1);
868 void sortTreeInfo(TreeInfo **node_first,
869 int (*compare_function)(const void *, const void *))
871 int num_nodes = numTreeInfo(*node_first);
872 TreeInfo **sort_array;
873 TreeInfo *node = *node_first;
879 /* allocate array for sorting structure pointers */
880 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
882 /* writing structure pointers to sorting array */
883 while (i < num_nodes && node) /* double boundary check... */
885 sort_array[i] = node;
891 /* sorting the structure pointers in the sorting array */
892 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
895 /* update the linkage of list elements with the sorted node array */
896 for (i=0; i<num_nodes - 1; i++)
897 sort_array[i]->next = sort_array[i + 1];
898 sort_array[num_nodes - 1]->next = NULL;
900 /* update the linkage of the main list anchor pointer */
901 *node_first = sort_array[0];
905 /* now recursively sort the level group structures */
909 if (node->node_group != NULL)
910 sortTreeInfo(&node->node_group, compare_function);
917 /* ========================================================================= */
918 /* some stuff from "files.c" */
919 /* ========================================================================= */
921 #if defined(PLATFORM_WIN32)
923 #define S_IRGRP S_IRUSR
926 #define S_IROTH S_IRUSR
929 #define S_IWGRP S_IWUSR
932 #define S_IWOTH S_IWUSR
935 #define S_IXGRP S_IXUSR
938 #define S_IXOTH S_IXUSR
941 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
946 #endif /* PLATFORM_WIN32 */
948 /* file permissions for newly written files */
949 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
950 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
951 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
953 #define MODE_W_PRIVATE (S_IWUSR)
954 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
955 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
957 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
958 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
960 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
961 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
963 char *getUserDataDir(void)
965 static char *userdata_dir = NULL;
967 if (userdata_dir == NULL)
968 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
973 char *getCommonDataDir(void)
975 static char *common_data_dir = NULL;
977 #if defined(PLATFORM_WIN32)
978 if (common_data_dir == NULL)
980 char *dir = checked_malloc(MAX_PATH + 1);
982 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
983 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
984 common_data_dir = getPath2(dir, program.userdata_directory);
986 common_data_dir = options.rw_base_directory;
989 if (common_data_dir == NULL)
990 common_data_dir = options.rw_base_directory;
993 return common_data_dir;
998 return getUserDataDir();
1001 static mode_t posix_umask(mode_t mask)
1003 #if defined(PLATFORM_UNIX)
1010 static int posix_mkdir(const char *pathname, mode_t mode)
1012 #if defined(PLATFORM_WIN32)
1013 return mkdir(pathname);
1015 return mkdir(pathname, mode);
1019 void createDirectory(char *dir, char *text, int permission_class)
1021 /* leave "other" permissions in umask untouched, but ensure group parts
1022 of USERDATA_DIR_MODE are not masked */
1023 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1024 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1025 mode_t normal_umask = posix_umask(0);
1026 mode_t group_umask = ~(dir_mode & S_IRWXG);
1027 posix_umask(normal_umask & group_umask);
1029 if (access(dir, F_OK) != 0)
1030 if (posix_mkdir(dir, dir_mode) != 0)
1031 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1033 posix_umask(normal_umask); /* reset normal umask */
1036 void InitUserDataDirectory()
1038 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1041 void SetFilePermissions(char *filename, int permission_class)
1043 chmod(filename, (permission_class == PERMS_PRIVATE ?
1044 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1047 char *getCookie(char *file_type)
1049 static char cookie[MAX_COOKIE_LEN + 1];
1051 if (strlen(program.cookie_prefix) + 1 +
1052 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1053 return "[COOKIE ERROR]"; /* should never happen */
1055 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1056 program.cookie_prefix, file_type,
1057 program.version_major, program.version_minor);
1062 int getFileVersionFromCookieString(const char *cookie)
1064 const char *ptr_cookie1, *ptr_cookie2;
1065 const char *pattern1 = "_FILE_VERSION_";
1066 const char *pattern2 = "?.?";
1067 const int len_cookie = strlen(cookie);
1068 const int len_pattern1 = strlen(pattern1);
1069 const int len_pattern2 = strlen(pattern2);
1070 const int len_pattern = len_pattern1 + len_pattern2;
1071 int version_major, version_minor;
1073 if (len_cookie <= len_pattern)
1076 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1077 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1079 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1082 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1083 ptr_cookie2[1] != '.' ||
1084 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1087 version_major = ptr_cookie2[0] - '0';
1088 version_minor = ptr_cookie2[2] - '0';
1090 return VERSION_IDENT(version_major, version_minor, 0);
1093 boolean checkCookieString(const char *cookie, const char *template)
1095 const char *pattern = "_FILE_VERSION_?.?";
1096 const int len_cookie = strlen(cookie);
1097 const int len_template = strlen(template);
1098 const int len_pattern = strlen(pattern);
1100 if (len_cookie != len_template)
1103 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1109 /* ------------------------------------------------------------------------- */
1110 /* setup file list and hash handling functions */
1111 /* ------------------------------------------------------------------------- */
1113 char *getFormattedSetupEntry(char *token, char *value)
1116 static char entry[MAX_LINE_LEN];
1118 /* start with the token and some spaces to format output line */
1119 sprintf(entry, "%s:", token);
1120 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1123 /* continue with the token's value */
1124 strcat(entry, value);
1129 SetupFileList *newSetupFileList(char *token, char *value)
1131 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1133 new->token = getStringCopy(token);
1134 new->value = getStringCopy(value);
1141 void freeSetupFileList(SetupFileList *list)
1151 freeSetupFileList(list->next);
1155 char *getListEntry(SetupFileList *list, char *token)
1160 if (strcmp(list->token, token) == 0)
1163 return getListEntry(list->next, token);
1166 void setListEntry(SetupFileList *list, char *token, char *value)
1171 if (strcmp(list->token, token) == 0)
1176 list->value = getStringCopy(value);
1178 else if (list->next == NULL)
1179 list->next = newSetupFileList(token, value);
1181 setListEntry(list->next, token, value);
1185 static void printSetupFileList(SetupFileList *list)
1190 printf("token: '%s'\n", list->token);
1191 printf("value: '%s'\n", list->value);
1193 printSetupFileList(list->next);
1198 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1199 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1200 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1201 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1203 #define insert_hash_entry hashtable_insert
1204 #define search_hash_entry hashtable_search
1205 #define change_hash_entry hashtable_change
1206 #define remove_hash_entry hashtable_remove
1209 static unsigned int get_hash_from_key(void *key)
1214 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1215 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1216 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1217 it works better than many other constants, prime or not) has never been
1218 adequately explained.
1220 If you just want to have a good hash function, and cannot wait, djb2
1221 is one of the best string hash functions i know. It has excellent
1222 distribution and speed on many different sets of keys and table sizes.
1223 You are not likely to do better with one of the "well known" functions
1224 such as PJW, K&R, etc.
1226 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1229 char *str = (char *)key;
1230 unsigned int hash = 5381;
1233 while ((c = *str++))
1234 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1239 static int keys_are_equal(void *key1, void *key2)
1241 return (strcmp((char *)key1, (char *)key2) == 0);
1244 SetupFileHash *newSetupFileHash()
1246 SetupFileHash *new_hash =
1247 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1252 void freeSetupFileHash(SetupFileHash *hash)
1257 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1260 char *getHashEntry(SetupFileHash *hash, char *token)
1265 return search_hash_entry(hash, token);
1268 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1275 value_copy = getStringCopy(value);
1277 /* change value; if it does not exist, insert it as new */
1278 if (!change_hash_entry(hash, token, value_copy))
1279 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1280 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1285 static void printSetupFileHash(SetupFileHash *hash)
1287 BEGIN_HASH_ITERATION(hash, itr)
1289 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1290 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1292 END_HASH_ITERATION(hash, itr)
1297 static void *loadSetupFileData(char *filename, boolean use_hash)
1300 char line[MAX_LINE_LEN];
1301 char *token, *value, *line_ptr;
1302 void *setup_file_data;
1306 setup_file_data = newSetupFileHash();
1308 setup_file_data = newSetupFileList("", "");
1310 if (!(file = fopen(filename, MODE_READ)))
1312 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1318 /* read next line of input file */
1319 if (!fgets(line, MAX_LINE_LEN, file))
1322 /* cut trailing comment or whitespace from input line */
1323 for (line_ptr = line; *line_ptr; line_ptr++)
1325 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1332 /* cut trailing whitespaces from input line */
1333 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1334 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1337 /* ignore empty lines */
1341 line_len = strlen(line);
1343 /* cut leading whitespaces from token */
1344 for (token = line; *token; token++)
1345 if (*token != ' ' && *token != '\t')
1348 /* find end of token */
1349 for (line_ptr = token; *line_ptr; line_ptr++)
1351 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1358 if (line_ptr < line + line_len)
1359 value = line_ptr + 1;
1363 /* cut leading whitespaces from value */
1364 for (; *value; value++)
1365 if (*value != ' ' && *value != '\t')
1368 if (*token && *value)
1371 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1373 setListEntry((SetupFileList *)setup_file_data, token, value);
1381 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1382 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1386 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1387 SetupFileList *first_valid_list_entry = setup_file_list->next;
1389 /* free empty list header */
1390 setup_file_list->next = NULL;
1391 freeSetupFileList(setup_file_list);
1392 setup_file_data = first_valid_list_entry;
1394 if (first_valid_list_entry == NULL)
1395 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1398 return setup_file_data;
1401 SetupFileList *loadSetupFileList(char *filename)
1403 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1406 SetupFileHash *loadSetupFileHash(char *filename)
1408 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1411 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1414 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1417 Error(ERR_WARN, "configuration file has no file identifier");
1418 else if (!checkCookieString(value, identifier))
1419 Error(ERR_WARN, "configuration file has wrong file identifier");
1423 /* ========================================================================= */
1424 /* setup file stuff */
1425 /* ========================================================================= */
1427 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1428 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1429 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1431 /* level directory info */
1432 #define LEVELINFO_TOKEN_IDENTIFIER 0
1433 #define LEVELINFO_TOKEN_NAME 1
1434 #define LEVELINFO_TOKEN_NAME_SORTING 2
1435 #define LEVELINFO_TOKEN_AUTHOR 3
1436 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1437 #define LEVELINFO_TOKEN_LEVELS 5
1438 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1439 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1440 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1441 #define LEVELINFO_TOKEN_READONLY 9
1442 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1443 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1444 #define LEVELINFO_TOKEN_MUSIC_SET 12
1446 #define NUM_LEVELINFO_TOKENS 13
1448 static LevelDirTree ldi;
1450 static struct TokenInfo levelinfo_tokens[] =
1452 /* level directory info */
1453 { TYPE_STRING, &ldi.identifier, "identifier" },
1454 { TYPE_STRING, &ldi.name, "name" },
1455 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1456 { TYPE_STRING, &ldi.author, "author" },
1457 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1458 { TYPE_INTEGER, &ldi.levels, "levels" },
1459 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1460 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1461 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1462 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1463 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1464 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1465 { TYPE_STRING, &ldi.music_set, "music_set" }
1468 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1472 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1473 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1474 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1475 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1478 ldi->node_parent = NULL;
1479 ldi->node_group = NULL;
1483 ldi->cl_cursor = -1;
1485 ldi->filename = NULL;
1486 ldi->fullpath = NULL;
1487 ldi->basepath = NULL;
1488 ldi->identifier = NULL;
1489 ldi->name = getStringCopy(ANONYMOUS_NAME);
1490 ldi->name_sorting = NULL;
1491 ldi->author = getStringCopy(ANONYMOUS_NAME);
1493 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1494 ldi->parent_link = FALSE;
1495 ldi->user_defined = FALSE;
1497 ldi->class_desc = NULL;
1499 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1501 ldi->imported_from = NULL;
1503 ldi->graphics_set = NULL;
1504 ldi->sounds_set = NULL;
1505 ldi->music_set = NULL;
1506 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1507 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1508 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1511 ldi->first_level = 0;
1512 ldi->last_level = 0;
1513 ldi->level_group = FALSE;
1514 ldi->handicap_level = 0;
1515 ldi->readonly = TRUE;
1519 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1523 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1525 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1531 /* copy all values from the parent structure */
1533 ldi->type = parent->type;
1535 ldi->node_top = parent->node_top;
1536 ldi->node_parent = parent;
1537 ldi->node_group = NULL;
1541 ldi->cl_cursor = -1;
1543 ldi->filename = NULL;
1544 ldi->fullpath = NULL;
1545 ldi->basepath = NULL;
1546 ldi->identifier = NULL;
1547 ldi->name = getStringCopy(ANONYMOUS_NAME);
1548 ldi->name_sorting = NULL;
1549 ldi->author = getStringCopy(parent->author);
1551 ldi->sort_priority = parent->sort_priority;
1552 ldi->parent_link = FALSE;
1553 ldi->user_defined = parent->user_defined;
1554 ldi->color = parent->color;
1555 ldi->class_desc = getStringCopy(parent->class_desc);
1557 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1559 ldi->imported_from = getStringCopy(parent->imported_from);
1561 ldi->graphics_set = NULL;
1562 ldi->sounds_set = NULL;
1563 ldi->music_set = NULL;
1564 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1565 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1566 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1569 ldi->first_level = 0;
1570 ldi->last_level = 0;
1571 ldi->level_group = FALSE;
1572 ldi->handicap_level = 0;
1573 ldi->readonly = TRUE;
1579 /* first copy all values from the parent structure ... */
1582 /* ... then set all fields to default that cannot be inherited from parent.
1583 This is especially important for all those fields that can be set from
1584 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1585 calls 'free()' for all already set token values which requires that no
1586 other structure's pointer may point to them!
1589 ldi->filename = NULL;
1590 ldi->fullpath = NULL;
1591 ldi->basepath = NULL;
1592 ldi->identifier = NULL;
1593 ldi->name = getStringCopy(ANONYMOUS_NAME);
1594 ldi->name_sorting = NULL;
1595 ldi->author = getStringCopy(parent->author);
1597 ldi->imported_from = getStringCopy(parent->imported_from);
1598 ldi->class_desc = getStringCopy(parent->class_desc);
1600 ldi->graphics_set = NULL;
1601 ldi->sounds_set = NULL;
1602 ldi->music_set = NULL;
1603 ldi->graphics_path = NULL;
1604 ldi->sounds_path = NULL;
1605 ldi->music_path = NULL;
1607 ldi->level_group = FALSE;
1608 ldi->parent_link = FALSE;
1610 ldi->node_top = parent->node_top;
1611 ldi->node_parent = parent;
1612 ldi->node_group = NULL;
1618 void setSetupInfo(struct TokenInfo *token_info,
1619 int token_nr, char *token_value)
1621 int token_type = token_info[token_nr].type;
1622 void *setup_value = token_info[token_nr].value;
1624 if (token_value == NULL)
1627 /* set setup field to corresponding token value */
1632 *(boolean *)setup_value = get_boolean_from_string(token_value);
1636 *(Key *)setup_value = getKeyFromKeyName(token_value);
1640 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1644 *(int *)setup_value = get_integer_from_string(token_value);
1648 if (*(char **)setup_value != NULL)
1649 free(*(char **)setup_value);
1650 *(char **)setup_value = getStringCopy(token_value);
1658 static int compareTreeInfoEntries(const void *object1, const void *object2)
1660 const TreeInfo *entry1 = *((TreeInfo **)object1);
1661 const TreeInfo *entry2 = *((TreeInfo **)object2);
1662 int class_sorting1, class_sorting2;
1665 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1667 class_sorting1 = LEVELSORTING(entry1);
1668 class_sorting2 = LEVELSORTING(entry2);
1672 class_sorting1 = ARTWORKSORTING(entry1);
1673 class_sorting2 = ARTWORKSORTING(entry2);
1676 if (entry1->parent_link || entry2->parent_link)
1677 compare_result = (entry1->parent_link ? -1 : +1);
1678 else if (entry1->sort_priority == entry2->sort_priority)
1680 char *name1 = getStringToLower(entry1->name_sorting);
1681 char *name2 = getStringToLower(entry2->name_sorting);
1683 compare_result = strcmp(name1, name2);
1688 else if (class_sorting1 == class_sorting2)
1689 compare_result = entry1->sort_priority - entry2->sort_priority;
1691 compare_result = class_sorting1 - class_sorting2;
1693 return compare_result;
1696 static void createParentTreeInfoNode(TreeInfo *node_parent)
1700 if (node_parent == NULL)
1703 ti_new = newTreeInfo();
1704 setTreeInfoToDefaults(ti_new, node_parent->type);
1706 ti_new->node_parent = node_parent;
1707 ti_new->parent_link = TRUE;
1709 ti_new->identifier = getStringCopy(node_parent->identifier);
1710 ti_new->name = ".. (parent directory)";
1711 ti_new->name_sorting = getStringCopy(ti_new->name);
1713 ti_new->filename = "..";
1714 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1716 ti_new->sort_priority = node_parent->sort_priority;
1717 ti_new->class_desc = getLevelClassDescription(ti_new);
1719 pushTreeInfo(&node_parent->node_group, ti_new);
1722 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1723 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1725 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1726 TreeInfo *node_parent,
1727 char *level_directory,
1728 char *directory_name)
1730 char *directory_path = getPath2(level_directory, directory_name);
1731 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1732 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1733 LevelDirTree *leveldir_new = NULL;
1736 if (setup_file_hash == NULL)
1738 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1740 free(directory_path);
1746 leveldir_new = newTreeInfo();
1749 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1751 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1753 leveldir_new->filename = getStringCopy(directory_name);
1755 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1757 /* set all structure fields according to the token/value pairs */
1758 ldi = *leveldir_new;
1759 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1760 setSetupInfo(levelinfo_tokens, i,
1761 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1762 *leveldir_new = ldi;
1764 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1766 free(leveldir_new->name);
1767 leveldir_new->name = getStringCopy(leveldir_new->filename);
1770 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1772 if (leveldir_new->identifier == NULL)
1773 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1775 if (leveldir_new->name_sorting == NULL)
1776 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1778 if (node_parent == NULL) /* top level group */
1780 leveldir_new->basepath = level_directory;
1781 leveldir_new->fullpath = leveldir_new->filename;
1783 else /* sub level group */
1785 leveldir_new->basepath = node_parent->basepath;
1786 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1789 if (leveldir_new->levels < 1)
1790 leveldir_new->levels = 1;
1792 leveldir_new->last_level =
1793 leveldir_new->first_level + leveldir_new->levels - 1;
1795 leveldir_new->user_defined =
1796 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1798 leveldir_new->color = LEVELCOLOR(leveldir_new);
1799 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1801 leveldir_new->handicap_level = /* set handicap to default value */
1802 (leveldir_new->user_defined ?
1803 leveldir_new->last_level :
1804 leveldir_new->first_level);
1806 pushTreeInfo(node_first, leveldir_new);
1808 freeSetupFileHash(setup_file_hash);
1810 if (leveldir_new->level_group)
1812 /* create node to link back to current level directory */
1813 createParentTreeInfoNode(leveldir_new);
1815 /* step into sub-directory and look for more level series */
1816 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1817 leveldir_new, directory_path);
1820 free(directory_path);
1826 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1827 TreeInfo *node_parent,
1828 char *level_directory)
1831 struct dirent *dir_entry;
1832 boolean valid_entry_found = FALSE;
1834 if ((dir = opendir(level_directory)) == NULL)
1836 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1840 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1842 struct stat file_status;
1843 char *directory_name = dir_entry->d_name;
1844 char *directory_path = getPath2(level_directory, directory_name);
1846 /* skip entries for current and parent directory */
1847 if (strcmp(directory_name, ".") == 0 ||
1848 strcmp(directory_name, "..") == 0)
1850 free(directory_path);
1854 /* find out if directory entry is itself a directory */
1855 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1856 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1858 free(directory_path);
1862 free(directory_path);
1864 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1865 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1866 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1869 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1876 if (!valid_entry_found)
1878 /* check if this directory directly contains a file "levelinfo.conf" */
1879 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1880 level_directory, ".");
1883 if (!valid_entry_found)
1884 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1888 void LoadLevelInfo()
1890 InitUserLevelDirectory(getLoginName());
1892 DrawInitText("Loading level series:", 120, FC_GREEN);
1894 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1895 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1897 /* before sorting, the first entries will be from the user directory */
1898 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1900 if (leveldir_first == NULL)
1901 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1903 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1906 dumpTreeInfo(leveldir_first, 0);
1910 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1911 TreeInfo *node_parent,
1912 char *base_directory,
1913 char *directory_name, int type)
1915 char *directory_path = getPath2(base_directory, directory_name);
1916 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1917 SetupFileHash *setup_file_hash = NULL;
1918 TreeInfo *artwork_new = NULL;
1921 if (access(filename, F_OK) == 0) /* file exists */
1922 setup_file_hash = loadSetupFileHash(filename);
1924 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1927 struct dirent *dir_entry;
1928 boolean valid_file_found = FALSE;
1930 if ((dir = opendir(directory_path)) != NULL)
1932 while ((dir_entry = readdir(dir)) != NULL)
1934 char *entry_name = dir_entry->d_name;
1936 if (FileIsArtworkType(entry_name, type))
1938 valid_file_found = TRUE;
1946 if (!valid_file_found)
1948 if (strcmp(directory_name, ".") != 0)
1949 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1951 free(directory_path);
1958 artwork_new = newTreeInfo();
1961 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1963 setTreeInfoToDefaults(artwork_new, type);
1965 artwork_new->filename = getStringCopy(directory_name);
1967 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1970 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1973 /* set all structure fields according to the token/value pairs */
1975 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1976 setSetupInfo(levelinfo_tokens, i,
1977 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1980 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1982 free(artwork_new->name);
1983 artwork_new->name = getStringCopy(artwork_new->filename);
1987 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1990 if (artwork_new->identifier == NULL)
1991 artwork_new->identifier = getStringCopy(artwork_new->filename);
1993 if (artwork_new->name_sorting == NULL)
1994 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1997 if (node_parent == NULL) /* top level group */
1999 artwork_new->basepath = getStringCopy(base_directory);
2000 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2002 else /* sub level group */
2004 artwork_new->basepath = getStringCopy(node_parent->basepath);
2005 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2008 artwork_new->user_defined =
2009 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2011 /* (may use ".sort_priority" from "setup_file_hash" above) */
2012 artwork_new->color = ARTWORKCOLOR(artwork_new);
2013 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2015 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2017 if (artwork_new->name != NULL)
2018 free(artwork_new->name);
2020 if (strcmp(artwork_new->filename, ".") == 0)
2022 if (artwork_new->user_defined)
2024 artwork_new->identifier = getStringCopy("private");
2025 artwork_new->sort_priority = ARTWORKCLASS_USER;
2029 artwork_new->identifier = getStringCopy("classic");
2030 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2033 /* set to new values after changing ".sort_priority" */
2034 artwork_new->color = ARTWORKCOLOR(artwork_new);
2035 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2039 artwork_new->identifier = getStringCopy(artwork_new->filename);
2042 artwork_new->name = getStringCopy(artwork_new->identifier);
2043 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2046 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2048 pushTreeInfo(node_first, artwork_new);
2050 freeSetupFileHash(setup_file_hash);
2052 free(directory_path);
2058 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2059 TreeInfo *node_parent,
2060 char *base_directory, int type)
2063 struct dirent *dir_entry;
2064 boolean valid_entry_found = FALSE;
2066 if ((dir = opendir(base_directory)) == NULL)
2068 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2069 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2073 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2075 struct stat file_status;
2076 char *directory_name = dir_entry->d_name;
2077 char *directory_path = getPath2(base_directory, directory_name);
2079 /* skip entries for current and parent directory */
2080 if (strcmp(directory_name, ".") == 0 ||
2081 strcmp(directory_name, "..") == 0)
2083 free(directory_path);
2087 /* find out if directory entry is itself a directory */
2088 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2089 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2091 free(directory_path);
2095 free(directory_path);
2097 /* check if this directory contains artwork with or without config file */
2098 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2100 directory_name, type);
2105 /* check if this directory directly contains artwork itself */
2106 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2107 base_directory, ".",
2109 if (!valid_entry_found)
2110 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2114 static TreeInfo *getDummyArtworkInfo(int type)
2116 /* this is only needed when there is completely no artwork available */
2117 TreeInfo *artwork_new = newTreeInfo();
2119 setTreeInfoToDefaults(artwork_new, type);
2121 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2122 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2123 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2125 if (artwork_new->name != NULL)
2126 free(artwork_new->name);
2128 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2129 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2130 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2135 void LoadArtworkInfo()
2137 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2139 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2140 options.graphics_directory,
2141 TREE_TYPE_GRAPHICS_DIR);
2142 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2143 getUserGraphicsDir(),
2144 TREE_TYPE_GRAPHICS_DIR);
2146 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2147 options.sounds_directory,
2148 TREE_TYPE_SOUNDS_DIR);
2149 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2151 TREE_TYPE_SOUNDS_DIR);
2153 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2154 options.music_directory,
2155 TREE_TYPE_MUSIC_DIR);
2156 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2158 TREE_TYPE_MUSIC_DIR);
2160 if (artwork.gfx_first == NULL)
2161 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2162 if (artwork.snd_first == NULL)
2163 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2164 if (artwork.mus_first == NULL)
2165 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2167 /* before sorting, the first entries will be from the user directory */
2168 artwork.gfx_current =
2169 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2170 if (artwork.gfx_current == NULL)
2171 artwork.gfx_current =
2172 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2173 if (artwork.gfx_current == NULL)
2174 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2176 artwork.snd_current =
2177 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2178 if (artwork.snd_current == NULL)
2179 artwork.snd_current =
2180 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2181 if (artwork.snd_current == NULL)
2182 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2184 artwork.mus_current =
2185 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2186 if (artwork.mus_current == NULL)
2187 artwork.mus_current =
2188 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2189 if (artwork.mus_current == NULL)
2190 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2192 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2193 artwork.snd_current_identifier = artwork.snd_current->identifier;
2194 artwork.mus_current_identifier = artwork.mus_current->identifier;
2197 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2198 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2199 printf("music set == %s\n\n", artwork.mus_current_identifier);
2202 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2203 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2204 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2207 dumpTreeInfo(artwork.gfx_first, 0);
2208 dumpTreeInfo(artwork.snd_first, 0);
2209 dumpTreeInfo(artwork.mus_first, 0);
2213 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2214 LevelDirTree *level_node)
2216 /* recursively check all level directories for artwork sub-directories */
2220 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2221 ARTWORK_DIRECTORY((*artwork_node)->type));
2224 if (!level_node->parent_link)
2225 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2226 level_node->filename, level_node->name);
2229 if (!level_node->parent_link)
2231 TreeInfo *topnode_last = *artwork_node;
2233 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2234 (*artwork_node)->type);
2236 if (topnode_last != *artwork_node)
2238 free((*artwork_node)->identifier);
2239 free((*artwork_node)->name);
2240 free((*artwork_node)->name_sorting);
2242 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2243 (*artwork_node)->name = getStringCopy(level_node->name);
2244 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2246 (*artwork_node)->sort_priority = level_node->sort_priority;
2247 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2253 if (level_node->node_group != NULL)
2254 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2256 level_node = level_node->next;
2260 void LoadLevelArtworkInfo()
2262 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2264 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2265 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2266 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2268 /* needed for reloading level artwork not known at ealier stage */
2269 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2271 artwork.gfx_current =
2272 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2273 if (artwork.gfx_current == NULL)
2274 artwork.gfx_current =
2275 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2276 if (artwork.gfx_current == NULL)
2277 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2280 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2282 artwork.snd_current =
2283 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2284 if (artwork.snd_current == NULL)
2285 artwork.snd_current =
2286 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2287 if (artwork.snd_current == NULL)
2288 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2291 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2293 artwork.mus_current =
2294 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2295 if (artwork.mus_current == NULL)
2296 artwork.mus_current =
2297 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2298 if (artwork.mus_current == NULL)
2299 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2302 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2303 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2304 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2307 dumpTreeInfo(artwork.gfx_first, 0);
2308 dumpTreeInfo(artwork.snd_first, 0);
2309 dumpTreeInfo(artwork.mus_first, 0);
2313 static void SaveUserLevelInfo()
2319 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2321 if (!(file = fopen(filename, MODE_WRITE)))
2323 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2328 /* always start with reliable default values */
2329 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2332 /* !!! FIX ME !!! */
2333 setString(&ldi.name, getLoginName());
2334 setString(&ldi.author, getRealName());
2336 ldi.first_level = 1;
2337 ldi.sort_priority = LEVELCLASS_USER_START;
2338 ldi.readonly = FALSE;
2339 setString(&ldi.graphics_set, GRAPHICS_SUBDIR);
2340 setString(&ldi.sounds_set, SOUNDS_SUBDIR);
2341 setString(&ldi.music_set, MUSIC_SUBDIR);
2343 ldi.name = getStringCopy(getLoginName());
2344 ldi.author = getStringCopy(getRealName());
2346 ldi.first_level = 1;
2347 ldi.sort_priority = LEVELCLASS_USER_START;
2348 ldi.readonly = FALSE;
2349 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2350 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2351 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2354 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2355 getCookie("LEVELINFO")));
2357 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2358 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2359 i != LEVELINFO_TOKEN_NAME_SORTING &&
2360 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2361 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2366 SetFilePermissions(filename, PERMS_PRIVATE);
2369 char *getSetupValue(int type, void *value)
2371 static char value_string[MAX_LINE_LEN];
2379 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2383 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2387 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2391 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2395 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2399 sprintf(value_string, "%d", *(int *)value);
2403 strcpy(value_string, *(char **)value);
2407 value_string[0] = '\0';
2411 return value_string;
2414 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2418 static char token_string[MAX_LINE_LEN];
2419 int token_type = token_info[token_nr].type;
2420 void *setup_value = token_info[token_nr].value;
2421 char *token_text = token_info[token_nr].text;
2422 char *value_string = getSetupValue(token_type, setup_value);
2424 /* build complete token string */
2425 sprintf(token_string, "%s%s", prefix, token_text);
2427 /* build setup entry line */
2428 line = getFormattedSetupEntry(token_string, value_string);
2430 if (token_type == TYPE_KEY_X11)
2432 Key key = *(Key *)setup_value;
2433 char *keyname = getKeyNameFromKey(key);
2435 /* add comment, if useful */
2436 if (strcmp(keyname, "(undefined)") != 0 &&
2437 strcmp(keyname, "(unknown)") != 0)
2439 /* add at least one whitespace */
2441 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2445 strcat(line, keyname);
2452 void LoadLevelSetup_LastSeries()
2455 SetupFileHash *level_setup_hash = NULL;
2457 /* always start with reliable default values */
2458 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2460 /* ----------------------------------------------------------------------- */
2461 /* ~/.<program>/levelsetup.conf */
2462 /* ----------------------------------------------------------------------- */
2464 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2466 if ((level_setup_hash = loadSetupFileHash(filename)))
2468 char *last_level_series =
2469 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2471 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2473 if (leveldir_current == NULL)
2474 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2476 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2478 freeSetupFileHash(level_setup_hash);
2481 Error(ERR_WARN, "using default setup values");
2486 void SaveLevelSetup_LastSeries()
2489 char *level_subdir = leveldir_current->filename;
2492 /* ----------------------------------------------------------------------- */
2493 /* ~/.<program>/levelsetup.conf */
2494 /* ----------------------------------------------------------------------- */
2496 InitUserDataDirectory();
2498 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2500 if (!(file = fopen(filename, MODE_WRITE)))
2502 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2507 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2508 getCookie("LEVELSETUP")));
2509 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2515 SetFilePermissions(filename, PERMS_PRIVATE);
2518 static void checkSeriesInfo()
2520 static char *level_directory = NULL;
2522 struct dirent *dir_entry;
2524 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2526 level_directory = getPath2((leveldir_current->user_defined ?
2527 getUserLevelDir(NULL) :
2528 options.level_directory),
2529 leveldir_current->fullpath);
2531 if ((dir = opendir(level_directory)) == NULL)
2533 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2537 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2539 if (strlen(dir_entry->d_name) > 4 &&
2540 dir_entry->d_name[3] == '.' &&
2541 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2543 char levelnum_str[4];
2546 strncpy(levelnum_str, dir_entry->d_name, 3);
2547 levelnum_str[3] = '\0';
2549 levelnum_value = atoi(levelnum_str);
2552 if (levelnum_value < leveldir_current->first_level)
2554 Error(ERR_WARN, "additional level %d found", levelnum_value);
2555 leveldir_current->first_level = levelnum_value;
2557 else if (levelnum_value > leveldir_current->last_level)
2559 Error(ERR_WARN, "additional level %d found", levelnum_value);
2560 leveldir_current->last_level = levelnum_value;
2569 void LoadLevelSetup_SeriesInfo()
2572 SetupFileHash *level_setup_hash = NULL;
2573 char *level_subdir = leveldir_current->filename;
2575 /* always start with reliable default values */
2576 level_nr = leveldir_current->first_level;
2578 checkSeriesInfo(leveldir_current);
2580 /* ----------------------------------------------------------------------- */
2581 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2582 /* ----------------------------------------------------------------------- */
2584 level_subdir = leveldir_current->filename;
2586 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2588 if ((level_setup_hash = loadSetupFileHash(filename)))
2592 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2596 level_nr = atoi(token_value);
2598 if (level_nr < leveldir_current->first_level)
2599 level_nr = leveldir_current->first_level;
2600 if (level_nr > leveldir_current->last_level)
2601 level_nr = leveldir_current->last_level;
2604 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2608 int level_nr = atoi(token_value);
2610 if (level_nr < leveldir_current->first_level)
2611 level_nr = leveldir_current->first_level;
2612 if (level_nr > leveldir_current->last_level + 1)
2613 level_nr = leveldir_current->last_level;
2615 if (leveldir_current->user_defined)
2616 level_nr = leveldir_current->last_level;
2618 leveldir_current->handicap_level = level_nr;
2621 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2623 freeSetupFileHash(level_setup_hash);
2626 Error(ERR_WARN, "using default setup values");
2631 void SaveLevelSetup_SeriesInfo()
2634 char *level_subdir = leveldir_current->filename;
2635 char *level_nr_str = int2str(level_nr, 0);
2636 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2639 /* ----------------------------------------------------------------------- */
2640 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2641 /* ----------------------------------------------------------------------- */
2643 InitLevelSetupDirectory(level_subdir);
2645 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2647 if (!(file = fopen(filename, MODE_WRITE)))
2649 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2654 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2655 getCookie("LEVELSETUP")));
2656 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2658 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2659 handicap_level_str));
2664 SetFilePermissions(filename, PERMS_PRIVATE);