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);
499 if (strcmp(basename, "RocksScreen.pcx") == 0)
501 printf("::: trying 2 '%s' ...\n", filename);
503 if (fileExists(filename))
509 /* 1st try: look for special artwork configured in level series config */
510 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
513 if (strcmp(basename, "RocksScreen.pcx") == 0)
515 printf("::: trying 2.1 '%s' ...\n", filename);
517 if (fileExists(filename))
524 /* 3rd try: look for special artwork in configured artwork directory */
525 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
528 if (strcmp(basename, "RocksScreen.pcx") == 0)
530 printf("::: trying 3 '%s' ...\n", filename);
532 if (fileExists(filename))
537 /* 4th try: look for default artwork in new default artwork directory */
538 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
540 if (strcmp(basename, "RocksScreen.pcx") == 0)
541 printf("::: trying 4 '%s' ...\n", filename);
543 if (fileExists(filename))
548 /* 5th try: look for default artwork in old default artwork directory */
549 filename = getPath2(options.graphics_directory, basename);
551 if (strcmp(basename, "RocksScreen.pcx") == 0)
552 printf("::: trying 5 '%s' ...\n", filename);
554 if (fileExists(filename))
557 return NULL; /* cannot find specified artwork file anywhere */
560 char *getCustomSoundFilename(char *basename)
562 static char *filename = NULL;
564 if (filename != NULL)
567 if (!setup.override_level_sounds)
570 /* 1st try: look for special artwork configured in level series config */
571 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
572 if (fileExists(filename))
578 /* 2nd try: look for special artwork in current level series directory */
579 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
580 if (fileExists(filename))
586 /* 1st try: look for special artwork configured in level series config */
587 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
588 if (fileExists(filename))
595 /* 3rd try: look for special artwork in configured artwork directory */
596 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
597 if (fileExists(filename))
602 /* 4th try: look for default artwork in new default artwork directory */
603 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
604 if (fileExists(filename))
609 /* 5th try: look for default artwork in old default artwork directory */
610 filename = getPath2(options.sounds_directory, basename);
611 if (fileExists(filename))
614 return NULL; /* cannot find specified artwork file anywhere */
617 char *getCustomArtworkFilename(char *basename, int type)
619 if (type == ARTWORK_TYPE_GRAPHICS)
620 return getCustomImageFilename(basename);
621 else if (type == ARTWORK_TYPE_SOUNDS)
622 return getCustomSoundFilename(basename);
624 return UNDEFINED_FILENAME;
627 char *getCustomArtworkConfigFilename(int type)
629 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
632 char *getCustomArtworkLevelConfigFilename(int type)
634 static char *filename = NULL;
636 if (filename != NULL)
639 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
644 char *getCustomMusicDirectory(void)
646 static char *directory = NULL;
648 if (directory != NULL)
651 if (!setup.override_level_music)
654 /* 1st try: look for special artwork configured in level series config */
655 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
656 if (fileExists(directory))
662 /* 2nd try: look for special artwork in current level series directory */
663 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
664 if (fileExists(directory))
670 /* 1st try: look for special artwork configured in level series config */
671 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
672 if (fileExists(directory))
679 /* 3rd try: look for special artwork in configured artwork directory */
680 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
681 if (fileExists(directory))
686 /* 4th try: look for default artwork in new default artwork directory */
687 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
688 if (fileExists(directory))
693 /* 5th try: look for default artwork in old default artwork directory */
694 directory = getStringCopy(options.music_directory);
695 if (fileExists(directory))
698 return NULL; /* cannot find specified artwork file anywhere */
701 void InitTapeDirectory(char *level_subdir)
703 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
704 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
705 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
708 void InitScoreDirectory(char *level_subdir)
710 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
711 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
712 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
715 static void SaveUserLevelInfo();
717 void InitUserLevelDirectory(char *level_subdir)
719 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
721 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
722 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
723 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
729 void InitLevelSetupDirectory(char *level_subdir)
731 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
732 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
733 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
737 /* ------------------------------------------------------------------------- */
738 /* some functions to handle lists of level directories */
739 /* ------------------------------------------------------------------------- */
741 TreeInfo *newTreeInfo()
743 return checked_calloc(sizeof(TreeInfo));
746 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
748 node_new->next = *node_first;
749 *node_first = node_new;
752 int numTreeInfo(TreeInfo *node)
765 boolean validLevelSeries(TreeInfo *node)
767 return (node != NULL && !node->node_group && !node->parent_link);
770 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
775 if (node->node_group) /* enter level group (step down into tree) */
776 return getFirstValidTreeInfoEntry(node->node_group);
777 else if (node->parent_link) /* skip start entry of level group */
779 if (node->next) /* get first real level series entry */
780 return getFirstValidTreeInfoEntry(node->next);
781 else /* leave empty level group and go on */
782 return getFirstValidTreeInfoEntry(node->node_parent->next);
784 else /* this seems to be a regular level series */
788 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
793 if (node->node_parent == NULL) /* top level group */
794 return *node->node_top;
795 else /* sub level group */
796 return node->node_parent->node_group;
799 int numTreeInfoInGroup(TreeInfo *node)
801 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
804 int posTreeInfo(TreeInfo *node)
806 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
811 if (node_cmp == node)
815 node_cmp = node_cmp->next;
821 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
823 TreeInfo *node_default = node;
838 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
840 if (identifier == NULL)
845 if (node->node_group)
847 TreeInfo *node_group;
849 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
854 else if (!node->parent_link)
856 if (strcmp(identifier, node->identifier) == 0)
866 void dumpTreeInfo(TreeInfo *node, int depth)
870 printf("Dumping TreeInfo:\n");
874 for (i=0; i<(depth + 1) * 3; i++)
877 printf("filename == '%s' (%s) [%s] (%d)\n",
878 node->filename, node->name, node->identifier, node->sort_priority);
880 if (node->node_group != NULL)
881 dumpTreeInfo(node->node_group, depth + 1);
887 void sortTreeInfo(TreeInfo **node_first,
888 int (*compare_function)(const void *, const void *))
890 int num_nodes = numTreeInfo(*node_first);
891 TreeInfo **sort_array;
892 TreeInfo *node = *node_first;
898 /* allocate array for sorting structure pointers */
899 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
901 /* writing structure pointers to sorting array */
902 while (i < num_nodes && node) /* double boundary check... */
904 sort_array[i] = node;
910 /* sorting the structure pointers in the sorting array */
911 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
914 /* update the linkage of list elements with the sorted node array */
915 for (i=0; i<num_nodes - 1; i++)
916 sort_array[i]->next = sort_array[i + 1];
917 sort_array[num_nodes - 1]->next = NULL;
919 /* update the linkage of the main list anchor pointer */
920 *node_first = sort_array[0];
924 /* now recursively sort the level group structures */
928 if (node->node_group != NULL)
929 sortTreeInfo(&node->node_group, compare_function);
936 /* ========================================================================= */
937 /* some stuff from "files.c" */
938 /* ========================================================================= */
940 #if defined(PLATFORM_WIN32)
942 #define S_IRGRP S_IRUSR
945 #define S_IROTH S_IRUSR
948 #define S_IWGRP S_IWUSR
951 #define S_IWOTH S_IWUSR
954 #define S_IXGRP S_IXUSR
957 #define S_IXOTH S_IXUSR
960 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
965 #endif /* PLATFORM_WIN32 */
967 /* file permissions for newly written files */
968 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
969 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
970 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
972 #define MODE_W_PRIVATE (S_IWUSR)
973 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
974 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
976 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
977 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
979 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
980 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
982 char *getUserDataDir(void)
984 static char *userdata_dir = NULL;
986 if (userdata_dir == NULL)
987 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
992 char *getCommonDataDir(void)
994 static char *common_data_dir = NULL;
996 #if defined(PLATFORM_WIN32)
997 if (common_data_dir == NULL)
999 char *dir = checked_malloc(MAX_PATH + 1);
1001 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1002 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1003 common_data_dir = getPath2(dir, program.userdata_directory);
1005 common_data_dir = options.rw_base_directory;
1008 if (common_data_dir == NULL)
1009 common_data_dir = options.rw_base_directory;
1012 return common_data_dir;
1017 return getUserDataDir();
1020 static mode_t posix_umask(mode_t mask)
1022 #if defined(PLATFORM_UNIX)
1029 static int posix_mkdir(const char *pathname, mode_t mode)
1031 #if defined(PLATFORM_WIN32)
1032 return mkdir(pathname);
1034 return mkdir(pathname, mode);
1038 void createDirectory(char *dir, char *text, int permission_class)
1040 /* leave "other" permissions in umask untouched, but ensure group parts
1041 of USERDATA_DIR_MODE are not masked */
1042 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1043 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1044 mode_t normal_umask = posix_umask(0);
1045 mode_t group_umask = ~(dir_mode & S_IRWXG);
1046 posix_umask(normal_umask & group_umask);
1048 if (access(dir, F_OK) != 0)
1049 if (posix_mkdir(dir, dir_mode) != 0)
1050 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1052 posix_umask(normal_umask); /* reset normal umask */
1055 void InitUserDataDirectory()
1057 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1060 void SetFilePermissions(char *filename, int permission_class)
1062 chmod(filename, (permission_class == PERMS_PRIVATE ?
1063 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1066 char *getCookie(char *file_type)
1068 static char cookie[MAX_COOKIE_LEN + 1];
1070 if (strlen(program.cookie_prefix) + 1 +
1071 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1072 return "[COOKIE ERROR]"; /* should never happen */
1074 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1075 program.cookie_prefix, file_type,
1076 program.version_major, program.version_minor);
1081 int getFileVersionFromCookieString(const char *cookie)
1083 const char *ptr_cookie1, *ptr_cookie2;
1084 const char *pattern1 = "_FILE_VERSION_";
1085 const char *pattern2 = "?.?";
1086 const int len_cookie = strlen(cookie);
1087 const int len_pattern1 = strlen(pattern1);
1088 const int len_pattern2 = strlen(pattern2);
1089 const int len_pattern = len_pattern1 + len_pattern2;
1090 int version_major, version_minor;
1092 if (len_cookie <= len_pattern)
1095 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1096 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1098 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1101 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1102 ptr_cookie2[1] != '.' ||
1103 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1106 version_major = ptr_cookie2[0] - '0';
1107 version_minor = ptr_cookie2[2] - '0';
1109 return VERSION_IDENT(version_major, version_minor, 0);
1112 boolean checkCookieString(const char *cookie, const char *template)
1114 const char *pattern = "_FILE_VERSION_?.?";
1115 const int len_cookie = strlen(cookie);
1116 const int len_template = strlen(template);
1117 const int len_pattern = strlen(pattern);
1119 if (len_cookie != len_template)
1122 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1128 /* ------------------------------------------------------------------------- */
1129 /* setup file list and hash handling functions */
1130 /* ------------------------------------------------------------------------- */
1132 char *getFormattedSetupEntry(char *token, char *value)
1135 static char entry[MAX_LINE_LEN];
1137 /* start with the token and some spaces to format output line */
1138 sprintf(entry, "%s:", token);
1139 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1142 /* continue with the token's value */
1143 strcat(entry, value);
1148 SetupFileList *newSetupFileList(char *token, char *value)
1150 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1152 new->token = getStringCopy(token);
1153 new->value = getStringCopy(value);
1160 void freeSetupFileList(SetupFileList *list)
1170 freeSetupFileList(list->next);
1174 char *getListEntry(SetupFileList *list, char *token)
1179 if (strcmp(list->token, token) == 0)
1182 return getListEntry(list->next, token);
1185 void setListEntry(SetupFileList *list, char *token, char *value)
1190 if (strcmp(list->token, token) == 0)
1195 list->value = getStringCopy(value);
1197 else if (list->next == NULL)
1198 list->next = newSetupFileList(token, value);
1200 setListEntry(list->next, token, value);
1204 static void printSetupFileList(SetupFileList *list)
1209 printf("token: '%s'\n", list->token);
1210 printf("value: '%s'\n", list->value);
1212 printSetupFileList(list->next);
1217 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1218 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1219 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1220 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1222 #define insert_hash_entry hashtable_insert
1223 #define search_hash_entry hashtable_search
1224 #define change_hash_entry hashtable_change
1225 #define remove_hash_entry hashtable_remove
1228 static unsigned int get_hash_from_key(void *key)
1233 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1234 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1235 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1236 it works better than many other constants, prime or not) has never been
1237 adequately explained.
1239 If you just want to have a good hash function, and cannot wait, djb2
1240 is one of the best string hash functions i know. It has excellent
1241 distribution and speed on many different sets of keys and table sizes.
1242 You are not likely to do better with one of the "well known" functions
1243 such as PJW, K&R, etc.
1245 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1248 char *str = (char *)key;
1249 unsigned int hash = 5381;
1252 while ((c = *str++))
1253 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1258 static int keys_are_equal(void *key1, void *key2)
1260 return (strcmp((char *)key1, (char *)key2) == 0);
1263 SetupFileHash *newSetupFileHash()
1265 SetupFileHash *new_hash =
1266 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1271 void freeSetupFileHash(SetupFileHash *hash)
1276 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1279 char *getHashEntry(SetupFileHash *hash, char *token)
1284 return search_hash_entry(hash, token);
1287 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1294 value_copy = getStringCopy(value);
1296 /* change value; if it does not exist, insert it as new */
1297 if (!change_hash_entry(hash, token, value_copy))
1298 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1299 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1304 static void printSetupFileHash(SetupFileHash *hash)
1306 BEGIN_HASH_ITERATION(hash, itr)
1308 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1309 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1311 END_HASH_ITERATION(hash, itr)
1316 static void *loadSetupFileData(char *filename, boolean use_hash)
1319 char line[MAX_LINE_LEN];
1320 char *token, *value, *line_ptr;
1321 void *setup_file_data;
1325 setup_file_data = newSetupFileHash();
1327 setup_file_data = newSetupFileList("", "");
1329 if (!(file = fopen(filename, MODE_READ)))
1331 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1337 /* read next line of input file */
1338 if (!fgets(line, MAX_LINE_LEN, file))
1341 /* cut trailing comment or whitespace from input line */
1342 for (line_ptr = line; *line_ptr; line_ptr++)
1344 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1351 /* cut trailing whitespaces from input line */
1352 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1353 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1356 /* ignore empty lines */
1360 line_len = strlen(line);
1362 /* cut leading whitespaces from token */
1363 for (token = line; *token; token++)
1364 if (*token != ' ' && *token != '\t')
1367 /* find end of token */
1368 for (line_ptr = token; *line_ptr; line_ptr++)
1370 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1377 if (line_ptr < line + line_len)
1378 value = line_ptr + 1;
1382 /* cut leading whitespaces from value */
1383 for (; *value; value++)
1384 if (*value != ' ' && *value != '\t')
1387 if (*token && *value)
1390 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1392 setListEntry((SetupFileList *)setup_file_data, token, value);
1400 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1401 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1405 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1406 SetupFileList *first_valid_list_entry = setup_file_list->next;
1408 /* free empty list header */
1409 setup_file_list->next = NULL;
1410 freeSetupFileList(setup_file_list);
1411 setup_file_data = first_valid_list_entry;
1413 if (first_valid_list_entry == NULL)
1414 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1417 return setup_file_data;
1420 SetupFileList *loadSetupFileList(char *filename)
1422 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1425 SetupFileHash *loadSetupFileHash(char *filename)
1427 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1430 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1433 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1436 Error(ERR_WARN, "configuration file has no file identifier");
1437 else if (!checkCookieString(value, identifier))
1438 Error(ERR_WARN, "configuration file has wrong file identifier");
1442 /* ========================================================================= */
1443 /* setup file stuff */
1444 /* ========================================================================= */
1446 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1447 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1448 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1450 /* level directory info */
1451 #define LEVELINFO_TOKEN_IDENTIFIER 0
1452 #define LEVELINFO_TOKEN_NAME 1
1453 #define LEVELINFO_TOKEN_NAME_SORTING 2
1454 #define LEVELINFO_TOKEN_AUTHOR 3
1455 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1456 #define LEVELINFO_TOKEN_LEVELS 5
1457 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1458 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1459 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1460 #define LEVELINFO_TOKEN_READONLY 9
1461 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1462 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1463 #define LEVELINFO_TOKEN_MUSIC_SET 12
1465 #define NUM_LEVELINFO_TOKENS 13
1467 static LevelDirTree ldi;
1469 static struct TokenInfo levelinfo_tokens[] =
1471 /* level directory info */
1472 { TYPE_STRING, &ldi.identifier, "identifier" },
1473 { TYPE_STRING, &ldi.name, "name" },
1474 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1475 { TYPE_STRING, &ldi.author, "author" },
1476 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1477 { TYPE_INTEGER, &ldi.levels, "levels" },
1478 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1479 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1480 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1481 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1482 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1483 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1484 { TYPE_STRING, &ldi.music_set, "music_set" }
1487 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1491 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1492 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1493 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1494 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1497 ldi->node_parent = NULL;
1498 ldi->node_group = NULL;
1502 ldi->cl_cursor = -1;
1504 ldi->filename = NULL;
1505 ldi->fullpath = NULL;
1506 ldi->basepath = NULL;
1507 ldi->identifier = NULL;
1508 ldi->name = getStringCopy(ANONYMOUS_NAME);
1509 ldi->name_sorting = NULL;
1510 ldi->author = getStringCopy(ANONYMOUS_NAME);
1512 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1513 ldi->parent_link = FALSE;
1514 ldi->user_defined = FALSE;
1516 ldi->class_desc = NULL;
1518 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1520 ldi->imported_from = NULL;
1521 ldi->graphics_set = NULL;
1522 ldi->sounds_set = NULL;
1523 ldi->music_set = NULL;
1524 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1525 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1526 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1528 ldi->first_level = 0;
1529 ldi->last_level = 0;
1530 ldi->level_group = FALSE;
1531 ldi->handicap_level = 0;
1532 ldi->readonly = TRUE;
1536 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1540 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1542 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1546 /* first copy all values from the parent structure ... */
1549 /* ... then set all fields to default that cannot be inherited from parent.
1550 This is especially important for all those fields that can be set from
1551 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1552 calls 'free()' for all already set token values which requires that no
1553 other structure's pointer may point to them!
1556 ldi->filename = NULL;
1557 ldi->fullpath = NULL;
1558 ldi->basepath = NULL;
1559 ldi->identifier = NULL;
1560 ldi->name = getStringCopy(ANONYMOUS_NAME);
1561 ldi->name_sorting = NULL;
1562 ldi->author = getStringCopy(parent->author);
1563 ldi->imported_from = getStringCopy(parent->imported_from);
1565 ldi->level_group = FALSE;
1566 ldi->parent_link = FALSE;
1568 ldi->node_top = parent->node_top;
1569 ldi->node_parent = parent;
1570 ldi->node_group = NULL;
1574 void setSetupInfo(struct TokenInfo *token_info,
1575 int token_nr, char *token_value)
1577 int token_type = token_info[token_nr].type;
1578 void *setup_value = token_info[token_nr].value;
1580 if (token_value == NULL)
1583 /* set setup field to corresponding token value */
1588 *(boolean *)setup_value = get_boolean_from_string(token_value);
1592 *(Key *)setup_value = getKeyFromKeyName(token_value);
1596 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1600 *(int *)setup_value = get_integer_from_string(token_value);
1604 if (*(char **)setup_value != NULL)
1605 free(*(char **)setup_value);
1606 *(char **)setup_value = getStringCopy(token_value);
1614 static int compareTreeInfoEntries(const void *object1, const void *object2)
1616 const TreeInfo *entry1 = *((TreeInfo **)object1);
1617 const TreeInfo *entry2 = *((TreeInfo **)object2);
1618 int class_sorting1, class_sorting2;
1621 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1623 class_sorting1 = LEVELSORTING(entry1);
1624 class_sorting2 = LEVELSORTING(entry2);
1628 class_sorting1 = ARTWORKSORTING(entry1);
1629 class_sorting2 = ARTWORKSORTING(entry2);
1632 if (entry1->parent_link || entry2->parent_link)
1633 compare_result = (entry1->parent_link ? -1 : +1);
1634 else if (entry1->sort_priority == entry2->sort_priority)
1636 char *name1 = getStringToLower(entry1->name_sorting);
1637 char *name2 = getStringToLower(entry2->name_sorting);
1639 compare_result = strcmp(name1, name2);
1644 else if (class_sorting1 == class_sorting2)
1645 compare_result = entry1->sort_priority - entry2->sort_priority;
1647 compare_result = class_sorting1 - class_sorting2;
1649 return compare_result;
1652 static void createParentTreeInfoNode(TreeInfo *node_parent)
1656 if (node_parent == NULL)
1659 ti_new = newTreeInfo();
1660 setTreeInfoToDefaults(ti_new, node_parent->type);
1662 ti_new->node_parent = node_parent;
1663 ti_new->parent_link = TRUE;
1665 ti_new->identifier = getStringCopy(node_parent->identifier);
1666 ti_new->name = ".. (parent directory)";
1667 ti_new->name_sorting = getStringCopy(ti_new->name);
1669 ti_new->filename = "..";
1670 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1672 ti_new->sort_priority = node_parent->sort_priority;
1673 ti_new->class_desc = getLevelClassDescription(ti_new);
1675 pushTreeInfo(&node_parent->node_group, ti_new);
1678 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1679 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1681 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1682 TreeInfo *node_parent,
1683 char *level_directory,
1684 char *directory_name)
1686 char *directory_path = getPath2(level_directory, directory_name);
1687 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1688 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1689 LevelDirTree *leveldir_new = NULL;
1692 if (setup_file_hash == NULL)
1694 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1696 free(directory_path);
1702 leveldir_new = newTreeInfo();
1705 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1707 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1709 leveldir_new->filename = getStringCopy(directory_name);
1711 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1713 /* set all structure fields according to the token/value pairs */
1714 ldi = *leveldir_new;
1715 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1716 setSetupInfo(levelinfo_tokens, i,
1717 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1718 *leveldir_new = ldi;
1720 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1722 free(leveldir_new->name);
1723 leveldir_new->name = getStringCopy(leveldir_new->filename);
1726 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1728 if (leveldir_new->identifier == NULL)
1729 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1731 if (leveldir_new->name_sorting == NULL)
1732 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1734 if (node_parent == NULL) /* top level group */
1736 leveldir_new->basepath = level_directory;
1737 leveldir_new->fullpath = leveldir_new->filename;
1739 else /* sub level group */
1741 leveldir_new->basepath = node_parent->basepath;
1742 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1745 if (leveldir_new->levels < 1)
1746 leveldir_new->levels = 1;
1748 leveldir_new->last_level =
1749 leveldir_new->first_level + leveldir_new->levels - 1;
1751 leveldir_new->user_defined =
1752 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1754 leveldir_new->color = LEVELCOLOR(leveldir_new);
1755 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1757 leveldir_new->handicap_level = /* set handicap to default value */
1758 (leveldir_new->user_defined ?
1759 leveldir_new->last_level :
1760 leveldir_new->first_level);
1762 pushTreeInfo(node_first, leveldir_new);
1764 freeSetupFileHash(setup_file_hash);
1766 if (leveldir_new->level_group)
1768 /* create node to link back to current level directory */
1769 createParentTreeInfoNode(leveldir_new);
1771 /* step into sub-directory and look for more level series */
1772 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1773 leveldir_new, directory_path);
1776 free(directory_path);
1782 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1783 TreeInfo *node_parent,
1784 char *level_directory)
1787 struct dirent *dir_entry;
1788 boolean valid_entry_found = FALSE;
1790 if ((dir = opendir(level_directory)) == NULL)
1792 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1796 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1798 struct stat file_status;
1799 char *directory_name = dir_entry->d_name;
1800 char *directory_path = getPath2(level_directory, directory_name);
1802 /* skip entries for current and parent directory */
1803 if (strcmp(directory_name, ".") == 0 ||
1804 strcmp(directory_name, "..") == 0)
1806 free(directory_path);
1810 /* find out if directory entry is itself a directory */
1811 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1812 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1814 free(directory_path);
1818 free(directory_path);
1820 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1821 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1822 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1825 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1832 if (!valid_entry_found)
1834 /* check if this directory directly contains a file "levelinfo.conf" */
1835 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1836 level_directory, ".");
1839 if (!valid_entry_found)
1840 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1844 void LoadLevelInfo()
1846 InitUserLevelDirectory(getLoginName());
1848 DrawInitText("Loading level series:", 120, FC_GREEN);
1850 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1851 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1853 /* before sorting, the first entries will be from the user directory */
1854 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1856 if (leveldir_first == NULL)
1857 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1859 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1862 dumpTreeInfo(leveldir_first, 0);
1866 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1867 TreeInfo *node_parent,
1868 char *base_directory,
1869 char *directory_name, int type)
1871 char *directory_path = getPath2(base_directory, directory_name);
1872 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1873 SetupFileHash *setup_file_hash = NULL;
1874 TreeInfo *artwork_new = NULL;
1877 if (access(filename, F_OK) == 0) /* file exists */
1878 setup_file_hash = loadSetupFileHash(filename);
1880 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1883 struct dirent *dir_entry;
1884 boolean valid_file_found = FALSE;
1886 if ((dir = opendir(directory_path)) != NULL)
1888 while ((dir_entry = readdir(dir)) != NULL)
1890 char *entry_name = dir_entry->d_name;
1892 if (FileIsArtworkType(entry_name, type))
1894 valid_file_found = TRUE;
1902 if (!valid_file_found)
1904 if (strcmp(directory_name, ".") != 0)
1905 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1907 free(directory_path);
1914 artwork_new = newTreeInfo();
1917 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1919 setTreeInfoToDefaults(artwork_new, type);
1921 artwork_new->filename = getStringCopy(directory_name);
1923 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
1926 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
1929 /* set all structure fields according to the token/value pairs */
1931 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1932 setSetupInfo(levelinfo_tokens, i,
1933 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1936 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1938 free(artwork_new->name);
1939 artwork_new->name = getStringCopy(artwork_new->filename);
1943 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1946 if (artwork_new->identifier == NULL)
1947 artwork_new->identifier = getStringCopy(artwork_new->filename);
1949 if (artwork_new->name_sorting == NULL)
1950 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1953 if (node_parent == NULL) /* top level group */
1955 artwork_new->basepath = getStringCopy(base_directory);
1956 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1958 else /* sub level group */
1960 artwork_new->basepath = getStringCopy(node_parent->basepath);
1961 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1964 artwork_new->user_defined =
1965 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1967 /* (may use ".sort_priority" from "setup_file_hash" above) */
1968 artwork_new->color = ARTWORKCOLOR(artwork_new);
1969 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1971 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
1973 if (artwork_new->name != NULL)
1974 free(artwork_new->name);
1976 if (strcmp(artwork_new->filename, ".") == 0)
1978 if (artwork_new->user_defined)
1980 artwork_new->identifier = getStringCopy("private");
1981 artwork_new->sort_priority = ARTWORKCLASS_USER;
1985 artwork_new->identifier = getStringCopy("classic");
1986 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1989 /* set to new values after changing ".sort_priority" */
1990 artwork_new->color = ARTWORKCOLOR(artwork_new);
1991 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1995 artwork_new->identifier = getStringCopy(artwork_new->filename);
1998 artwork_new->name = getStringCopy(artwork_new->identifier);
1999 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2002 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2004 pushTreeInfo(node_first, artwork_new);
2006 freeSetupFileHash(setup_file_hash);
2008 free(directory_path);
2014 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2015 TreeInfo *node_parent,
2016 char *base_directory, int type)
2019 struct dirent *dir_entry;
2020 boolean valid_entry_found = FALSE;
2022 if ((dir = opendir(base_directory)) == NULL)
2024 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2025 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2029 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2031 struct stat file_status;
2032 char *directory_name = dir_entry->d_name;
2033 char *directory_path = getPath2(base_directory, directory_name);
2035 /* skip entries for current and parent directory */
2036 if (strcmp(directory_name, ".") == 0 ||
2037 strcmp(directory_name, "..") == 0)
2039 free(directory_path);
2043 /* find out if directory entry is itself a directory */
2044 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2045 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2047 free(directory_path);
2051 free(directory_path);
2053 /* check if this directory contains artwork with or without config file */
2054 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2056 directory_name, type);
2061 /* check if this directory directly contains artwork itself */
2062 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2063 base_directory, ".",
2065 if (!valid_entry_found)
2066 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2070 static TreeInfo *getDummyArtworkInfo(int type)
2072 /* this is only needed when there is completely no artwork available */
2073 TreeInfo *artwork_new = newTreeInfo();
2075 setTreeInfoToDefaults(artwork_new, type);
2077 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2078 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2079 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2081 if (artwork_new->name != NULL)
2082 free(artwork_new->name);
2084 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2085 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2086 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2091 void LoadArtworkInfo()
2093 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2095 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2096 options.graphics_directory,
2097 TREE_TYPE_GRAPHICS_DIR);
2098 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2099 getUserGraphicsDir(),
2100 TREE_TYPE_GRAPHICS_DIR);
2102 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2103 options.sounds_directory,
2104 TREE_TYPE_SOUNDS_DIR);
2105 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2107 TREE_TYPE_SOUNDS_DIR);
2109 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2110 options.music_directory,
2111 TREE_TYPE_MUSIC_DIR);
2112 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2114 TREE_TYPE_MUSIC_DIR);
2116 if (artwork.gfx_first == NULL)
2117 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2118 if (artwork.snd_first == NULL)
2119 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2120 if (artwork.mus_first == NULL)
2121 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2123 /* before sorting, the first entries will be from the user directory */
2124 artwork.gfx_current =
2125 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2126 if (artwork.gfx_current == NULL)
2127 artwork.gfx_current =
2128 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2129 if (artwork.gfx_current == NULL)
2130 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2132 artwork.snd_current =
2133 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2134 if (artwork.snd_current == NULL)
2135 artwork.snd_current =
2136 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2137 if (artwork.snd_current == NULL)
2138 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2140 artwork.mus_current =
2141 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2142 if (artwork.mus_current == NULL)
2143 artwork.mus_current =
2144 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2145 if (artwork.mus_current == NULL)
2146 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2148 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2149 artwork.snd_current_identifier = artwork.snd_current->identifier;
2150 artwork.mus_current_identifier = artwork.mus_current->identifier;
2153 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2154 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2155 printf("music set == %s\n\n", artwork.mus_current_identifier);
2158 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2159 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2160 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2163 dumpTreeInfo(artwork.gfx_first, 0);
2164 dumpTreeInfo(artwork.snd_first, 0);
2165 dumpTreeInfo(artwork.mus_first, 0);
2169 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2170 LevelDirTree *level_node)
2172 /* recursively check all level directories for artwork sub-directories */
2176 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2177 ARTWORK_DIRECTORY((*artwork_node)->type));
2180 if (!level_node->parent_link)
2181 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2182 level_node->filename, level_node->name);
2185 if (!level_node->parent_link)
2187 TreeInfo *topnode_last = *artwork_node;
2189 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2190 (*artwork_node)->type);
2192 if (topnode_last != *artwork_node)
2194 free((*artwork_node)->identifier);
2195 free((*artwork_node)->name);
2196 free((*artwork_node)->name_sorting);
2198 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2199 (*artwork_node)->name = getStringCopy(level_node->name);
2200 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2202 (*artwork_node)->sort_priority = level_node->sort_priority;
2203 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2209 if (level_node->node_group != NULL)
2210 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2212 level_node = level_node->next;
2216 void LoadLevelArtworkInfo()
2218 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2220 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2221 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2222 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2224 /* needed for reloading level artwork not known at ealier stage */
2225 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2227 artwork.gfx_current =
2228 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2229 if (artwork.gfx_current == NULL)
2230 artwork.gfx_current =
2231 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2232 if (artwork.gfx_current == NULL)
2233 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2236 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2238 artwork.snd_current =
2239 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2240 if (artwork.snd_current == NULL)
2241 artwork.snd_current =
2242 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2243 if (artwork.snd_current == NULL)
2244 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2247 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2249 artwork.mus_current =
2250 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2251 if (artwork.mus_current == NULL)
2252 artwork.mus_current =
2253 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2254 if (artwork.mus_current == NULL)
2255 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2258 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2259 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2260 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2263 dumpTreeInfo(artwork.gfx_first, 0);
2264 dumpTreeInfo(artwork.snd_first, 0);
2265 dumpTreeInfo(artwork.mus_first, 0);
2269 static void SaveUserLevelInfo()
2275 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2277 if (!(file = fopen(filename, MODE_WRITE)))
2279 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2284 /* always start with reliable default values */
2285 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2287 ldi.name = getStringCopy(getLoginName());
2288 ldi.author = getStringCopy(getRealName());
2290 ldi.first_level = 1;
2291 ldi.sort_priority = LEVELCLASS_USER_START;
2292 ldi.readonly = FALSE;
2293 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2294 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2295 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2297 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2298 getCookie("LEVELINFO")));
2300 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2301 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2302 i != LEVELINFO_TOKEN_NAME_SORTING &&
2303 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2304 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2309 SetFilePermissions(filename, PERMS_PRIVATE);
2312 char *getSetupValue(int type, void *value)
2314 static char value_string[MAX_LINE_LEN];
2322 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2326 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2330 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2334 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2338 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2342 sprintf(value_string, "%d", *(int *)value);
2346 strcpy(value_string, *(char **)value);
2350 value_string[0] = '\0';
2354 return value_string;
2357 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2361 static char token_string[MAX_LINE_LEN];
2362 int token_type = token_info[token_nr].type;
2363 void *setup_value = token_info[token_nr].value;
2364 char *token_text = token_info[token_nr].text;
2365 char *value_string = getSetupValue(token_type, setup_value);
2367 /* build complete token string */
2368 sprintf(token_string, "%s%s", prefix, token_text);
2370 /* build setup entry line */
2371 line = getFormattedSetupEntry(token_string, value_string);
2373 if (token_type == TYPE_KEY_X11)
2375 Key key = *(Key *)setup_value;
2376 char *keyname = getKeyNameFromKey(key);
2378 /* add comment, if useful */
2379 if (strcmp(keyname, "(undefined)") != 0 &&
2380 strcmp(keyname, "(unknown)") != 0)
2382 /* add at least one whitespace */
2384 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2388 strcat(line, keyname);
2395 void LoadLevelSetup_LastSeries()
2398 SetupFileHash *level_setup_hash = NULL;
2400 /* always start with reliable default values */
2401 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2403 /* ----------------------------------------------------------------------- */
2404 /* ~/.<program>/levelsetup.conf */
2405 /* ----------------------------------------------------------------------- */
2407 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2409 if ((level_setup_hash = loadSetupFileHash(filename)))
2411 char *last_level_series =
2412 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2414 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2416 if (leveldir_current == NULL)
2417 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2419 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2421 freeSetupFileHash(level_setup_hash);
2424 Error(ERR_WARN, "using default setup values");
2429 void SaveLevelSetup_LastSeries()
2432 char *level_subdir = leveldir_current->filename;
2435 /* ----------------------------------------------------------------------- */
2436 /* ~/.<program>/levelsetup.conf */
2437 /* ----------------------------------------------------------------------- */
2439 InitUserDataDirectory();
2441 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2443 if (!(file = fopen(filename, MODE_WRITE)))
2445 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2450 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2451 getCookie("LEVELSETUP")));
2452 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2458 SetFilePermissions(filename, PERMS_PRIVATE);
2461 static void checkSeriesInfo()
2463 static char *level_directory = NULL;
2465 struct dirent *dir_entry;
2467 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2469 level_directory = getPath2((leveldir_current->user_defined ?
2470 getUserLevelDir(NULL) :
2471 options.level_directory),
2472 leveldir_current->fullpath);
2474 if ((dir = opendir(level_directory)) == NULL)
2476 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2480 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2482 if (strlen(dir_entry->d_name) > 4 &&
2483 dir_entry->d_name[3] == '.' &&
2484 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2486 char levelnum_str[4];
2489 strncpy(levelnum_str, dir_entry->d_name, 3);
2490 levelnum_str[3] = '\0';
2492 levelnum_value = atoi(levelnum_str);
2495 if (levelnum_value < leveldir_current->first_level)
2497 Error(ERR_WARN, "additional level %d found", levelnum_value);
2498 leveldir_current->first_level = levelnum_value;
2500 else if (levelnum_value > leveldir_current->last_level)
2502 Error(ERR_WARN, "additional level %d found", levelnum_value);
2503 leveldir_current->last_level = levelnum_value;
2512 void LoadLevelSetup_SeriesInfo()
2515 SetupFileHash *level_setup_hash = NULL;
2516 char *level_subdir = leveldir_current->filename;
2518 /* always start with reliable default values */
2519 level_nr = leveldir_current->first_level;
2521 checkSeriesInfo(leveldir_current);
2523 /* ----------------------------------------------------------------------- */
2524 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2525 /* ----------------------------------------------------------------------- */
2527 level_subdir = leveldir_current->filename;
2529 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2531 if ((level_setup_hash = loadSetupFileHash(filename)))
2535 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2539 level_nr = atoi(token_value);
2541 if (level_nr < leveldir_current->first_level)
2542 level_nr = leveldir_current->first_level;
2543 if (level_nr > leveldir_current->last_level)
2544 level_nr = leveldir_current->last_level;
2547 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2551 int level_nr = atoi(token_value);
2553 if (level_nr < leveldir_current->first_level)
2554 level_nr = leveldir_current->first_level;
2555 if (level_nr > leveldir_current->last_level + 1)
2556 level_nr = leveldir_current->last_level;
2558 if (leveldir_current->user_defined)
2559 level_nr = leveldir_current->last_level;
2561 leveldir_current->handicap_level = level_nr;
2564 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2566 freeSetupFileHash(level_setup_hash);
2569 Error(ERR_WARN, "using default setup values");
2574 void SaveLevelSetup_SeriesInfo()
2577 char *level_subdir = leveldir_current->filename;
2578 char *level_nr_str = int2str(level_nr, 0);
2579 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2582 /* ----------------------------------------------------------------------- */
2583 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2584 /* ----------------------------------------------------------------------- */
2586 InitLevelSetupDirectory(level_subdir);
2588 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2590 if (!(file = fopen(filename, MODE_WRITE)))
2592 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2597 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2598 getCookie("LEVELSETUP")));
2599 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2601 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2602 handicap_level_str));
2607 SetFilePermissions(filename, PERMS_PRIVATE);