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) == TREE_TYPE_GRAPHICS_DIR ? \
102 GRAPHICSINFO_FILENAME : \
103 (type) == TREE_TYPE_SOUNDS_DIR ? \
104 SOUNDSINFO_FILENAME : \
105 (type) == TREE_TYPE_MUSIC_DIR ? \
106 MUSICINFO_FILENAME : "")
108 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
109 GRAPHICS_DIRECTORY : \
110 (type) == TREE_TYPE_SOUNDS_DIR ? \
112 (type) == TREE_TYPE_MUSIC_DIR ? \
113 MUSIC_DIRECTORY : "")
115 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
116 options.graphics_directory : \
117 (type) == TREE_TYPE_SOUNDS_DIR ? \
118 options.sounds_directory : \
119 (type) == TREE_TYPE_MUSIC_DIR ? \
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 *getDefaultArtworkDir(int type)
273 return (type == TREE_TYPE_GRAPHICS_DIR ?
274 getDefaultGraphicsDir(GRAPHICS_SUBDIR) :
275 type == TREE_TYPE_SOUNDS_DIR ?
276 getDefaultSoundsDir(SOUNDS_SUBDIR) :
277 type == TREE_TYPE_MUSIC_DIR ?
278 getDefaultMusicDir(MUSIC_SUBDIR) : "");
281 static char *getUserGraphicsDir()
283 static char *usergraphics_dir = NULL;
285 if (usergraphics_dir == NULL)
286 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
288 return usergraphics_dir;
291 static char *getUserSoundsDir()
293 static char *usersounds_dir = NULL;
295 if (usersounds_dir == NULL)
296 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
298 return usersounds_dir;
301 static char *getUserMusicDir()
303 static char *usermusic_dir = NULL;
305 if (usermusic_dir == NULL)
306 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
308 return usermusic_dir;
311 static char *getSetupArtworkDir(TreeInfo *ti)
313 static char *artwork_dir = NULL;
315 if (artwork_dir != NULL)
318 artwork_dir = getPath2(ti->basepath, ti->fullpath);
323 void setLevelArtworkDir(TreeInfo *ti)
325 char **artwork_path_ptr, *artwork_set;
326 TreeInfo *level_artwork;
328 if (ti == NULL || leveldir_current == NULL)
332 (ti->type == TREE_TYPE_GRAPHICS_DIR ? &leveldir_current->graphics_path :
333 ti->type == TREE_TYPE_SOUNDS_DIR ? &leveldir_current->sounds_path :
334 &leveldir_current->music_path);
337 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_set :
338 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_set :
339 leveldir_current->music_set);
341 if (*artwork_path_ptr != NULL)
342 free(*artwork_path_ptr);
344 if ((level_artwork = getTreeInfoFromIdentifier(ti, artwork_set)))
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));
358 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
360 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
366 static char *getLevelArtworkDir(int type)
370 if (leveldir_current == NULL)
371 return UNDEFINED_FILENAME;
374 (type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_path :
375 type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_path :
376 type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_path :
382 char *getLevelFilename(int nr)
384 static char *filename = NULL;
385 char basename[MAX_FILENAME_LEN];
387 if (filename != NULL)
391 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
393 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
395 filename = getPath2(getCurrentLevelDir(), basename);
400 char *getTapeFilename(int nr)
402 static char *filename = NULL;
403 char basename[MAX_FILENAME_LEN];
405 if (filename != NULL)
408 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
409 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
414 char *getScoreFilename(int nr)
416 static char *filename = NULL;
417 char basename[MAX_FILENAME_LEN];
419 if (filename != NULL)
422 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
423 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
428 char *getSetupFilename()
430 static char *filename = NULL;
432 if (filename != NULL)
435 filename = getPath2(getSetupDir(), SETUP_FILENAME);
440 static char *getCorrectedImageBasename(char *basename)
442 char *basename_corrected = basename;
444 #if defined(PLATFORM_MSDOS)
445 if (program.filename_prefix != NULL)
447 int prefix_len = strlen(program.filename_prefix);
449 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
450 basename_corrected = &basename[prefix_len];
452 /* if corrected filename is still longer than standard MS-DOS filename
453 size (8 characters + 1 dot + 3 characters file extension), shorten
454 filename by writing file extension after 8th basename character */
455 if (strlen(basename_corrected) > 8+1+3)
457 static char *msdos_filename = NULL;
459 if (msdos_filename != NULL)
460 free(msdos_filename);
462 msdos_filename = getStringCopy(basename_corrected);
463 strncpy(&msdos_filename[8], &basename[strlen(basename) - 1+3], 1+3 + 1);
468 return basename_corrected;
471 char *getCustomImageFilename(char *basename)
473 static char *filename = NULL;
475 if (filename != NULL)
478 basename = getCorrectedImageBasename(basename);
480 if (!setup.override_level_graphics)
483 /* 1st try: look for special artwork configured in level series config */
484 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
486 if (strcmp(basename, "RocksScreen.pcx") == 0)
487 printf("::: trying 1 '%s' ...\n", filename);
489 if (fileExists(filename))
495 /* 2nd try: look for special artwork in current level series directory */
496 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
498 if (strcmp(basename, "RocksScreen.pcx") == 0)
499 printf("::: trying 2 '%s' ...\n", filename);
501 if (fileExists(filename))
507 /* 1st try: look for special artwork configured in level series config */
508 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
510 if (strcmp(basename, "RocksScreen.pcx") == 0)
511 printf("::: trying 2.1 '%s' ...\n", filename);
513 if (fileExists(filename))
520 /* 3rd try: look for special artwork in configured artwork directory */
521 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
523 if (strcmp(basename, "RocksScreen.pcx") == 0)
524 printf("::: trying 3 '%s' ...\n", filename);
526 if (fileExists(filename))
531 /* 4th try: look for default artwork in new default artwork directory */
532 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
534 if (strcmp(basename, "RocksScreen.pcx") == 0)
535 printf("::: trying 4 '%s' ...\n", filename);
537 if (fileExists(filename))
542 /* 5th try: look for default artwork in old default artwork directory */
543 filename = getPath2(options.graphics_directory, basename);
545 if (strcmp(basename, "RocksScreen.pcx") == 0)
546 printf("::: trying 5 '%s' ...\n", filename);
548 if (fileExists(filename))
551 return NULL; /* cannot find specified artwork file anywhere */
554 char *getCustomSoundFilename(char *basename)
556 static char *filename = NULL;
558 if (filename != NULL)
561 if (!setup.override_level_sounds)
564 /* 1st try: look for special artwork configured in level series config */
565 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
566 if (fileExists(filename))
572 /* 2nd try: look for special artwork in current level series directory */
573 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
574 if (fileExists(filename))
580 /* 1st try: look for special artwork configured in level series config */
581 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
582 if (fileExists(filename))
589 /* 3rd try: look for special artwork in configured artwork directory */
590 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
591 if (fileExists(filename))
596 /* 4th try: look for default artwork in new default artwork directory */
597 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
598 if (fileExists(filename))
603 /* 5th try: look for default artwork in old default artwork directory */
604 filename = getPath2(options.sounds_directory, basename);
605 if (fileExists(filename))
608 return NULL; /* cannot find specified artwork file anywhere */
611 char *getCustomArtworkFilename(char *basename, int type)
613 if (type == ARTWORK_TYPE_GRAPHICS)
614 return getCustomImageFilename(basename);
615 else if (type == ARTWORK_TYPE_SOUNDS)
616 return getCustomSoundFilename(basename);
618 return UNDEFINED_FILENAME;
621 char *getCustomArtworkConfigFilename(int type)
623 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
626 char *getCustomArtworkLevelConfigFilename(int type)
628 static char *filename = NULL;
630 if (filename != NULL)
633 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
638 char *getCustomMusicDirectory(void)
640 static char *directory = NULL;
642 if (directory != NULL)
645 if (!setup.override_level_music)
648 /* 1st try: look for special artwork configured in level series config */
649 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
650 if (fileExists(directory))
656 /* 2nd try: look for special artwork in current level series directory */
657 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
658 if (fileExists(directory))
664 /* 1st try: look for special artwork configured in level series config */
665 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
666 if (fileExists(directory))
673 /* 3rd try: look for special artwork in configured artwork directory */
674 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
675 if (fileExists(directory))
680 /* 4th try: look for default artwork in new default artwork directory */
681 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
682 if (fileExists(directory))
687 /* 5th try: look for default artwork in old default artwork directory */
688 directory = getStringCopy(options.music_directory);
689 if (fileExists(directory))
692 return NULL; /* cannot find specified artwork file anywhere */
695 void InitTapeDirectory(char *level_subdir)
697 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
698 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
699 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
702 void InitScoreDirectory(char *level_subdir)
704 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
705 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
706 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
709 static void SaveUserLevelInfo();
711 void InitUserLevelDirectory(char *level_subdir)
713 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
715 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
716 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
717 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
723 void InitLevelSetupDirectory(char *level_subdir)
725 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
726 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
727 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
731 /* ------------------------------------------------------------------------- */
732 /* some functions to handle lists of level directories */
733 /* ------------------------------------------------------------------------- */
735 TreeInfo *newTreeInfo()
737 return checked_calloc(sizeof(TreeInfo));
740 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
742 node_new->next = *node_first;
743 *node_first = node_new;
746 int numTreeInfo(TreeInfo *node)
759 boolean validLevelSeries(TreeInfo *node)
761 return (node != NULL && !node->node_group && !node->parent_link);
764 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
769 if (node->node_group) /* enter level group (step down into tree) */
770 return getFirstValidTreeInfoEntry(node->node_group);
771 else if (node->parent_link) /* skip start entry of level group */
773 if (node->next) /* get first real level series entry */
774 return getFirstValidTreeInfoEntry(node->next);
775 else /* leave empty level group and go on */
776 return getFirstValidTreeInfoEntry(node->node_parent->next);
778 else /* this seems to be a regular level series */
782 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
787 if (node->node_parent == NULL) /* top level group */
788 return *node->node_top;
789 else /* sub level group */
790 return node->node_parent->node_group;
793 int numTreeInfoInGroup(TreeInfo *node)
795 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
798 int posTreeInfo(TreeInfo *node)
800 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
805 if (node_cmp == node)
809 node_cmp = node_cmp->next;
815 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
817 TreeInfo *node_default = node;
832 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
834 if (identifier == NULL)
839 if (node->node_group)
841 TreeInfo *node_group;
843 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
848 else if (!node->parent_link)
850 if (strcmp(identifier, node->identifier) == 0)
860 void dumpTreeInfo(TreeInfo *node, int depth)
864 printf("Dumping TreeInfo:\n");
868 for (i=0; i<(depth + 1) * 3; i++)
871 printf("filename == '%s' (%s) [%s] (%d)\n",
872 node->filename, node->name, node->identifier, node->sort_priority);
874 if (node->node_group != NULL)
875 dumpTreeInfo(node->node_group, depth + 1);
881 void sortTreeInfo(TreeInfo **node_first,
882 int (*compare_function)(const void *, const void *))
884 int num_nodes = numTreeInfo(*node_first);
885 TreeInfo **sort_array;
886 TreeInfo *node = *node_first;
892 /* allocate array for sorting structure pointers */
893 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
895 /* writing structure pointers to sorting array */
896 while (i < num_nodes && node) /* double boundary check... */
898 sort_array[i] = node;
904 /* sorting the structure pointers in the sorting array */
905 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
908 /* update the linkage of list elements with the sorted node array */
909 for (i=0; i<num_nodes - 1; i++)
910 sort_array[i]->next = sort_array[i + 1];
911 sort_array[num_nodes - 1]->next = NULL;
913 /* update the linkage of the main list anchor pointer */
914 *node_first = sort_array[0];
918 /* now recursively sort the level group structures */
922 if (node->node_group != NULL)
923 sortTreeInfo(&node->node_group, compare_function);
930 /* ========================================================================= */
931 /* some stuff from "files.c" */
932 /* ========================================================================= */
934 #if defined(PLATFORM_WIN32)
936 #define S_IRGRP S_IRUSR
939 #define S_IROTH S_IRUSR
942 #define S_IWGRP S_IWUSR
945 #define S_IWOTH S_IWUSR
948 #define S_IXGRP S_IXUSR
951 #define S_IXOTH S_IXUSR
954 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
959 #endif /* PLATFORM_WIN32 */
961 /* file permissions for newly written files */
962 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
963 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
964 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
966 #define MODE_W_PRIVATE (S_IWUSR)
967 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
968 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
970 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
971 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
973 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
974 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
976 char *getUserDataDir(void)
978 static char *userdata_dir = NULL;
980 if (userdata_dir == NULL)
981 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
986 char *getCommonDataDir(void)
988 static char *common_data_dir = NULL;
990 #if defined(PLATFORM_WIN32)
991 if (common_data_dir == NULL)
993 char *dir = checked_malloc(MAX_PATH + 1);
995 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
996 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
997 common_data_dir = getPath2(dir, program.userdata_directory);
999 common_data_dir = options.rw_base_directory;
1002 if (common_data_dir == NULL)
1003 common_data_dir = options.rw_base_directory;
1006 return common_data_dir;
1011 return getUserDataDir();
1014 static mode_t posix_umask(mode_t mask)
1016 #if defined(PLATFORM_UNIX)
1023 static int posix_mkdir(const char *pathname, mode_t mode)
1025 #if defined(PLATFORM_WIN32)
1026 return mkdir(pathname);
1028 return mkdir(pathname, mode);
1032 void createDirectory(char *dir, char *text, int permission_class)
1034 /* leave "other" permissions in umask untouched, but ensure group parts
1035 of USERDATA_DIR_MODE are not masked */
1036 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1037 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1038 mode_t normal_umask = posix_umask(0);
1039 mode_t group_umask = ~(dir_mode & S_IRWXG);
1040 posix_umask(normal_umask & group_umask);
1042 if (access(dir, F_OK) != 0)
1043 if (posix_mkdir(dir, dir_mode) != 0)
1044 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1046 posix_umask(normal_umask); /* reset normal umask */
1049 void InitUserDataDirectory()
1051 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1054 void SetFilePermissions(char *filename, int permission_class)
1056 chmod(filename, (permission_class == PERMS_PRIVATE ?
1057 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1060 char *getCookie(char *file_type)
1062 static char cookie[MAX_COOKIE_LEN + 1];
1064 if (strlen(program.cookie_prefix) + 1 +
1065 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1066 return "[COOKIE ERROR]"; /* should never happen */
1068 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1069 program.cookie_prefix, file_type,
1070 program.version_major, program.version_minor);
1075 int getFileVersionFromCookieString(const char *cookie)
1077 const char *ptr_cookie1, *ptr_cookie2;
1078 const char *pattern1 = "_FILE_VERSION_";
1079 const char *pattern2 = "?.?";
1080 const int len_cookie = strlen(cookie);
1081 const int len_pattern1 = strlen(pattern1);
1082 const int len_pattern2 = strlen(pattern2);
1083 const int len_pattern = len_pattern1 + len_pattern2;
1084 int version_major, version_minor;
1086 if (len_cookie <= len_pattern)
1089 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1090 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1092 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1095 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1096 ptr_cookie2[1] != '.' ||
1097 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1100 version_major = ptr_cookie2[0] - '0';
1101 version_minor = ptr_cookie2[2] - '0';
1103 return VERSION_IDENT(version_major, version_minor, 0);
1106 boolean checkCookieString(const char *cookie, const char *template)
1108 const char *pattern = "_FILE_VERSION_?.?";
1109 const int len_cookie = strlen(cookie);
1110 const int len_template = strlen(template);
1111 const int len_pattern = strlen(pattern);
1113 if (len_cookie != len_template)
1116 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1122 /* ------------------------------------------------------------------------- */
1123 /* setup file list and hash handling functions */
1124 /* ------------------------------------------------------------------------- */
1126 char *getFormattedSetupEntry(char *token, char *value)
1129 static char entry[MAX_LINE_LEN];
1131 /* start with the token and some spaces to format output line */
1132 sprintf(entry, "%s:", token);
1133 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1136 /* continue with the token's value */
1137 strcat(entry, value);
1142 SetupFileList *newSetupFileList(char *token, char *value)
1144 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1146 new->token = getStringCopy(token);
1147 new->value = getStringCopy(value);
1154 void freeSetupFileList(SetupFileList *list)
1164 freeSetupFileList(list->next);
1168 char *getListEntry(SetupFileList *list, char *token)
1173 if (strcmp(list->token, token) == 0)
1176 return getListEntry(list->next, token);
1179 void setListEntry(SetupFileList *list, char *token, char *value)
1184 if (strcmp(list->token, token) == 0)
1189 list->value = getStringCopy(value);
1191 else if (list->next == NULL)
1192 list->next = newSetupFileList(token, value);
1194 setListEntry(list->next, token, value);
1198 static void printSetupFileList(SetupFileList *list)
1203 printf("token: '%s'\n", list->token);
1204 printf("value: '%s'\n", list->value);
1206 printSetupFileList(list->next);
1211 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1212 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1213 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1214 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1216 #define insert_hash_entry hashtable_insert
1217 #define search_hash_entry hashtable_search
1218 #define change_hash_entry hashtable_change
1219 #define remove_hash_entry hashtable_remove
1222 static unsigned int get_hash_from_key(void *key)
1227 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1228 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1229 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1230 it works better than many other constants, prime or not) has never been
1231 adequately explained.
1233 If you just want to have a good hash function, and cannot wait, djb2
1234 is one of the best string hash functions i know. It has excellent
1235 distribution and speed on many different sets of keys and table sizes.
1236 You are not likely to do better with one of the "well known" functions
1237 such as PJW, K&R, etc.
1239 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1242 char *str = (char *)key;
1243 unsigned int hash = 5381;
1246 while ((c = *str++))
1247 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1252 static int keys_are_equal(void *key1, void *key2)
1254 return (strcmp((char *)key1, (char *)key2) == 0);
1257 SetupFileHash *newSetupFileHash()
1259 SetupFileHash *new_hash =
1260 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1265 void freeSetupFileHash(SetupFileHash *hash)
1270 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1273 char *getHashEntry(SetupFileHash *hash, char *token)
1278 return search_hash_entry(hash, token);
1281 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1288 value_copy = getStringCopy(value);
1290 /* change value; if it does not exist, insert it as new */
1291 if (!change_hash_entry(hash, token, value_copy))
1292 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1293 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1298 static void printSetupFileHash(SetupFileHash *hash)
1300 BEGIN_HASH_ITERATION(hash, itr)
1302 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1303 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1305 END_HASH_ITERATION(hash, itr)
1310 static void *loadSetupFileData(char *filename, boolean use_hash)
1313 char line[MAX_LINE_LEN];
1314 char *token, *value, *line_ptr;
1315 void *setup_file_data;
1319 setup_file_data = newSetupFileHash();
1321 setup_file_data = newSetupFileList("", "");
1323 if (!(file = fopen(filename, MODE_READ)))
1325 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1331 /* read next line of input file */
1332 if (!fgets(line, MAX_LINE_LEN, file))
1335 /* cut trailing comment or whitespace from input line */
1336 for (line_ptr = line; *line_ptr; line_ptr++)
1338 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1345 /* cut trailing whitespaces from input line */
1346 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1347 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1350 /* ignore empty lines */
1354 line_len = strlen(line);
1356 /* cut leading whitespaces from token */
1357 for (token = line; *token; token++)
1358 if (*token != ' ' && *token != '\t')
1361 /* find end of token */
1362 for (line_ptr = token; *line_ptr; line_ptr++)
1364 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1371 if (line_ptr < line + line_len)
1372 value = line_ptr + 1;
1376 /* cut leading whitespaces from value */
1377 for (; *value; value++)
1378 if (*value != ' ' && *value != '\t')
1381 if (*token && *value)
1384 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1386 setListEntry((SetupFileList *)setup_file_data, token, value);
1394 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1395 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1399 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1400 SetupFileList *first_valid_list_entry = setup_file_list->next;
1402 /* free empty list header */
1403 setup_file_list->next = NULL;
1404 freeSetupFileList(setup_file_list);
1405 setup_file_data = first_valid_list_entry;
1407 if (first_valid_list_entry == NULL)
1408 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1411 return setup_file_data;
1414 SetupFileList *loadSetupFileList(char *filename)
1416 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1419 SetupFileHash *loadSetupFileHash(char *filename)
1421 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1424 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1427 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1430 Error(ERR_WARN, "configuration file has no file identifier");
1431 else if (!checkCookieString(value, identifier))
1432 Error(ERR_WARN, "configuration file has wrong file identifier");
1436 /* ========================================================================= */
1437 /* setup file stuff */
1438 /* ========================================================================= */
1440 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1441 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1442 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1444 /* level directory info */
1445 #define LEVELINFO_TOKEN_IDENTIFIER 0
1446 #define LEVELINFO_TOKEN_NAME 1
1447 #define LEVELINFO_TOKEN_NAME_SORTING 2
1448 #define LEVELINFO_TOKEN_AUTHOR 3
1449 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1450 #define LEVELINFO_TOKEN_LEVELS 5
1451 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1452 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1453 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1454 #define LEVELINFO_TOKEN_READONLY 9
1455 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1456 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1457 #define LEVELINFO_TOKEN_MUSIC_SET 12
1459 #define NUM_LEVELINFO_TOKENS 13
1461 static LevelDirTree ldi;
1463 static struct TokenInfo levelinfo_tokens[] =
1465 /* level directory info */
1466 { TYPE_STRING, &ldi.identifier, "identifier" },
1467 { TYPE_STRING, &ldi.name, "name" },
1468 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1469 { TYPE_STRING, &ldi.author, "author" },
1470 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1471 { TYPE_INTEGER, &ldi.levels, "levels" },
1472 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1473 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1474 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1475 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1476 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1477 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1478 { TYPE_STRING, &ldi.music_set, "music_set" }
1481 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1485 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1486 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1487 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1488 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1491 ldi->node_parent = NULL;
1492 ldi->node_group = NULL;
1496 ldi->cl_cursor = -1;
1498 ldi->filename = NULL;
1499 ldi->fullpath = NULL;
1500 ldi->basepath = NULL;
1501 ldi->identifier = NULL;
1502 ldi->name = getStringCopy(ANONYMOUS_NAME);
1503 ldi->name_sorting = NULL;
1504 ldi->author = getStringCopy(ANONYMOUS_NAME);
1506 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1507 ldi->parent_link = FALSE;
1508 ldi->user_defined = FALSE;
1510 ldi->class_desc = NULL;
1512 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1514 ldi->imported_from = NULL;
1515 ldi->graphics_set = NULL;
1516 ldi->sounds_set = NULL;
1517 ldi->music_set = NULL;
1518 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1519 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1520 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1522 ldi->first_level = 0;
1523 ldi->last_level = 0;
1524 ldi->level_group = FALSE;
1525 ldi->handicap_level = 0;
1526 ldi->readonly = TRUE;
1530 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1534 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1536 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1540 /* first copy all values from the parent structure ... */
1543 /* ... then set all fields to default that cannot be inherited from parent.
1544 This is especially important for all those fields that can be set from
1545 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1546 calls 'free()' for all already set token values which requires that no
1547 other structure's pointer may point to them!
1550 ldi->filename = NULL;
1551 ldi->fullpath = NULL;
1552 ldi->basepath = NULL;
1553 ldi->identifier = NULL;
1554 ldi->name = getStringCopy(ANONYMOUS_NAME);
1555 ldi->name_sorting = NULL;
1556 ldi->author = getStringCopy(parent->author);
1557 ldi->imported_from = getStringCopy(parent->imported_from);
1559 ldi->level_group = FALSE;
1560 ldi->parent_link = FALSE;
1562 ldi->node_top = parent->node_top;
1563 ldi->node_parent = parent;
1564 ldi->node_group = NULL;
1568 void setSetupInfo(struct TokenInfo *token_info,
1569 int token_nr, char *token_value)
1571 int token_type = token_info[token_nr].type;
1572 void *setup_value = token_info[token_nr].value;
1574 if (token_value == NULL)
1577 /* set setup field to corresponding token value */
1582 *(boolean *)setup_value = get_boolean_from_string(token_value);
1586 *(Key *)setup_value = getKeyFromKeyName(token_value);
1590 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1594 *(int *)setup_value = get_integer_from_string(token_value);
1598 if (*(char **)setup_value != NULL)
1599 free(*(char **)setup_value);
1600 *(char **)setup_value = getStringCopy(token_value);
1608 static int compareTreeInfoEntries(const void *object1, const void *object2)
1610 const TreeInfo *entry1 = *((TreeInfo **)object1);
1611 const TreeInfo *entry2 = *((TreeInfo **)object2);
1612 int class_sorting1, class_sorting2;
1615 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1617 class_sorting1 = LEVELSORTING(entry1);
1618 class_sorting2 = LEVELSORTING(entry2);
1622 class_sorting1 = ARTWORKSORTING(entry1);
1623 class_sorting2 = ARTWORKSORTING(entry2);
1626 if (entry1->parent_link || entry2->parent_link)
1627 compare_result = (entry1->parent_link ? -1 : +1);
1628 else if (entry1->sort_priority == entry2->sort_priority)
1630 char *name1 = getStringToLower(entry1->name_sorting);
1631 char *name2 = getStringToLower(entry2->name_sorting);
1633 compare_result = strcmp(name1, name2);
1638 else if (class_sorting1 == class_sorting2)
1639 compare_result = entry1->sort_priority - entry2->sort_priority;
1641 compare_result = class_sorting1 - class_sorting2;
1643 return compare_result;
1646 static void createParentTreeInfoNode(TreeInfo *node_parent)
1650 if (node_parent == NULL)
1653 ti_new = newTreeInfo();
1654 setTreeInfoToDefaults(ti_new, node_parent->type);
1656 ti_new->node_parent = node_parent;
1657 ti_new->parent_link = TRUE;
1659 ti_new->identifier = getStringCopy(node_parent->identifier);
1660 ti_new->name = ".. (parent directory)";
1661 ti_new->name_sorting = getStringCopy(ti_new->name);
1663 ti_new->filename = "..";
1664 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1666 ti_new->sort_priority = node_parent->sort_priority;
1667 ti_new->class_desc = getLevelClassDescription(ti_new);
1669 pushTreeInfo(&node_parent->node_group, ti_new);
1672 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1673 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1675 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1676 TreeInfo *node_parent,
1677 char *level_directory,
1678 char *directory_name)
1680 char *directory_path = getPath2(level_directory, directory_name);
1681 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1682 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1683 LevelDirTree *leveldir_new = NULL;
1686 if (setup_file_hash == NULL)
1688 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1690 free(directory_path);
1696 leveldir_new = newTreeInfo();
1699 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1701 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1703 leveldir_new->filename = getStringCopy(directory_name);
1705 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1707 /* set all structure fields according to the token/value pairs */
1708 ldi = *leveldir_new;
1709 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1710 setSetupInfo(levelinfo_tokens, i,
1711 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1712 *leveldir_new = ldi;
1714 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1716 free(leveldir_new->name);
1717 leveldir_new->name = getStringCopy(leveldir_new->filename);
1720 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1722 if (leveldir_new->identifier == NULL)
1723 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1725 if (leveldir_new->name_sorting == NULL)
1726 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1728 if (node_parent == NULL) /* top level group */
1730 leveldir_new->basepath = level_directory;
1731 leveldir_new->fullpath = leveldir_new->filename;
1733 else /* sub level group */
1735 leveldir_new->basepath = node_parent->basepath;
1736 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1739 if (leveldir_new->levels < 1)
1740 leveldir_new->levels = 1;
1742 leveldir_new->last_level =
1743 leveldir_new->first_level + leveldir_new->levels - 1;
1745 leveldir_new->user_defined =
1746 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1748 leveldir_new->color = LEVELCOLOR(leveldir_new);
1749 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1751 leveldir_new->handicap_level = /* set handicap to default value */
1752 (leveldir_new->user_defined ?
1753 leveldir_new->last_level :
1754 leveldir_new->first_level);
1756 pushTreeInfo(node_first, leveldir_new);
1758 freeSetupFileHash(setup_file_hash);
1760 if (leveldir_new->level_group)
1762 /* create node to link back to current level directory */
1763 createParentTreeInfoNode(leveldir_new);
1765 /* step into sub-directory and look for more level series */
1766 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1767 leveldir_new, directory_path);
1770 free(directory_path);
1776 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1777 TreeInfo *node_parent,
1778 char *level_directory)
1781 struct dirent *dir_entry;
1782 boolean valid_entry_found = FALSE;
1784 if ((dir = opendir(level_directory)) == NULL)
1786 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1790 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1792 struct stat file_status;
1793 char *directory_name = dir_entry->d_name;
1794 char *directory_path = getPath2(level_directory, directory_name);
1796 /* skip entries for current and parent directory */
1797 if (strcmp(directory_name, ".") == 0 ||
1798 strcmp(directory_name, "..") == 0)
1800 free(directory_path);
1804 /* find out if directory entry is itself a directory */
1805 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1806 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1808 free(directory_path);
1812 free(directory_path);
1814 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1815 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1816 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1819 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1826 if (!valid_entry_found)
1828 /* check if this directory directly contains a file "levelinfo.conf" */
1829 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1830 level_directory, ".");
1833 if (!valid_entry_found)
1834 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1838 void LoadLevelInfo()
1840 InitUserLevelDirectory(getLoginName());
1842 DrawInitText("Loading level series:", 120, FC_GREEN);
1844 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1845 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1847 /* before sorting, the first entries will be from the user directory */
1848 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1850 if (leveldir_first == NULL)
1851 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1853 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1856 dumpTreeInfo(leveldir_first, 0);
1860 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1861 TreeInfo *node_parent,
1862 char *base_directory,
1863 char *directory_name, int type)
1865 char *directory_path = getPath2(base_directory, directory_name);
1866 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1867 SetupFileHash *setup_file_hash = NULL;
1868 TreeInfo *artwork_new = NULL;
1871 if (access(filename, F_OK) == 0) /* file exists */
1872 setup_file_hash = loadSetupFileHash(filename);
1874 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1877 struct dirent *dir_entry;
1878 boolean valid_file_found = FALSE;
1880 if ((dir = opendir(directory_path)) != NULL)
1882 while ((dir_entry = readdir(dir)) != NULL)
1884 char *entry_name = dir_entry->d_name;
1886 if (FileIsArtworkType(entry_name, type))
1888 valid_file_found = TRUE;
1896 if (!valid_file_found)
1898 if (strcmp(directory_name, ".") != 0)
1899 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1901 free(directory_path);
1908 artwork_new = newTreeInfo();
1911 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1913 setTreeInfoToDefaults(artwork_new, type);
1915 artwork_new->filename = getStringCopy(directory_name);
1917 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1920 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1923 /* set all structure fields according to the token/value pairs */
1925 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1926 setSetupInfo(levelinfo_tokens, i,
1927 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1930 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1932 free(artwork_new->name);
1933 artwork_new->name = getStringCopy(artwork_new->filename);
1937 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1940 if (artwork_new->identifier == NULL)
1941 artwork_new->identifier = getStringCopy(artwork_new->filename);
1943 if (artwork_new->name_sorting == NULL)
1944 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1947 if (node_parent == NULL) /* top level group */
1949 artwork_new->basepath = getStringCopy(base_directory);
1950 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1952 else /* sub level group */
1954 artwork_new->basepath = getStringCopy(node_parent->basepath);
1955 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1958 artwork_new->user_defined =
1959 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1961 /* (may use ".sort_priority" from "setup_file_hash" above) */
1962 artwork_new->color = ARTWORKCOLOR(artwork_new);
1963 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1965 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
1967 if (artwork_new->name != NULL)
1968 free(artwork_new->name);
1970 if (strcmp(artwork_new->filename, ".") == 0)
1972 if (artwork_new->user_defined)
1974 artwork_new->identifier = getStringCopy("private");
1975 artwork_new->sort_priority = ARTWORKCLASS_USER;
1979 artwork_new->identifier = getStringCopy("classic");
1980 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1983 /* set to new values after changing ".sort_priority" */
1984 artwork_new->color = ARTWORKCOLOR(artwork_new);
1985 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1989 artwork_new->identifier = getStringCopy(artwork_new->filename);
1992 artwork_new->name = getStringCopy(artwork_new->identifier);
1993 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1996 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1998 pushTreeInfo(node_first, artwork_new);
2000 freeSetupFileHash(setup_file_hash);
2002 free(directory_path);
2008 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2009 TreeInfo *node_parent,
2010 char *base_directory, int type)
2013 struct dirent *dir_entry;
2014 boolean valid_entry_found = FALSE;
2016 if ((dir = opendir(base_directory)) == NULL)
2018 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2019 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2023 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2025 struct stat file_status;
2026 char *directory_name = dir_entry->d_name;
2027 char *directory_path = getPath2(base_directory, directory_name);
2029 /* skip entries for current and parent directory */
2030 if (strcmp(directory_name, ".") == 0 ||
2031 strcmp(directory_name, "..") == 0)
2033 free(directory_path);
2037 /* find out if directory entry is itself a directory */
2038 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2039 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2041 free(directory_path);
2045 free(directory_path);
2047 /* check if this directory contains artwork with or without config file */
2048 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2050 directory_name, type);
2055 /* check if this directory directly contains artwork itself */
2056 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2057 base_directory, ".",
2059 if (!valid_entry_found)
2060 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2064 static TreeInfo *getDummyArtworkInfo(int type)
2066 /* this is only needed when there is completely no artwork available */
2067 TreeInfo *artwork_new = newTreeInfo();
2069 setTreeInfoToDefaults(artwork_new, type);
2071 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2072 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2073 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2075 if (artwork_new->name != NULL)
2076 free(artwork_new->name);
2078 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2079 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2080 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2085 void LoadArtworkInfo()
2087 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2089 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2090 options.graphics_directory,
2091 TREE_TYPE_GRAPHICS_DIR);
2092 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2093 getUserGraphicsDir(),
2094 TREE_TYPE_GRAPHICS_DIR);
2096 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2097 options.sounds_directory,
2098 TREE_TYPE_SOUNDS_DIR);
2099 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2101 TREE_TYPE_SOUNDS_DIR);
2103 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2104 options.music_directory,
2105 TREE_TYPE_MUSIC_DIR);
2106 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2108 TREE_TYPE_MUSIC_DIR);
2110 if (artwork.gfx_first == NULL)
2111 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2112 if (artwork.snd_first == NULL)
2113 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2114 if (artwork.mus_first == NULL)
2115 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2117 /* before sorting, the first entries will be from the user directory */
2118 artwork.gfx_current =
2119 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2120 if (artwork.gfx_current == NULL)
2121 artwork.gfx_current =
2122 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2123 if (artwork.gfx_current == NULL)
2124 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2126 artwork.snd_current =
2127 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2128 if (artwork.snd_current == NULL)
2129 artwork.snd_current =
2130 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2131 if (artwork.snd_current == NULL)
2132 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2134 artwork.mus_current =
2135 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2136 if (artwork.mus_current == NULL)
2137 artwork.mus_current =
2138 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2139 if (artwork.mus_current == NULL)
2140 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2142 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2143 artwork.snd_current_identifier = artwork.snd_current->identifier;
2144 artwork.mus_current_identifier = artwork.mus_current->identifier;
2147 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2148 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2149 printf("music set == %s\n\n", artwork.mus_current_identifier);
2152 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2153 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2154 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2157 dumpTreeInfo(artwork.gfx_first, 0);
2158 dumpTreeInfo(artwork.snd_first, 0);
2159 dumpTreeInfo(artwork.mus_first, 0);
2163 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2164 LevelDirTree *level_node)
2166 /* recursively check all level directories for artwork sub-directories */
2170 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2171 ARTWORK_DIRECTORY((*artwork_node)->type));
2174 if (!level_node->parent_link)
2175 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2176 level_node->filename, level_node->name);
2179 if (!level_node->parent_link)
2181 TreeInfo *topnode_last = *artwork_node;
2183 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2184 (*artwork_node)->type);
2186 if (topnode_last != *artwork_node)
2188 free((*artwork_node)->identifier);
2189 free((*artwork_node)->name);
2190 free((*artwork_node)->name_sorting);
2192 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2193 (*artwork_node)->name = getStringCopy(level_node->name);
2194 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2196 (*artwork_node)->sort_priority = level_node->sort_priority;
2197 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2203 if (level_node->node_group != NULL)
2204 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2206 level_node = level_node->next;
2210 void LoadLevelArtworkInfo()
2212 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2214 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2215 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2216 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2218 /* needed for reloading level artwork not known at ealier stage */
2219 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2221 artwork.gfx_current =
2222 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2223 if (artwork.gfx_current == NULL)
2224 artwork.gfx_current =
2225 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2226 if (artwork.gfx_current == NULL)
2227 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2230 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2232 artwork.snd_current =
2233 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2234 if (artwork.snd_current == NULL)
2235 artwork.snd_current =
2236 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2237 if (artwork.snd_current == NULL)
2238 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2241 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2243 artwork.mus_current =
2244 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2245 if (artwork.mus_current == NULL)
2246 artwork.mus_current =
2247 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2248 if (artwork.mus_current == NULL)
2249 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2252 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2253 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2254 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2257 dumpTreeInfo(artwork.gfx_first, 0);
2258 dumpTreeInfo(artwork.snd_first, 0);
2259 dumpTreeInfo(artwork.mus_first, 0);
2263 static void SaveUserLevelInfo()
2269 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2271 if (!(file = fopen(filename, MODE_WRITE)))
2273 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2278 /* always start with reliable default values */
2279 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2281 ldi.name = getStringCopy(getLoginName());
2282 ldi.author = getStringCopy(getRealName());
2284 ldi.first_level = 1;
2285 ldi.sort_priority = LEVELCLASS_USER_START;
2286 ldi.readonly = FALSE;
2287 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2288 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2289 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2291 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2292 getCookie("LEVELINFO")));
2294 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2295 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2296 i != LEVELINFO_TOKEN_NAME_SORTING &&
2297 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2298 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2303 SetFilePermissions(filename, PERMS_PRIVATE);
2306 char *getSetupValue(int type, void *value)
2308 static char value_string[MAX_LINE_LEN];
2316 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2320 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2324 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2328 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2332 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2336 sprintf(value_string, "%d", *(int *)value);
2340 strcpy(value_string, *(char **)value);
2344 value_string[0] = '\0';
2348 return value_string;
2351 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2355 static char token_string[MAX_LINE_LEN];
2356 int token_type = token_info[token_nr].type;
2357 void *setup_value = token_info[token_nr].value;
2358 char *token_text = token_info[token_nr].text;
2359 char *value_string = getSetupValue(token_type, setup_value);
2361 /* build complete token string */
2362 sprintf(token_string, "%s%s", prefix, token_text);
2364 /* build setup entry line */
2365 line = getFormattedSetupEntry(token_string, value_string);
2367 if (token_type == TYPE_KEY_X11)
2369 Key key = *(Key *)setup_value;
2370 char *keyname = getKeyNameFromKey(key);
2372 /* add comment, if useful */
2373 if (strcmp(keyname, "(undefined)") != 0 &&
2374 strcmp(keyname, "(unknown)") != 0)
2376 /* add at least one whitespace */
2378 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2382 strcat(line, keyname);
2389 void LoadLevelSetup_LastSeries()
2392 SetupFileHash *level_setup_hash = NULL;
2394 /* always start with reliable default values */
2395 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2397 /* ----------------------------------------------------------------------- */
2398 /* ~/.<program>/levelsetup.conf */
2399 /* ----------------------------------------------------------------------- */
2401 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2403 if ((level_setup_hash = loadSetupFileHash(filename)))
2405 char *last_level_series =
2406 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2408 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2410 if (leveldir_current == NULL)
2411 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2413 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2415 freeSetupFileHash(level_setup_hash);
2418 Error(ERR_WARN, "using default setup values");
2423 void SaveLevelSetup_LastSeries()
2426 char *level_subdir = leveldir_current->filename;
2429 /* ----------------------------------------------------------------------- */
2430 /* ~/.<program>/levelsetup.conf */
2431 /* ----------------------------------------------------------------------- */
2433 InitUserDataDirectory();
2435 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2437 if (!(file = fopen(filename, MODE_WRITE)))
2439 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2444 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2445 getCookie("LEVELSETUP")));
2446 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2452 SetFilePermissions(filename, PERMS_PRIVATE);
2455 static void checkSeriesInfo()
2457 static char *level_directory = NULL;
2459 struct dirent *dir_entry;
2461 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2463 level_directory = getPath2((leveldir_current->user_defined ?
2464 getUserLevelDir(NULL) :
2465 options.level_directory),
2466 leveldir_current->fullpath);
2468 if ((dir = opendir(level_directory)) == NULL)
2470 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2474 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2476 if (strlen(dir_entry->d_name) > 4 &&
2477 dir_entry->d_name[3] == '.' &&
2478 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2480 char levelnum_str[4];
2483 strncpy(levelnum_str, dir_entry->d_name, 3);
2484 levelnum_str[3] = '\0';
2486 levelnum_value = atoi(levelnum_str);
2489 if (levelnum_value < leveldir_current->first_level)
2491 Error(ERR_WARN, "additional level %d found", levelnum_value);
2492 leveldir_current->first_level = levelnum_value;
2494 else if (levelnum_value > leveldir_current->last_level)
2496 Error(ERR_WARN, "additional level %d found", levelnum_value);
2497 leveldir_current->last_level = levelnum_value;
2506 void LoadLevelSetup_SeriesInfo()
2509 SetupFileHash *level_setup_hash = NULL;
2510 char *level_subdir = leveldir_current->filename;
2512 /* always start with reliable default values */
2513 level_nr = leveldir_current->first_level;
2515 checkSeriesInfo(leveldir_current);
2517 /* ----------------------------------------------------------------------- */
2518 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2519 /* ----------------------------------------------------------------------- */
2521 level_subdir = leveldir_current->filename;
2523 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2525 if ((level_setup_hash = loadSetupFileHash(filename)))
2529 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2533 level_nr = atoi(token_value);
2535 if (level_nr < leveldir_current->first_level)
2536 level_nr = leveldir_current->first_level;
2537 if (level_nr > leveldir_current->last_level)
2538 level_nr = leveldir_current->last_level;
2541 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2545 int level_nr = atoi(token_value);
2547 if (level_nr < leveldir_current->first_level)
2548 level_nr = leveldir_current->first_level;
2549 if (level_nr > leveldir_current->last_level + 1)
2550 level_nr = leveldir_current->last_level;
2552 if (leveldir_current->user_defined)
2553 level_nr = leveldir_current->last_level;
2555 leveldir_current->handicap_level = level_nr;
2558 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2560 freeSetupFileHash(level_setup_hash);
2563 Error(ERR_WARN, "using default setup values");
2568 void SaveLevelSetup_SeriesInfo()
2571 char *level_subdir = leveldir_current->filename;
2572 char *level_nr_str = int2str(level_nr, 0);
2573 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2576 /* ----------------------------------------------------------------------- */
2577 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2578 /* ----------------------------------------------------------------------- */
2580 InitLevelSetupDirectory(level_subdir);
2582 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2584 if (!(file = fopen(filename, MODE_WRITE)))
2586 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2591 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2592 getCookie("LEVELSETUP")));
2593 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2595 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2596 handicap_level_str));
2601 SetFilePermissions(filename, PERMS_PRIVATE);