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 void 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;
375 inline static char *getLevelArtworkSet(int type)
377 if (leveldir_current == NULL)
380 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
383 inline static char *getLevelArtworkDir(int type)
385 if (leveldir_current == NULL)
386 return UNDEFINED_FILENAME;
388 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
391 char *getLevelFilename(int nr)
393 static char *filename = NULL;
394 char basename[MAX_FILENAME_LEN];
396 if (filename != NULL)
400 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
402 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
404 filename = getPath2(getCurrentLevelDir(), basename);
409 char *getTapeFilename(int nr)
411 static char *filename = NULL;
412 char basename[MAX_FILENAME_LEN];
414 if (filename != NULL)
417 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
418 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
423 char *getScoreFilename(int nr)
425 static char *filename = NULL;
426 char basename[MAX_FILENAME_LEN];
428 if (filename != NULL)
431 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
432 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
437 char *getSetupFilename()
439 static char *filename = NULL;
441 if (filename != NULL)
444 filename = getPath2(getSetupDir(), SETUP_FILENAME);
449 static char *getCorrectedImageBasename(char *basename)
451 char *basename_corrected = basename;
453 #if defined(PLATFORM_MSDOS)
454 if (program.filename_prefix != NULL)
456 int prefix_len = strlen(program.filename_prefix);
458 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
459 basename_corrected = &basename[prefix_len];
461 /* if corrected filename is still longer than standard MS-DOS filename
462 size (8 characters + 1 dot + 3 characters file extension), shorten
463 filename by writing file extension after 8th basename character */
464 if (strlen(basename_corrected) > 8+1+3)
466 static char *msdos_filename = NULL;
468 if (msdos_filename != NULL)
469 free(msdos_filename);
471 msdos_filename = getStringCopy(basename_corrected);
472 strncpy(&msdos_filename[8], &basename[strlen(basename) - 1+3], 1+3 + 1);
477 return basename_corrected;
480 char *getCustomImageFilename(char *basename)
482 static char *filename = NULL;
483 boolean skip_setup_artwork = FALSE;
485 if (filename != NULL)
488 basename = getCorrectedImageBasename(basename);
490 if (!setup.override_level_graphics)
492 /* 1st try: look for special artwork in current level series directory */
493 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
494 if (fileExists(filename))
499 /* check if there is special artwork configured in level series config */
500 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
502 /* 2nd try: look for special artwork configured in level series config */
503 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
504 if (fileExists(filename))
509 /* take missing artwork configured in level set config from default */
510 skip_setup_artwork = TRUE;
514 if (!skip_setup_artwork)
516 /* 3rd try: look for special artwork in configured artwork directory */
517 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
518 if (fileExists(filename))
524 /* 4th try: look for default artwork in new default artwork directory */
525 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
526 if (fileExists(filename))
531 /* 5th try: look for default artwork in old default artwork directory */
532 filename = getPath2(options.graphics_directory, basename);
533 if (fileExists(filename))
536 return NULL; /* cannot find specified artwork file anywhere */
539 char *getCustomSoundFilename(char *basename)
541 static char *filename = NULL;
542 boolean skip_setup_artwork = FALSE;
544 if (filename != NULL)
547 if (!setup.override_level_sounds)
549 /* 1st try: look for special artwork in current level series directory */
550 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
551 if (fileExists(filename))
556 /* check if there is special artwork configured in level series config */
557 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
559 /* 2nd try: look for special artwork configured in level series config */
560 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
561 if (fileExists(filename))
566 /* take missing artwork configured in level set config from default */
567 skip_setup_artwork = TRUE;
571 if (!skip_setup_artwork)
573 /* 3rd try: look for special artwork in configured artwork directory */
574 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
575 if (fileExists(filename))
581 /* 4th try: look for default artwork in new default artwork directory */
582 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
583 if (fileExists(filename))
588 /* 5th try: look for default artwork in old default artwork directory */
589 filename = getPath2(options.sounds_directory, basename);
590 if (fileExists(filename))
593 return NULL; /* cannot find specified artwork file anywhere */
596 char *getCustomArtworkFilename(char *basename, int type)
598 if (type == ARTWORK_TYPE_GRAPHICS)
599 return getCustomImageFilename(basename);
600 else if (type == ARTWORK_TYPE_SOUNDS)
601 return getCustomSoundFilename(basename);
603 return UNDEFINED_FILENAME;
606 char *getCustomArtworkConfigFilename(int type)
608 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
611 char *getCustomArtworkLevelConfigFilename(int type)
613 static char *filename = NULL;
615 if (filename != NULL)
618 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
623 char *getCustomMusicDirectory(void)
625 static char *directory = NULL;
626 boolean skip_setup_artwork = FALSE;
628 if (directory != NULL)
631 if (!setup.override_level_music)
633 /* 1st try: look for special artwork in current level series directory */
634 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
635 if (fileExists(directory))
640 /* check if there is special artwork configured in level series config */
641 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
643 /* 2nd try: look for special artwork configured in level series config */
644 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
645 if (fileExists(directory))
650 /* take missing artwork configured in level set config from default */
651 skip_setup_artwork = TRUE;
655 if (!skip_setup_artwork)
657 /* 3rd try: look for special artwork in configured artwork directory */
658 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
659 if (fileExists(directory))
665 /* 4th try: look for default artwork in new default artwork directory */
666 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
667 if (fileExists(directory))
672 /* 5th try: look for default artwork in old default artwork directory */
673 directory = getStringCopy(options.music_directory);
674 if (fileExists(directory))
677 return NULL; /* cannot find specified artwork file anywhere */
680 void InitTapeDirectory(char *level_subdir)
682 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
683 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
684 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
687 void InitScoreDirectory(char *level_subdir)
689 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
690 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
691 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
694 static void SaveUserLevelInfo();
696 void InitUserLevelDirectory(char *level_subdir)
698 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
700 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
701 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
702 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
708 void InitLevelSetupDirectory(char *level_subdir)
710 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
711 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
712 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
716 /* ------------------------------------------------------------------------- */
717 /* some functions to handle lists of level directories */
718 /* ------------------------------------------------------------------------- */
720 TreeInfo *newTreeInfo()
722 return checked_calloc(sizeof(TreeInfo));
725 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
727 node_new->next = *node_first;
728 *node_first = node_new;
731 int numTreeInfo(TreeInfo *node)
744 boolean validLevelSeries(TreeInfo *node)
746 return (node != NULL && !node->node_group && !node->parent_link);
749 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
754 if (node->node_group) /* enter level group (step down into tree) */
755 return getFirstValidTreeInfoEntry(node->node_group);
756 else if (node->parent_link) /* skip start entry of level group */
758 if (node->next) /* get first real level series entry */
759 return getFirstValidTreeInfoEntry(node->next);
760 else /* leave empty level group and go on */
761 return getFirstValidTreeInfoEntry(node->node_parent->next);
763 else /* this seems to be a regular level series */
767 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
772 if (node->node_parent == NULL) /* top level group */
773 return *node->node_top;
774 else /* sub level group */
775 return node->node_parent->node_group;
778 int numTreeInfoInGroup(TreeInfo *node)
780 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
783 int posTreeInfo(TreeInfo *node)
785 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
790 if (node_cmp == node)
794 node_cmp = node_cmp->next;
800 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
802 TreeInfo *node_default = node;
817 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
819 if (identifier == NULL)
824 if (node->node_group)
826 TreeInfo *node_group;
828 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
833 else if (!node->parent_link)
835 if (strcmp(identifier, node->identifier) == 0)
845 void dumpTreeInfo(TreeInfo *node, int depth)
849 printf("Dumping TreeInfo:\n");
853 for (i=0; i<(depth + 1) * 3; i++)
856 printf("filename == '%s' (%s) [%s] (%d)\n",
857 node->filename, node->name, node->identifier, node->sort_priority);
859 if (node->node_group != NULL)
860 dumpTreeInfo(node->node_group, depth + 1);
866 void sortTreeInfo(TreeInfo **node_first,
867 int (*compare_function)(const void *, const void *))
869 int num_nodes = numTreeInfo(*node_first);
870 TreeInfo **sort_array;
871 TreeInfo *node = *node_first;
877 /* allocate array for sorting structure pointers */
878 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
880 /* writing structure pointers to sorting array */
881 while (i < num_nodes && node) /* double boundary check... */
883 sort_array[i] = node;
889 /* sorting the structure pointers in the sorting array */
890 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
893 /* update the linkage of list elements with the sorted node array */
894 for (i=0; i<num_nodes - 1; i++)
895 sort_array[i]->next = sort_array[i + 1];
896 sort_array[num_nodes - 1]->next = NULL;
898 /* update the linkage of the main list anchor pointer */
899 *node_first = sort_array[0];
903 /* now recursively sort the level group structures */
907 if (node->node_group != NULL)
908 sortTreeInfo(&node->node_group, compare_function);
915 /* ========================================================================= */
916 /* some stuff from "files.c" */
917 /* ========================================================================= */
919 #if defined(PLATFORM_WIN32)
921 #define S_IRGRP S_IRUSR
924 #define S_IROTH S_IRUSR
927 #define S_IWGRP S_IWUSR
930 #define S_IWOTH S_IWUSR
933 #define S_IXGRP S_IXUSR
936 #define S_IXOTH S_IXUSR
939 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
944 #endif /* PLATFORM_WIN32 */
946 /* file permissions for newly written files */
947 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
948 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
949 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
951 #define MODE_W_PRIVATE (S_IWUSR)
952 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
953 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
955 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
956 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
958 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
959 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
961 char *getUserDataDir(void)
963 static char *userdata_dir = NULL;
965 if (userdata_dir == NULL)
966 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
971 char *getCommonDataDir(void)
973 static char *common_data_dir = NULL;
975 #if defined(PLATFORM_WIN32)
976 if (common_data_dir == NULL)
978 char *dir = checked_malloc(MAX_PATH + 1);
980 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
981 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
982 common_data_dir = getPath2(dir, program.userdata_directory);
984 common_data_dir = options.rw_base_directory;
987 if (common_data_dir == NULL)
988 common_data_dir = options.rw_base_directory;
991 return common_data_dir;
996 return getUserDataDir();
999 static mode_t posix_umask(mode_t mask)
1001 #if defined(PLATFORM_UNIX)
1008 static int posix_mkdir(const char *pathname, mode_t mode)
1010 #if defined(PLATFORM_WIN32)
1011 return mkdir(pathname);
1013 return mkdir(pathname, mode);
1017 void createDirectory(char *dir, char *text, int permission_class)
1019 /* leave "other" permissions in umask untouched, but ensure group parts
1020 of USERDATA_DIR_MODE are not masked */
1021 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1022 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1023 mode_t normal_umask = posix_umask(0);
1024 mode_t group_umask = ~(dir_mode & S_IRWXG);
1025 posix_umask(normal_umask & group_umask);
1027 if (access(dir, F_OK) != 0)
1028 if (posix_mkdir(dir, dir_mode) != 0)
1029 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1031 posix_umask(normal_umask); /* reset normal umask */
1034 void InitUserDataDirectory()
1036 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1039 void SetFilePermissions(char *filename, int permission_class)
1041 chmod(filename, (permission_class == PERMS_PRIVATE ?
1042 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1045 char *getCookie(char *file_type)
1047 static char cookie[MAX_COOKIE_LEN + 1];
1049 if (strlen(program.cookie_prefix) + 1 +
1050 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1051 return "[COOKIE ERROR]"; /* should never happen */
1053 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1054 program.cookie_prefix, file_type,
1055 program.version_major, program.version_minor);
1060 int getFileVersionFromCookieString(const char *cookie)
1062 const char *ptr_cookie1, *ptr_cookie2;
1063 const char *pattern1 = "_FILE_VERSION_";
1064 const char *pattern2 = "?.?";
1065 const int len_cookie = strlen(cookie);
1066 const int len_pattern1 = strlen(pattern1);
1067 const int len_pattern2 = strlen(pattern2);
1068 const int len_pattern = len_pattern1 + len_pattern2;
1069 int version_major, version_minor;
1071 if (len_cookie <= len_pattern)
1074 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1075 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1077 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1080 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1081 ptr_cookie2[1] != '.' ||
1082 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1085 version_major = ptr_cookie2[0] - '0';
1086 version_minor = ptr_cookie2[2] - '0';
1088 return VERSION_IDENT(version_major, version_minor, 0);
1091 boolean checkCookieString(const char *cookie, const char *template)
1093 const char *pattern = "_FILE_VERSION_?.?";
1094 const int len_cookie = strlen(cookie);
1095 const int len_template = strlen(template);
1096 const int len_pattern = strlen(pattern);
1098 if (len_cookie != len_template)
1101 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1107 /* ------------------------------------------------------------------------- */
1108 /* setup file list and hash handling functions */
1109 /* ------------------------------------------------------------------------- */
1111 char *getFormattedSetupEntry(char *token, char *value)
1114 static char entry[MAX_LINE_LEN];
1116 /* start with the token and some spaces to format output line */
1117 sprintf(entry, "%s:", token);
1118 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1121 /* continue with the token's value */
1122 strcat(entry, value);
1127 SetupFileList *newSetupFileList(char *token, char *value)
1129 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1131 new->token = getStringCopy(token);
1132 new->value = getStringCopy(value);
1139 void freeSetupFileList(SetupFileList *list)
1149 freeSetupFileList(list->next);
1153 char *getListEntry(SetupFileList *list, char *token)
1158 if (strcmp(list->token, token) == 0)
1161 return getListEntry(list->next, token);
1164 void setListEntry(SetupFileList *list, char *token, char *value)
1169 if (strcmp(list->token, token) == 0)
1174 list->value = getStringCopy(value);
1176 else if (list->next == NULL)
1177 list->next = newSetupFileList(token, value);
1179 setListEntry(list->next, token, value);
1183 static void printSetupFileList(SetupFileList *list)
1188 printf("token: '%s'\n", list->token);
1189 printf("value: '%s'\n", list->value);
1191 printSetupFileList(list->next);
1196 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1197 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1198 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1199 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1201 #define insert_hash_entry hashtable_insert
1202 #define search_hash_entry hashtable_search
1203 #define change_hash_entry hashtable_change
1204 #define remove_hash_entry hashtable_remove
1207 static unsigned int get_hash_from_key(void *key)
1212 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1213 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1214 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1215 it works better than many other constants, prime or not) has never been
1216 adequately explained.
1218 If you just want to have a good hash function, and cannot wait, djb2
1219 is one of the best string hash functions i know. It has excellent
1220 distribution and speed on many different sets of keys and table sizes.
1221 You are not likely to do better with one of the "well known" functions
1222 such as PJW, K&R, etc.
1224 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1227 char *str = (char *)key;
1228 unsigned int hash = 5381;
1231 while ((c = *str++))
1232 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1237 static int keys_are_equal(void *key1, void *key2)
1239 return (strcmp((char *)key1, (char *)key2) == 0);
1242 SetupFileHash *newSetupFileHash()
1244 SetupFileHash *new_hash =
1245 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1250 void freeSetupFileHash(SetupFileHash *hash)
1255 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1258 char *getHashEntry(SetupFileHash *hash, char *token)
1263 return search_hash_entry(hash, token);
1266 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1273 value_copy = getStringCopy(value);
1275 /* change value; if it does not exist, insert it as new */
1276 if (!change_hash_entry(hash, token, value_copy))
1277 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1278 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1283 static void printSetupFileHash(SetupFileHash *hash)
1285 BEGIN_HASH_ITERATION(hash, itr)
1287 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1288 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1290 END_HASH_ITERATION(hash, itr)
1295 static void *loadSetupFileData(char *filename, boolean use_hash)
1298 char line[MAX_LINE_LEN];
1299 char *token, *value, *line_ptr;
1300 void *setup_file_data;
1304 setup_file_data = newSetupFileHash();
1306 setup_file_data = newSetupFileList("", "");
1308 if (!(file = fopen(filename, MODE_READ)))
1310 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1316 /* read next line of input file */
1317 if (!fgets(line, MAX_LINE_LEN, file))
1320 /* cut trailing comment or whitespace from input line */
1321 for (line_ptr = line; *line_ptr; line_ptr++)
1323 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1330 /* cut trailing whitespaces from input line */
1331 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1332 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1335 /* ignore empty lines */
1339 line_len = strlen(line);
1341 /* cut leading whitespaces from token */
1342 for (token = line; *token; token++)
1343 if (*token != ' ' && *token != '\t')
1346 /* find end of token */
1347 for (line_ptr = token; *line_ptr; line_ptr++)
1349 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1356 if (line_ptr < line + line_len)
1357 value = line_ptr + 1;
1361 /* cut leading whitespaces from value */
1362 for (; *value; value++)
1363 if (*value != ' ' && *value != '\t')
1366 if (*token && *value)
1369 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1371 setListEntry((SetupFileList *)setup_file_data, token, value);
1379 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1380 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1384 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1385 SetupFileList *first_valid_list_entry = setup_file_list->next;
1387 /* free empty list header */
1388 setup_file_list->next = NULL;
1389 freeSetupFileList(setup_file_list);
1390 setup_file_data = first_valid_list_entry;
1392 if (first_valid_list_entry == NULL)
1393 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1396 return setup_file_data;
1399 SetupFileList *loadSetupFileList(char *filename)
1401 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1404 SetupFileHash *loadSetupFileHash(char *filename)
1406 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1409 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1412 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1415 Error(ERR_WARN, "configuration file has no file identifier");
1416 else if (!checkCookieString(value, identifier))
1417 Error(ERR_WARN, "configuration file has wrong file identifier");
1421 /* ========================================================================= */
1422 /* setup file stuff */
1423 /* ========================================================================= */
1425 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1426 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1427 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1429 /* level directory info */
1430 #define LEVELINFO_TOKEN_IDENTIFIER 0
1431 #define LEVELINFO_TOKEN_NAME 1
1432 #define LEVELINFO_TOKEN_NAME_SORTING 2
1433 #define LEVELINFO_TOKEN_AUTHOR 3
1434 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1435 #define LEVELINFO_TOKEN_LEVELS 5
1436 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1437 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1438 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1439 #define LEVELINFO_TOKEN_READONLY 9
1440 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1441 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1442 #define LEVELINFO_TOKEN_MUSIC_SET 12
1444 #define NUM_LEVELINFO_TOKENS 13
1446 static LevelDirTree ldi;
1448 static struct TokenInfo levelinfo_tokens[] =
1450 /* level directory info */
1451 { TYPE_STRING, &ldi.identifier, "identifier" },
1452 { TYPE_STRING, &ldi.name, "name" },
1453 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1454 { TYPE_STRING, &ldi.author, "author" },
1455 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1456 { TYPE_INTEGER, &ldi.levels, "levels" },
1457 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1458 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1459 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1460 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1461 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1462 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1463 { TYPE_STRING, &ldi.music_set, "music_set" }
1466 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1470 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1471 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1472 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1473 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1476 ldi->node_parent = NULL;
1477 ldi->node_group = NULL;
1481 ldi->cl_cursor = -1;
1483 ldi->filename = NULL;
1484 ldi->fullpath = NULL;
1485 ldi->basepath = NULL;
1486 ldi->identifier = NULL;
1487 ldi->name = getStringCopy(ANONYMOUS_NAME);
1488 ldi->name_sorting = NULL;
1489 ldi->author = getStringCopy(ANONYMOUS_NAME);
1491 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1492 ldi->parent_link = FALSE;
1493 ldi->user_defined = FALSE;
1495 ldi->class_desc = NULL;
1497 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1499 ldi->imported_from = NULL;
1500 ldi->graphics_set = NULL;
1501 ldi->sounds_set = NULL;
1502 ldi->music_set = NULL;
1503 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1504 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1505 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1507 ldi->first_level = 0;
1508 ldi->last_level = 0;
1509 ldi->level_group = FALSE;
1510 ldi->handicap_level = 0;
1511 ldi->readonly = TRUE;
1515 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1519 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1521 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1526 /* first copy all values from the parent structure ... */
1529 /* ... then set all fields to default that cannot be inherited from parent.
1530 This is especially important for all those fields that can be set from
1531 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1532 calls 'free()' for all already set token values which requires that no
1533 other structure's pointer may point to them!
1536 ldi->filename = NULL;
1537 ldi->fullpath = NULL;
1538 ldi->basepath = NULL;
1539 ldi->identifier = NULL;
1540 ldi->name = getStringCopy(ANONYMOUS_NAME);
1541 ldi->name_sorting = NULL;
1542 ldi->author = getStringCopy(parent->author);
1543 ldi->imported_from = getStringCopy(parent->imported_from);
1545 ldi->level_group = FALSE;
1546 ldi->parent_link = FALSE;
1548 ldi->node_top = parent->node_top;
1549 ldi->node_parent = parent;
1550 ldi->node_group = NULL;
1554 void setSetupInfo(struct TokenInfo *token_info,
1555 int token_nr, char *token_value)
1557 int token_type = token_info[token_nr].type;
1558 void *setup_value = token_info[token_nr].value;
1560 if (token_value == NULL)
1563 /* set setup field to corresponding token value */
1568 *(boolean *)setup_value = get_boolean_from_string(token_value);
1572 *(Key *)setup_value = getKeyFromKeyName(token_value);
1576 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1580 *(int *)setup_value = get_integer_from_string(token_value);
1584 if (*(char **)setup_value != NULL)
1585 free(*(char **)setup_value);
1586 *(char **)setup_value = getStringCopy(token_value);
1594 static int compareTreeInfoEntries(const void *object1, const void *object2)
1596 const TreeInfo *entry1 = *((TreeInfo **)object1);
1597 const TreeInfo *entry2 = *((TreeInfo **)object2);
1598 int class_sorting1, class_sorting2;
1601 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1603 class_sorting1 = LEVELSORTING(entry1);
1604 class_sorting2 = LEVELSORTING(entry2);
1608 class_sorting1 = ARTWORKSORTING(entry1);
1609 class_sorting2 = ARTWORKSORTING(entry2);
1612 if (entry1->parent_link || entry2->parent_link)
1613 compare_result = (entry1->parent_link ? -1 : +1);
1614 else if (entry1->sort_priority == entry2->sort_priority)
1616 char *name1 = getStringToLower(entry1->name_sorting);
1617 char *name2 = getStringToLower(entry2->name_sorting);
1619 compare_result = strcmp(name1, name2);
1624 else if (class_sorting1 == class_sorting2)
1625 compare_result = entry1->sort_priority - entry2->sort_priority;
1627 compare_result = class_sorting1 - class_sorting2;
1629 return compare_result;
1632 static void createParentTreeInfoNode(TreeInfo *node_parent)
1636 if (node_parent == NULL)
1639 ti_new = newTreeInfo();
1640 setTreeInfoToDefaults(ti_new, node_parent->type);
1642 ti_new->node_parent = node_parent;
1643 ti_new->parent_link = TRUE;
1645 ti_new->identifier = getStringCopy(node_parent->identifier);
1646 ti_new->name = ".. (parent directory)";
1647 ti_new->name_sorting = getStringCopy(ti_new->name);
1649 ti_new->filename = "..";
1650 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1652 ti_new->sort_priority = node_parent->sort_priority;
1653 ti_new->class_desc = getLevelClassDescription(ti_new);
1655 pushTreeInfo(&node_parent->node_group, ti_new);
1658 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1659 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1661 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1662 TreeInfo *node_parent,
1663 char *level_directory,
1664 char *directory_name)
1666 char *directory_path = getPath2(level_directory, directory_name);
1667 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1668 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1669 LevelDirTree *leveldir_new = NULL;
1672 if (setup_file_hash == NULL)
1674 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1676 free(directory_path);
1682 leveldir_new = newTreeInfo();
1685 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1687 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1689 leveldir_new->filename = getStringCopy(directory_name);
1691 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1693 /* set all structure fields according to the token/value pairs */
1694 ldi = *leveldir_new;
1695 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1696 setSetupInfo(levelinfo_tokens, i,
1697 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1698 *leveldir_new = ldi;
1700 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1702 free(leveldir_new->name);
1703 leveldir_new->name = getStringCopy(leveldir_new->filename);
1706 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1708 if (leveldir_new->identifier == NULL)
1709 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1711 if (leveldir_new->name_sorting == NULL)
1712 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1714 if (node_parent == NULL) /* top level group */
1716 leveldir_new->basepath = level_directory;
1717 leveldir_new->fullpath = leveldir_new->filename;
1719 else /* sub level group */
1721 leveldir_new->basepath = node_parent->basepath;
1722 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1725 if (leveldir_new->levels < 1)
1726 leveldir_new->levels = 1;
1728 leveldir_new->last_level =
1729 leveldir_new->first_level + leveldir_new->levels - 1;
1731 leveldir_new->user_defined =
1732 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1734 leveldir_new->color = LEVELCOLOR(leveldir_new);
1735 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1737 leveldir_new->handicap_level = /* set handicap to default value */
1738 (leveldir_new->user_defined ?
1739 leveldir_new->last_level :
1740 leveldir_new->first_level);
1742 pushTreeInfo(node_first, leveldir_new);
1744 freeSetupFileHash(setup_file_hash);
1746 if (leveldir_new->level_group)
1748 /* create node to link back to current level directory */
1749 createParentTreeInfoNode(leveldir_new);
1751 /* step into sub-directory and look for more level series */
1752 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1753 leveldir_new, directory_path);
1756 free(directory_path);
1762 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1763 TreeInfo *node_parent,
1764 char *level_directory)
1767 struct dirent *dir_entry;
1768 boolean valid_entry_found = FALSE;
1770 if ((dir = opendir(level_directory)) == NULL)
1772 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1776 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1778 struct stat file_status;
1779 char *directory_name = dir_entry->d_name;
1780 char *directory_path = getPath2(level_directory, directory_name);
1782 /* skip entries for current and parent directory */
1783 if (strcmp(directory_name, ".") == 0 ||
1784 strcmp(directory_name, "..") == 0)
1786 free(directory_path);
1790 /* find out if directory entry is itself a directory */
1791 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1792 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1794 free(directory_path);
1798 free(directory_path);
1800 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1801 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1802 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1805 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1812 if (!valid_entry_found)
1814 /* check if this directory directly contains a file "levelinfo.conf" */
1815 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1816 level_directory, ".");
1819 if (!valid_entry_found)
1820 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1824 void LoadLevelInfo()
1826 InitUserLevelDirectory(getLoginName());
1828 DrawInitText("Loading level series:", 120, FC_GREEN);
1830 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1831 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1833 /* before sorting, the first entries will be from the user directory */
1834 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1836 if (leveldir_first == NULL)
1837 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1839 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1842 dumpTreeInfo(leveldir_first, 0);
1846 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1847 TreeInfo *node_parent,
1848 char *base_directory,
1849 char *directory_name, int type)
1851 char *directory_path = getPath2(base_directory, directory_name);
1852 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1853 SetupFileHash *setup_file_hash = NULL;
1854 TreeInfo *artwork_new = NULL;
1857 if (access(filename, F_OK) == 0) /* file exists */
1858 setup_file_hash = loadSetupFileHash(filename);
1860 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1863 struct dirent *dir_entry;
1864 boolean valid_file_found = FALSE;
1866 if ((dir = opendir(directory_path)) != NULL)
1868 while ((dir_entry = readdir(dir)) != NULL)
1870 char *entry_name = dir_entry->d_name;
1872 if (FileIsArtworkType(entry_name, type))
1874 valid_file_found = TRUE;
1882 if (!valid_file_found)
1884 if (strcmp(directory_name, ".") != 0)
1885 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1887 free(directory_path);
1894 artwork_new = newTreeInfo();
1897 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1899 setTreeInfoToDefaults(artwork_new, type);
1901 artwork_new->filename = getStringCopy(directory_name);
1903 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1906 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1909 /* set all structure fields according to the token/value pairs */
1911 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1912 setSetupInfo(levelinfo_tokens, i,
1913 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1916 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1918 free(artwork_new->name);
1919 artwork_new->name = getStringCopy(artwork_new->filename);
1923 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1926 if (artwork_new->identifier == NULL)
1927 artwork_new->identifier = getStringCopy(artwork_new->filename);
1929 if (artwork_new->name_sorting == NULL)
1930 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1933 if (node_parent == NULL) /* top level group */
1935 artwork_new->basepath = getStringCopy(base_directory);
1936 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1938 else /* sub level group */
1940 artwork_new->basepath = getStringCopy(node_parent->basepath);
1941 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1944 artwork_new->user_defined =
1945 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1947 /* (may use ".sort_priority" from "setup_file_hash" above) */
1948 artwork_new->color = ARTWORKCOLOR(artwork_new);
1949 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1951 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
1953 if (artwork_new->name != NULL)
1954 free(artwork_new->name);
1956 if (strcmp(artwork_new->filename, ".") == 0)
1958 if (artwork_new->user_defined)
1960 artwork_new->identifier = getStringCopy("private");
1961 artwork_new->sort_priority = ARTWORKCLASS_USER;
1965 artwork_new->identifier = getStringCopy("classic");
1966 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1969 /* set to new values after changing ".sort_priority" */
1970 artwork_new->color = ARTWORKCOLOR(artwork_new);
1971 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1975 artwork_new->identifier = getStringCopy(artwork_new->filename);
1978 artwork_new->name = getStringCopy(artwork_new->identifier);
1979 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1982 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1984 pushTreeInfo(node_first, artwork_new);
1986 freeSetupFileHash(setup_file_hash);
1988 free(directory_path);
1994 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1995 TreeInfo *node_parent,
1996 char *base_directory, int type)
1999 struct dirent *dir_entry;
2000 boolean valid_entry_found = FALSE;
2002 if ((dir = opendir(base_directory)) == NULL)
2004 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2005 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2009 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2011 struct stat file_status;
2012 char *directory_name = dir_entry->d_name;
2013 char *directory_path = getPath2(base_directory, directory_name);
2015 /* skip entries for current and parent directory */
2016 if (strcmp(directory_name, ".") == 0 ||
2017 strcmp(directory_name, "..") == 0)
2019 free(directory_path);
2023 /* find out if directory entry is itself a directory */
2024 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2025 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2027 free(directory_path);
2031 free(directory_path);
2033 /* check if this directory contains artwork with or without config file */
2034 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2036 directory_name, type);
2041 /* check if this directory directly contains artwork itself */
2042 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2043 base_directory, ".",
2045 if (!valid_entry_found)
2046 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2050 static TreeInfo *getDummyArtworkInfo(int type)
2052 /* this is only needed when there is completely no artwork available */
2053 TreeInfo *artwork_new = newTreeInfo();
2055 setTreeInfoToDefaults(artwork_new, type);
2057 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2058 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2059 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2061 if (artwork_new->name != NULL)
2062 free(artwork_new->name);
2064 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2065 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2066 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2071 void LoadArtworkInfo()
2073 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2075 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2076 options.graphics_directory,
2077 TREE_TYPE_GRAPHICS_DIR);
2078 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2079 getUserGraphicsDir(),
2080 TREE_TYPE_GRAPHICS_DIR);
2082 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2083 options.sounds_directory,
2084 TREE_TYPE_SOUNDS_DIR);
2085 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2087 TREE_TYPE_SOUNDS_DIR);
2089 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2090 options.music_directory,
2091 TREE_TYPE_MUSIC_DIR);
2092 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2094 TREE_TYPE_MUSIC_DIR);
2096 if (artwork.gfx_first == NULL)
2097 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2098 if (artwork.snd_first == NULL)
2099 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2100 if (artwork.mus_first == NULL)
2101 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2103 /* before sorting, the first entries will be from the user directory */
2104 artwork.gfx_current =
2105 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2106 if (artwork.gfx_current == NULL)
2107 artwork.gfx_current =
2108 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2109 if (artwork.gfx_current == NULL)
2110 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2112 artwork.snd_current =
2113 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2114 if (artwork.snd_current == NULL)
2115 artwork.snd_current =
2116 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2117 if (artwork.snd_current == NULL)
2118 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2120 artwork.mus_current =
2121 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2122 if (artwork.mus_current == NULL)
2123 artwork.mus_current =
2124 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2125 if (artwork.mus_current == NULL)
2126 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2128 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2129 artwork.snd_current_identifier = artwork.snd_current->identifier;
2130 artwork.mus_current_identifier = artwork.mus_current->identifier;
2133 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2134 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2135 printf("music set == %s\n\n", artwork.mus_current_identifier);
2138 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2139 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2140 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2143 dumpTreeInfo(artwork.gfx_first, 0);
2144 dumpTreeInfo(artwork.snd_first, 0);
2145 dumpTreeInfo(artwork.mus_first, 0);
2149 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2150 LevelDirTree *level_node)
2152 /* recursively check all level directories for artwork sub-directories */
2156 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2157 ARTWORK_DIRECTORY((*artwork_node)->type));
2160 if (!level_node->parent_link)
2161 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2162 level_node->filename, level_node->name);
2165 if (!level_node->parent_link)
2167 TreeInfo *topnode_last = *artwork_node;
2169 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2170 (*artwork_node)->type);
2172 if (topnode_last != *artwork_node)
2174 free((*artwork_node)->identifier);
2175 free((*artwork_node)->name);
2176 free((*artwork_node)->name_sorting);
2178 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2179 (*artwork_node)->name = getStringCopy(level_node->name);
2180 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2182 (*artwork_node)->sort_priority = level_node->sort_priority;
2183 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2189 if (level_node->node_group != NULL)
2190 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2192 level_node = level_node->next;
2196 void LoadLevelArtworkInfo()
2198 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2200 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2201 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2202 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2204 /* needed for reloading level artwork not known at ealier stage */
2205 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2207 artwork.gfx_current =
2208 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2209 if (artwork.gfx_current == NULL)
2210 artwork.gfx_current =
2211 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2212 if (artwork.gfx_current == NULL)
2213 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2216 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2218 artwork.snd_current =
2219 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2220 if (artwork.snd_current == NULL)
2221 artwork.snd_current =
2222 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2223 if (artwork.snd_current == NULL)
2224 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2227 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2229 artwork.mus_current =
2230 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2231 if (artwork.mus_current == NULL)
2232 artwork.mus_current =
2233 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2234 if (artwork.mus_current == NULL)
2235 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2238 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2239 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2240 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2243 dumpTreeInfo(artwork.gfx_first, 0);
2244 dumpTreeInfo(artwork.snd_first, 0);
2245 dumpTreeInfo(artwork.mus_first, 0);
2249 static void SaveUserLevelInfo()
2255 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2257 if (!(file = fopen(filename, MODE_WRITE)))
2259 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2264 /* always start with reliable default values */
2265 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2267 ldi.name = getStringCopy(getLoginName());
2268 ldi.author = getStringCopy(getRealName());
2270 ldi.first_level = 1;
2271 ldi.sort_priority = LEVELCLASS_USER_START;
2272 ldi.readonly = FALSE;
2273 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2274 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2275 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2277 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2278 getCookie("LEVELINFO")));
2280 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2281 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2282 i != LEVELINFO_TOKEN_NAME_SORTING &&
2283 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2284 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2289 SetFilePermissions(filename, PERMS_PRIVATE);
2292 char *getSetupValue(int type, void *value)
2294 static char value_string[MAX_LINE_LEN];
2302 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2306 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2310 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2314 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2318 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2322 sprintf(value_string, "%d", *(int *)value);
2326 strcpy(value_string, *(char **)value);
2330 value_string[0] = '\0';
2334 return value_string;
2337 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2341 static char token_string[MAX_LINE_LEN];
2342 int token_type = token_info[token_nr].type;
2343 void *setup_value = token_info[token_nr].value;
2344 char *token_text = token_info[token_nr].text;
2345 char *value_string = getSetupValue(token_type, setup_value);
2347 /* build complete token string */
2348 sprintf(token_string, "%s%s", prefix, token_text);
2350 /* build setup entry line */
2351 line = getFormattedSetupEntry(token_string, value_string);
2353 if (token_type == TYPE_KEY_X11)
2355 Key key = *(Key *)setup_value;
2356 char *keyname = getKeyNameFromKey(key);
2358 /* add comment, if useful */
2359 if (strcmp(keyname, "(undefined)") != 0 &&
2360 strcmp(keyname, "(unknown)") != 0)
2362 /* add at least one whitespace */
2364 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2368 strcat(line, keyname);
2375 void LoadLevelSetup_LastSeries()
2378 SetupFileHash *level_setup_hash = NULL;
2380 /* always start with reliable default values */
2381 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2383 /* ----------------------------------------------------------------------- */
2384 /* ~/.<program>/levelsetup.conf */
2385 /* ----------------------------------------------------------------------- */
2387 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2389 if ((level_setup_hash = loadSetupFileHash(filename)))
2391 char *last_level_series =
2392 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2394 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2396 if (leveldir_current == NULL)
2397 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2399 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2401 freeSetupFileHash(level_setup_hash);
2404 Error(ERR_WARN, "using default setup values");
2409 void SaveLevelSetup_LastSeries()
2412 char *level_subdir = leveldir_current->filename;
2415 /* ----------------------------------------------------------------------- */
2416 /* ~/.<program>/levelsetup.conf */
2417 /* ----------------------------------------------------------------------- */
2419 InitUserDataDirectory();
2421 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2423 if (!(file = fopen(filename, MODE_WRITE)))
2425 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2430 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2431 getCookie("LEVELSETUP")));
2432 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2438 SetFilePermissions(filename, PERMS_PRIVATE);
2441 static void checkSeriesInfo()
2443 static char *level_directory = NULL;
2445 struct dirent *dir_entry;
2447 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2449 level_directory = getPath2((leveldir_current->user_defined ?
2450 getUserLevelDir(NULL) :
2451 options.level_directory),
2452 leveldir_current->fullpath);
2454 if ((dir = opendir(level_directory)) == NULL)
2456 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2460 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2462 if (strlen(dir_entry->d_name) > 4 &&
2463 dir_entry->d_name[3] == '.' &&
2464 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2466 char levelnum_str[4];
2469 strncpy(levelnum_str, dir_entry->d_name, 3);
2470 levelnum_str[3] = '\0';
2472 levelnum_value = atoi(levelnum_str);
2475 if (levelnum_value < leveldir_current->first_level)
2477 Error(ERR_WARN, "additional level %d found", levelnum_value);
2478 leveldir_current->first_level = levelnum_value;
2480 else if (levelnum_value > leveldir_current->last_level)
2482 Error(ERR_WARN, "additional level %d found", levelnum_value);
2483 leveldir_current->last_level = levelnum_value;
2492 void LoadLevelSetup_SeriesInfo()
2495 SetupFileHash *level_setup_hash = NULL;
2496 char *level_subdir = leveldir_current->filename;
2498 /* always start with reliable default values */
2499 level_nr = leveldir_current->first_level;
2501 checkSeriesInfo(leveldir_current);
2503 /* ----------------------------------------------------------------------- */
2504 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2505 /* ----------------------------------------------------------------------- */
2507 level_subdir = leveldir_current->filename;
2509 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2511 if ((level_setup_hash = loadSetupFileHash(filename)))
2515 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2519 level_nr = atoi(token_value);
2521 if (level_nr < leveldir_current->first_level)
2522 level_nr = leveldir_current->first_level;
2523 if (level_nr > leveldir_current->last_level)
2524 level_nr = leveldir_current->last_level;
2527 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2531 int level_nr = atoi(token_value);
2533 if (level_nr < leveldir_current->first_level)
2534 level_nr = leveldir_current->first_level;
2535 if (level_nr > leveldir_current->last_level + 1)
2536 level_nr = leveldir_current->last_level;
2538 if (leveldir_current->user_defined)
2539 level_nr = leveldir_current->last_level;
2541 leveldir_current->handicap_level = level_nr;
2544 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2546 freeSetupFileHash(level_setup_hash);
2549 Error(ERR_WARN, "using default setup values");
2554 void SaveLevelSetup_SeriesInfo()
2557 char *level_subdir = leveldir_current->filename;
2558 char *level_nr_str = int2str(level_nr, 0);
2559 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2562 /* ----------------------------------------------------------------------- */
2563 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2564 /* ----------------------------------------------------------------------- */
2566 InitLevelSetupDirectory(level_subdir);
2568 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2570 if (!(file = fopen(filename, MODE_WRITE)))
2572 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2577 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2578 getCookie("LEVELSETUP")));
2579 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2581 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2582 handicap_level_str));
2587 SetFilePermissions(filename, PERMS_PRIVATE);