1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
26 /* file names and filename extensions */
27 #if !defined(PLATFORM_MSDOS)
28 #define LEVELSETUP_DIRECTORY "levelsetup"
29 #define SETUP_FILENAME "setup.conf"
30 #define LEVELSETUP_FILENAME "levelsetup.conf"
31 #define LEVELINFO_FILENAME "levelinfo.conf"
32 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
33 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
34 #define MUSICINFO_FILENAME "musicinfo.conf"
35 #define LEVELFILE_EXTENSION "level"
36 #define TAPEFILE_EXTENSION "tape"
37 #define SCOREFILE_EXTENSION "score"
39 #define LEVELSETUP_DIRECTORY "lvlsetup"
40 #define SETUP_FILENAME "setup.cnf"
41 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
42 #define LEVELINFO_FILENAME "lvlinfo.cnf"
43 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
44 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
45 #define MUSICINFO_FILENAME "musinfo.cnf"
46 #define LEVELFILE_EXTENSION "lvl"
47 #define TAPEFILE_EXTENSION "tap"
48 #define SCOREFILE_EXTENSION "sco"
51 #define NUM_LEVELCLASS_DESC 8
52 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
64 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
65 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
66 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
67 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
68 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
69 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
70 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
71 IS_LEVELCLASS_USER(n) ? FC_RED : \
74 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
75 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
76 IS_LEVELCLASS_BD(n) ? 2 : \
77 IS_LEVELCLASS_EM(n) ? 3 : \
78 IS_LEVELCLASS_SP(n) ? 4 : \
79 IS_LEVELCLASS_DX(n) ? 5 : \
80 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
81 IS_LEVELCLASS_USER(n) ? 7 : \
84 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
85 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
86 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
87 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
90 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
91 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
92 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
93 IS_ARTWORKCLASS_USER(n) ? 3 : \
96 #define TOKEN_VALUE_POSITION 40
97 #define TOKEN_COMMENT_POSITION 60
99 #define MAX_COOKIE_LEN 256
101 #define ARTWORKINFO_FILENAME(type) ((type) == ARTWORK_TYPE_GRAPHICS ? \
102 GRAPHICSINFO_FILENAME : \
103 (type) == ARTWORK_TYPE_SOUNDS ? \
104 SOUNDSINFO_FILENAME : \
105 (type) == ARTWORK_TYPE_MUSIC ? \
106 MUSICINFO_FILENAME : "")
108 #define ARTWORK_DIRECTORY(type) ((type) == ARTWORK_TYPE_GRAPHICS ? \
109 GRAPHICS_DIRECTORY : \
110 (type) == ARTWORK_TYPE_SOUNDS ? \
112 (type) == ARTWORK_TYPE_MUSIC ? \
113 MUSIC_DIRECTORY : "")
115 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == ARTWORK_TYPE_GRAPHICS ? \
116 options.graphics_directory : \
117 (type) == ARTWORK_TYPE_SOUNDS ? \
118 options.sounds_directory : \
119 (type) == ARTWORK_TYPE_MUSIC ? \
120 options.music_directory : "")
123 /* ------------------------------------------------------------------------- */
125 /* ------------------------------------------------------------------------- */
127 static char *getLevelClassDescription(TreeInfo *ldi)
129 int position = ldi->sort_priority / 100;
131 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
132 return levelclass_desc[position];
134 return "Unknown Level Class";
137 static char *getUserLevelDir(char *level_subdir)
139 static char *userlevel_dir = NULL;
140 char *data_dir = getUserDataDir();
141 char *userlevel_subdir = LEVELS_DIRECTORY;
146 if (level_subdir != NULL)
147 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
149 userlevel_dir = getPath2(data_dir, userlevel_subdir);
151 return userlevel_dir;
154 static char *getTapeDir(char *level_subdir)
156 static char *tape_dir = NULL;
157 char *data_dir = getUserDataDir();
158 char *tape_subdir = TAPES_DIRECTORY;
163 if (level_subdir != NULL)
164 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
166 tape_dir = getPath2(data_dir, tape_subdir);
171 static char *getScoreDir(char *level_subdir)
173 static char *score_dir = NULL;
174 char *data_dir = getCommonDataDir();
175 char *score_subdir = SCORES_DIRECTORY;
180 if (level_subdir != NULL)
181 score_dir = getPath3(data_dir, score_subdir, level_subdir);
183 score_dir = getPath2(data_dir, score_subdir);
188 static char *getLevelSetupDir(char *level_subdir)
190 static char *levelsetup_dir = NULL;
191 char *data_dir = getUserDataDir();
192 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
195 free(levelsetup_dir);
197 if (level_subdir != NULL)
198 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
200 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
202 return levelsetup_dir;
205 static char *getLevelDirFromTreeInfo(TreeInfo *node)
207 static char *level_dir = NULL;
210 return options.level_directory;
215 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
216 options.level_directory), node->fullpath);
221 static char *getCurrentLevelDir()
223 return getLevelDirFromTreeInfo(leveldir_current);
226 static char *getDefaultGraphicsDir(char *graphics_subdir)
228 static char *graphics_dir = NULL;
230 if (graphics_subdir == NULL)
231 return options.graphics_directory;
236 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
241 static char *getDefaultSoundsDir(char *sounds_subdir)
243 static char *sounds_dir = NULL;
245 if (sounds_subdir == NULL)
246 return options.sounds_directory;
251 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
256 static char *getDefaultMusicDir(char *music_subdir)
258 static char *music_dir = NULL;
260 if (music_subdir == NULL)
261 return options.music_directory;
266 music_dir = getPath2(options.music_directory, music_subdir);
271 static char *getDefaultArtworkSet(int type)
273 return (type == TREE_TYPE_GRAPHICS_DIR ? GRAPHICS_SUBDIR :
274 type == TREE_TYPE_SOUNDS_DIR ? SOUNDS_SUBDIR :
275 type == TREE_TYPE_MUSIC_DIR ? MUSIC_SUBDIR : "");
278 static char *getDefaultArtworkDir(int type)
280 return (type == TREE_TYPE_GRAPHICS_DIR ?
281 getDefaultGraphicsDir(GRAPHICS_SUBDIR) :
282 type == TREE_TYPE_SOUNDS_DIR ?
283 getDefaultSoundsDir(SOUNDS_SUBDIR) :
284 type == TREE_TYPE_MUSIC_DIR ?
285 getDefaultMusicDir(MUSIC_SUBDIR) : "");
288 static char *getUserGraphicsDir()
290 static char *usergraphics_dir = NULL;
292 if (usergraphics_dir == NULL)
293 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
295 return usergraphics_dir;
298 static char *getUserSoundsDir()
300 static char *usersounds_dir = NULL;
302 if (usersounds_dir == NULL)
303 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
305 return usersounds_dir;
308 static char *getUserMusicDir()
310 static char *usermusic_dir = NULL;
312 if (usermusic_dir == NULL)
313 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
315 return usermusic_dir;
318 static char *getSetupArtworkDir(TreeInfo *ti)
320 static char *artwork_dir = NULL;
322 if (artwork_dir != NULL)
325 artwork_dir = getPath2(ti->basepath, ti->fullpath);
330 void setLevelArtworkDir(TreeInfo *ti)
332 char **artwork_path_ptr, **artwork_set_ptr;
333 TreeInfo *level_artwork;
335 TreeInfo *tst1 = getTreeInfoFromIdentifier(leveldir_first, "jue1");
336 printf("::: XXX 0.1 '%s'\n", tst1->graphics_path);
338 if (ti == NULL || leveldir_current == NULL)
341 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
342 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
344 printf("::: ['%s', '%s']\n", tst1->identifier, leveldir_current->identifier);
346 printf("::: XXX 0.2 '%s' [%x]\n", tst1->graphics_path, tst1->graphics_path);
349 if (*artwork_path_ptr != NULL)
352 printf("::: free'ing '%s' [%x] [%x] [type %d] ...\n",
353 *artwork_path_ptr, *artwork_path_ptr,
354 leveldir_current->graphics_path, ti->type);
356 free(*artwork_path_ptr);
360 printf("::: XXX 0.3 '%s' [%x]\n", tst1->graphics_path, tst1->graphics_path);
362 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
364 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
366 printf(":1: setting to '%s' [type %d] ...\n", *artwork_path_ptr, ti->type);
370 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
371 normally result in using the artwork configured in the setup menu. But
372 if an artwork subdirectory exists (which might contain custom artwork
373 or an artwork configuration file), this level artwork must be treated
374 as relative to the default "classic" artwork, not to the artwork that
375 is currently configured in the setup menu. */
377 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
379 if (*artwork_set_ptr != NULL)
380 free(*artwork_set_ptr);
384 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
385 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
388 printf(":2: setting to '%s' [type %d] ...\n",
389 *artwork_path_ptr, ti->type);
393 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
394 *artwork_set_ptr = NULL;
397 printf(":3: setting to '%s' [type %d] ...\n",
398 *artwork_path_ptr, ti->type);
405 inline static char *getLevelArtworkSet(int type)
407 if (leveldir_current == NULL)
410 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
413 inline static char *getLevelArtworkDir(int type)
415 if (leveldir_current == NULL)
416 return UNDEFINED_FILENAME;
418 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
421 char *getLevelFilename(int nr)
423 static char *filename = NULL;
424 char basename[MAX_FILENAME_LEN];
426 if (filename != NULL)
430 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
432 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
434 filename = getPath2(getCurrentLevelDir(), basename);
439 char *getTapeFilename(int nr)
441 static char *filename = NULL;
442 char basename[MAX_FILENAME_LEN];
444 if (filename != NULL)
447 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
448 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
453 char *getScoreFilename(int nr)
455 static char *filename = NULL;
456 char basename[MAX_FILENAME_LEN];
458 if (filename != NULL)
461 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
462 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
467 char *getSetupFilename()
469 static char *filename = NULL;
471 if (filename != NULL)
474 filename = getPath2(getSetupDir(), SETUP_FILENAME);
479 static char *getCorrectedImageBasename(char *basename)
481 char *basename_corrected = basename;
483 #if defined(PLATFORM_MSDOS)
484 if (program.filename_prefix != NULL)
486 int prefix_len = strlen(program.filename_prefix);
488 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
489 basename_corrected = &basename[prefix_len];
491 /* if corrected filename is still longer than standard MS-DOS filename
492 size (8 characters + 1 dot + 3 characters file extension), shorten
493 filename by writing file extension after 8th basename character */
494 if (strlen(basename_corrected) > 8+1+3)
496 static char *msdos_filename = NULL;
498 if (msdos_filename != NULL)
499 free(msdos_filename);
501 msdos_filename = getStringCopy(basename_corrected);
502 strncpy(&msdos_filename[8], &basename[strlen(basename) - 1+3], 1+3 + 1);
507 return basename_corrected;
510 char *getCustomImageFilename(char *basename)
512 static char *filename = NULL;
513 boolean skip_setup_artwork = FALSE;
515 if (filename != NULL)
518 basename = getCorrectedImageBasename(basename);
520 if (!setup.override_level_graphics)
522 /* 1st try: look for special artwork in current level series directory */
523 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
524 if (fileExists(filename))
529 /* check if there is special artwork configured in level series config */
530 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
532 /* 2nd try: look for special artwork configured in level series config */
533 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
534 if (fileExists(filename))
539 /* take missing artwork configured in level set config from default */
540 skip_setup_artwork = TRUE;
544 if (!skip_setup_artwork)
546 /* 3rd try: look for special artwork in configured artwork directory */
547 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
548 if (fileExists(filename))
554 /* 4th try: look for default artwork in new default artwork directory */
555 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
556 if (fileExists(filename))
561 /* 5th try: look for default artwork in old default artwork directory */
562 filename = getPath2(options.graphics_directory, basename);
563 if (fileExists(filename))
566 return NULL; /* cannot find specified artwork file anywhere */
569 char *getCustomSoundFilename(char *basename)
571 static char *filename = NULL;
572 boolean skip_setup_artwork = FALSE;
574 if (filename != NULL)
577 if (!setup.override_level_sounds)
579 /* 1st try: look for special artwork in current level series directory */
580 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
581 if (fileExists(filename))
586 /* check if there is special artwork configured in level series config */
587 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
589 /* 2nd try: look for special artwork configured in level series config */
590 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
591 if (fileExists(filename))
596 /* take missing artwork configured in level set config from default */
597 skip_setup_artwork = TRUE;
601 if (!skip_setup_artwork)
603 /* 3rd try: look for special artwork in configured artwork directory */
604 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
605 if (fileExists(filename))
611 /* 4th try: look for default artwork in new default artwork directory */
612 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
613 if (fileExists(filename))
618 /* 5th try: look for default artwork in old default artwork directory */
619 filename = getPath2(options.sounds_directory, basename);
620 if (fileExists(filename))
623 return NULL; /* cannot find specified artwork file anywhere */
626 char *getCustomArtworkFilename(char *basename, int type)
628 if (type == ARTWORK_TYPE_GRAPHICS)
629 return getCustomImageFilename(basename);
630 else if (type == ARTWORK_TYPE_SOUNDS)
631 return getCustomSoundFilename(basename);
633 return UNDEFINED_FILENAME;
636 char *getCustomArtworkConfigFilename(int type)
638 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
641 char *getCustomArtworkLevelConfigFilename(int type)
643 static char *filename = NULL;
645 if (filename != NULL)
648 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
653 char *getCustomMusicDirectory(void)
655 static char *directory = NULL;
656 boolean skip_setup_artwork = FALSE;
658 if (directory != NULL)
661 if (!setup.override_level_music)
663 /* 1st try: look for special artwork in current level series directory */
664 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
665 if (fileExists(directory))
670 /* check if there is special artwork configured in level series config */
671 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
673 /* 2nd try: look for special artwork configured in level series config */
674 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
675 if (fileExists(directory))
680 /* take missing artwork configured in level set config from default */
681 skip_setup_artwork = TRUE;
685 if (!skip_setup_artwork)
687 /* 3rd try: look for special artwork in configured artwork directory */
688 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
689 if (fileExists(directory))
695 /* 4th try: look for default artwork in new default artwork directory */
696 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
697 if (fileExists(directory))
702 /* 5th try: look for default artwork in old default artwork directory */
703 directory = getStringCopy(options.music_directory);
704 if (fileExists(directory))
707 return NULL; /* cannot find specified artwork file anywhere */
710 void InitTapeDirectory(char *level_subdir)
712 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
713 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
714 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
717 void InitScoreDirectory(char *level_subdir)
719 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
720 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
721 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
724 static void SaveUserLevelInfo();
726 void InitUserLevelDirectory(char *level_subdir)
728 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
730 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
731 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
732 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
738 void InitLevelSetupDirectory(char *level_subdir)
740 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
741 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
742 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
746 /* ------------------------------------------------------------------------- */
747 /* some functions to handle lists of level directories */
748 /* ------------------------------------------------------------------------- */
750 TreeInfo *newTreeInfo()
752 return checked_calloc(sizeof(TreeInfo));
755 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
757 node_new->next = *node_first;
758 *node_first = node_new;
761 int numTreeInfo(TreeInfo *node)
774 boolean validLevelSeries(TreeInfo *node)
776 return (node != NULL && !node->node_group && !node->parent_link);
779 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
784 if (node->node_group) /* enter level group (step down into tree) */
785 return getFirstValidTreeInfoEntry(node->node_group);
786 else if (node->parent_link) /* skip start entry of level group */
788 if (node->next) /* get first real level series entry */
789 return getFirstValidTreeInfoEntry(node->next);
790 else /* leave empty level group and go on */
791 return getFirstValidTreeInfoEntry(node->node_parent->next);
793 else /* this seems to be a regular level series */
797 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
802 if (node->node_parent == NULL) /* top level group */
803 return *node->node_top;
804 else /* sub level group */
805 return node->node_parent->node_group;
808 int numTreeInfoInGroup(TreeInfo *node)
810 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
813 int posTreeInfo(TreeInfo *node)
815 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
820 if (node_cmp == node)
824 node_cmp = node_cmp->next;
830 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
832 TreeInfo *node_default = node;
847 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
849 if (identifier == NULL)
854 if (node->node_group)
856 TreeInfo *node_group;
858 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
863 else if (!node->parent_link)
865 if (strcmp(identifier, node->identifier) == 0)
875 void dumpTreeInfo(TreeInfo *node, int depth)
879 printf("Dumping TreeInfo:\n");
883 for (i=0; i<(depth + 1) * 3; i++)
886 printf("filename == '%s' (%s) [%s] (%d)\n",
887 node->filename, node->name, node->identifier, node->sort_priority);
889 if (node->node_group != NULL)
890 dumpTreeInfo(node->node_group, depth + 1);
896 void sortTreeInfo(TreeInfo **node_first,
897 int (*compare_function)(const void *, const void *))
899 int num_nodes = numTreeInfo(*node_first);
900 TreeInfo **sort_array;
901 TreeInfo *node = *node_first;
907 /* allocate array for sorting structure pointers */
908 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
910 /* writing structure pointers to sorting array */
911 while (i < num_nodes && node) /* double boundary check... */
913 sort_array[i] = node;
919 /* sorting the structure pointers in the sorting array */
920 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
923 /* update the linkage of list elements with the sorted node array */
924 for (i=0; i<num_nodes - 1; i++)
925 sort_array[i]->next = sort_array[i + 1];
926 sort_array[num_nodes - 1]->next = NULL;
928 /* update the linkage of the main list anchor pointer */
929 *node_first = sort_array[0];
933 /* now recursively sort the level group structures */
937 if (node->node_group != NULL)
938 sortTreeInfo(&node->node_group, compare_function);
945 /* ========================================================================= */
946 /* some stuff from "files.c" */
947 /* ========================================================================= */
949 #if defined(PLATFORM_WIN32)
951 #define S_IRGRP S_IRUSR
954 #define S_IROTH S_IRUSR
957 #define S_IWGRP S_IWUSR
960 #define S_IWOTH S_IWUSR
963 #define S_IXGRP S_IXUSR
966 #define S_IXOTH S_IXUSR
969 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
974 #endif /* PLATFORM_WIN32 */
976 /* file permissions for newly written files */
977 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
978 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
979 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
981 #define MODE_W_PRIVATE (S_IWUSR)
982 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
983 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
985 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
986 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
988 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
989 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
991 char *getUserDataDir(void)
993 static char *userdata_dir = NULL;
995 if (userdata_dir == NULL)
996 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1001 char *getCommonDataDir(void)
1003 static char *common_data_dir = NULL;
1005 #if defined(PLATFORM_WIN32)
1006 if (common_data_dir == NULL)
1008 char *dir = checked_malloc(MAX_PATH + 1);
1010 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1011 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1012 common_data_dir = getPath2(dir, program.userdata_directory);
1014 common_data_dir = options.rw_base_directory;
1017 if (common_data_dir == NULL)
1018 common_data_dir = options.rw_base_directory;
1021 return common_data_dir;
1026 return getUserDataDir();
1029 static mode_t posix_umask(mode_t mask)
1031 #if defined(PLATFORM_UNIX)
1038 static int posix_mkdir(const char *pathname, mode_t mode)
1040 #if defined(PLATFORM_WIN32)
1041 return mkdir(pathname);
1043 return mkdir(pathname, mode);
1047 void createDirectory(char *dir, char *text, int permission_class)
1049 /* leave "other" permissions in umask untouched, but ensure group parts
1050 of USERDATA_DIR_MODE are not masked */
1051 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1052 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1053 mode_t normal_umask = posix_umask(0);
1054 mode_t group_umask = ~(dir_mode & S_IRWXG);
1055 posix_umask(normal_umask & group_umask);
1057 if (access(dir, F_OK) != 0)
1058 if (posix_mkdir(dir, dir_mode) != 0)
1059 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1061 posix_umask(normal_umask); /* reset normal umask */
1064 void InitUserDataDirectory()
1066 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1069 void SetFilePermissions(char *filename, int permission_class)
1071 chmod(filename, (permission_class == PERMS_PRIVATE ?
1072 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1075 char *getCookie(char *file_type)
1077 static char cookie[MAX_COOKIE_LEN + 1];
1079 if (strlen(program.cookie_prefix) + 1 +
1080 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1081 return "[COOKIE ERROR]"; /* should never happen */
1083 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1084 program.cookie_prefix, file_type,
1085 program.version_major, program.version_minor);
1090 int getFileVersionFromCookieString(const char *cookie)
1092 const char *ptr_cookie1, *ptr_cookie2;
1093 const char *pattern1 = "_FILE_VERSION_";
1094 const char *pattern2 = "?.?";
1095 const int len_cookie = strlen(cookie);
1096 const int len_pattern1 = strlen(pattern1);
1097 const int len_pattern2 = strlen(pattern2);
1098 const int len_pattern = len_pattern1 + len_pattern2;
1099 int version_major, version_minor;
1101 if (len_cookie <= len_pattern)
1104 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1105 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1107 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1110 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1111 ptr_cookie2[1] != '.' ||
1112 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1115 version_major = ptr_cookie2[0] - '0';
1116 version_minor = ptr_cookie2[2] - '0';
1118 return VERSION_IDENT(version_major, version_minor, 0);
1121 boolean checkCookieString(const char *cookie, const char *template)
1123 const char *pattern = "_FILE_VERSION_?.?";
1124 const int len_cookie = strlen(cookie);
1125 const int len_template = strlen(template);
1126 const int len_pattern = strlen(pattern);
1128 if (len_cookie != len_template)
1131 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1137 /* ------------------------------------------------------------------------- */
1138 /* setup file list and hash handling functions */
1139 /* ------------------------------------------------------------------------- */
1141 char *getFormattedSetupEntry(char *token, char *value)
1144 static char entry[MAX_LINE_LEN];
1146 /* start with the token and some spaces to format output line */
1147 sprintf(entry, "%s:", token);
1148 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1151 /* continue with the token's value */
1152 strcat(entry, value);
1157 SetupFileList *newSetupFileList(char *token, char *value)
1159 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1161 new->token = getStringCopy(token);
1162 new->value = getStringCopy(value);
1169 void freeSetupFileList(SetupFileList *list)
1179 freeSetupFileList(list->next);
1183 char *getListEntry(SetupFileList *list, char *token)
1188 if (strcmp(list->token, token) == 0)
1191 return getListEntry(list->next, token);
1194 void setListEntry(SetupFileList *list, char *token, char *value)
1199 if (strcmp(list->token, token) == 0)
1204 list->value = getStringCopy(value);
1206 else if (list->next == NULL)
1207 list->next = newSetupFileList(token, value);
1209 setListEntry(list->next, token, value);
1213 static void printSetupFileList(SetupFileList *list)
1218 printf("token: '%s'\n", list->token);
1219 printf("value: '%s'\n", list->value);
1221 printSetupFileList(list->next);
1226 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1227 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1228 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1229 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1231 #define insert_hash_entry hashtable_insert
1232 #define search_hash_entry hashtable_search
1233 #define change_hash_entry hashtable_change
1234 #define remove_hash_entry hashtable_remove
1237 static unsigned int get_hash_from_key(void *key)
1242 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1243 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1244 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1245 it works better than many other constants, prime or not) has never been
1246 adequately explained.
1248 If you just want to have a good hash function, and cannot wait, djb2
1249 is one of the best string hash functions i know. It has excellent
1250 distribution and speed on many different sets of keys and table sizes.
1251 You are not likely to do better with one of the "well known" functions
1252 such as PJW, K&R, etc.
1254 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1257 char *str = (char *)key;
1258 unsigned int hash = 5381;
1261 while ((c = *str++))
1262 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1267 static int keys_are_equal(void *key1, void *key2)
1269 return (strcmp((char *)key1, (char *)key2) == 0);
1272 SetupFileHash *newSetupFileHash()
1274 SetupFileHash *new_hash =
1275 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1280 void freeSetupFileHash(SetupFileHash *hash)
1285 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1288 char *getHashEntry(SetupFileHash *hash, char *token)
1293 return search_hash_entry(hash, token);
1296 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1303 value_copy = getStringCopy(value);
1305 /* change value; if it does not exist, insert it as new */
1306 if (!change_hash_entry(hash, token, value_copy))
1307 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1308 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1313 static void printSetupFileHash(SetupFileHash *hash)
1315 BEGIN_HASH_ITERATION(hash, itr)
1317 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1318 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1320 END_HASH_ITERATION(hash, itr)
1325 static void *loadSetupFileData(char *filename, boolean use_hash)
1328 char line[MAX_LINE_LEN];
1329 char *token, *value, *line_ptr;
1330 void *setup_file_data;
1334 setup_file_data = newSetupFileHash();
1336 setup_file_data = newSetupFileList("", "");
1338 if (!(file = fopen(filename, MODE_READ)))
1340 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1346 /* read next line of input file */
1347 if (!fgets(line, MAX_LINE_LEN, file))
1350 /* cut trailing comment or whitespace from input line */
1351 for (line_ptr = line; *line_ptr; line_ptr++)
1353 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1360 /* cut trailing whitespaces from input line */
1361 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1362 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1365 /* ignore empty lines */
1369 line_len = strlen(line);
1371 /* cut leading whitespaces from token */
1372 for (token = line; *token; token++)
1373 if (*token != ' ' && *token != '\t')
1376 /* find end of token */
1377 for (line_ptr = token; *line_ptr; line_ptr++)
1379 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1386 if (line_ptr < line + line_len)
1387 value = line_ptr + 1;
1391 /* cut leading whitespaces from value */
1392 for (; *value; value++)
1393 if (*value != ' ' && *value != '\t')
1396 if (*token && *value)
1399 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1401 setListEntry((SetupFileList *)setup_file_data, token, value);
1409 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1410 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1414 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1415 SetupFileList *first_valid_list_entry = setup_file_list->next;
1417 /* free empty list header */
1418 setup_file_list->next = NULL;
1419 freeSetupFileList(setup_file_list);
1420 setup_file_data = first_valid_list_entry;
1422 if (first_valid_list_entry == NULL)
1423 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1426 return setup_file_data;
1429 SetupFileList *loadSetupFileList(char *filename)
1431 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1434 SetupFileHash *loadSetupFileHash(char *filename)
1436 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1439 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1442 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1445 Error(ERR_WARN, "configuration file has no file identifier");
1446 else if (!checkCookieString(value, identifier))
1447 Error(ERR_WARN, "configuration file has wrong file identifier");
1451 /* ========================================================================= */
1452 /* setup file stuff */
1453 /* ========================================================================= */
1455 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1456 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1457 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1459 /* level directory info */
1460 #define LEVELINFO_TOKEN_IDENTIFIER 0
1461 #define LEVELINFO_TOKEN_NAME 1
1462 #define LEVELINFO_TOKEN_NAME_SORTING 2
1463 #define LEVELINFO_TOKEN_AUTHOR 3
1464 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1465 #define LEVELINFO_TOKEN_LEVELS 5
1466 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1467 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1468 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1469 #define LEVELINFO_TOKEN_READONLY 9
1470 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1471 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1472 #define LEVELINFO_TOKEN_MUSIC_SET 12
1474 #define NUM_LEVELINFO_TOKENS 13
1476 static LevelDirTree ldi;
1478 static struct TokenInfo levelinfo_tokens[] =
1480 /* level directory info */
1481 { TYPE_STRING, &ldi.identifier, "identifier" },
1482 { TYPE_STRING, &ldi.name, "name" },
1483 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1484 { TYPE_STRING, &ldi.author, "author" },
1485 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1486 { TYPE_INTEGER, &ldi.levels, "levels" },
1487 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1488 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1489 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1490 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1491 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1492 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1493 { TYPE_STRING, &ldi.music_set, "music_set" }
1496 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1500 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1501 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1502 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1503 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1506 ldi->node_parent = NULL;
1507 ldi->node_group = NULL;
1511 ldi->cl_cursor = -1;
1513 ldi->filename = NULL;
1514 ldi->fullpath = NULL;
1515 ldi->basepath = NULL;
1516 ldi->identifier = NULL;
1517 ldi->name = getStringCopy(ANONYMOUS_NAME);
1518 ldi->name_sorting = NULL;
1519 ldi->author = getStringCopy(ANONYMOUS_NAME);
1521 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1522 ldi->parent_link = FALSE;
1523 ldi->user_defined = FALSE;
1525 ldi->class_desc = NULL;
1527 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1529 ldi->imported_from = NULL;
1531 ldi->graphics_set = NULL;
1532 ldi->sounds_set = NULL;
1533 ldi->music_set = NULL;
1534 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1535 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1536 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1539 ldi->first_level = 0;
1540 ldi->last_level = 0;
1541 ldi->level_group = FALSE;
1542 ldi->handicap_level = 0;
1543 ldi->readonly = TRUE;
1547 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1551 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1553 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1559 /* copy all values from the parent structure */
1561 ldi->type = parent->type;
1563 ldi->node_top = parent->node_top;
1564 ldi->node_parent = parent;
1565 ldi->node_group = NULL;
1569 ldi->cl_cursor = -1;
1571 ldi->filename = NULL;
1572 ldi->fullpath = NULL;
1573 ldi->basepath = NULL;
1574 ldi->identifier = NULL;
1575 ldi->name = getStringCopy(ANONYMOUS_NAME);
1576 ldi->name_sorting = NULL;
1577 ldi->author = getStringCopy(parent->author);
1579 ldi->sort_priority = parent->sort_priority;
1580 ldi->parent_link = FALSE;
1581 ldi->user_defined = parent->user_defined;
1582 ldi->color = parent->color;
1583 ldi->class_desc = getStringCopy(parent->class_desc);
1585 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1587 ldi->imported_from = getStringCopy(parent->imported_from);
1589 ldi->graphics_set = NULL;
1590 ldi->sounds_set = NULL;
1591 ldi->music_set = NULL;
1592 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1593 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1594 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1597 ldi->first_level = 0;
1598 ldi->last_level = 0;
1599 ldi->level_group = FALSE;
1600 ldi->handicap_level = 0;
1601 ldi->readonly = TRUE;
1607 /* first copy all values from the parent structure ... */
1610 /* ... then set all fields to default that cannot be inherited from parent.
1611 This is especially important for all those fields that can be set from
1612 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1613 calls 'free()' for all already set token values which requires that no
1614 other structure's pointer may point to them!
1617 ldi->filename = NULL;
1618 ldi->fullpath = NULL;
1619 ldi->basepath = NULL;
1620 ldi->identifier = NULL;
1621 ldi->name = getStringCopy(ANONYMOUS_NAME);
1622 ldi->name_sorting = NULL;
1623 ldi->author = getStringCopy(parent->author);
1625 ldi->imported_from = getStringCopy(parent->imported_from);
1626 ldi->class_desc = getStringCopy(parent->class_desc);
1628 ldi->graphics_set = NULL;
1629 ldi->sounds_set = NULL;
1630 ldi->music_set = NULL;
1631 ldi->graphics_path = NULL;
1632 ldi->sounds_path = NULL;
1633 ldi->music_path = NULL;
1635 ldi->level_group = FALSE;
1636 ldi->parent_link = FALSE;
1638 ldi->node_top = parent->node_top;
1639 ldi->node_parent = parent;
1640 ldi->node_group = NULL;
1646 void setSetupInfo(struct TokenInfo *token_info,
1647 int token_nr, char *token_value)
1649 int token_type = token_info[token_nr].type;
1650 void *setup_value = token_info[token_nr].value;
1652 if (token_value == NULL)
1655 /* set setup field to corresponding token value */
1660 *(boolean *)setup_value = get_boolean_from_string(token_value);
1664 *(Key *)setup_value = getKeyFromKeyName(token_value);
1668 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1672 *(int *)setup_value = get_integer_from_string(token_value);
1676 if (*(char **)setup_value != NULL)
1677 free(*(char **)setup_value);
1678 *(char **)setup_value = getStringCopy(token_value);
1686 static int compareTreeInfoEntries(const void *object1, const void *object2)
1688 const TreeInfo *entry1 = *((TreeInfo **)object1);
1689 const TreeInfo *entry2 = *((TreeInfo **)object2);
1690 int class_sorting1, class_sorting2;
1693 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1695 class_sorting1 = LEVELSORTING(entry1);
1696 class_sorting2 = LEVELSORTING(entry2);
1700 class_sorting1 = ARTWORKSORTING(entry1);
1701 class_sorting2 = ARTWORKSORTING(entry2);
1704 if (entry1->parent_link || entry2->parent_link)
1705 compare_result = (entry1->parent_link ? -1 : +1);
1706 else if (entry1->sort_priority == entry2->sort_priority)
1708 char *name1 = getStringToLower(entry1->name_sorting);
1709 char *name2 = getStringToLower(entry2->name_sorting);
1711 compare_result = strcmp(name1, name2);
1716 else if (class_sorting1 == class_sorting2)
1717 compare_result = entry1->sort_priority - entry2->sort_priority;
1719 compare_result = class_sorting1 - class_sorting2;
1721 return compare_result;
1724 static void createParentTreeInfoNode(TreeInfo *node_parent)
1728 if (node_parent == NULL)
1731 ti_new = newTreeInfo();
1732 setTreeInfoToDefaults(ti_new, node_parent->type);
1734 ti_new->node_parent = node_parent;
1735 ti_new->parent_link = TRUE;
1737 ti_new->identifier = getStringCopy(node_parent->identifier);
1738 ti_new->name = ".. (parent directory)";
1739 ti_new->name_sorting = getStringCopy(ti_new->name);
1741 ti_new->filename = "..";
1742 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1744 ti_new->sort_priority = node_parent->sort_priority;
1745 ti_new->class_desc = getLevelClassDescription(ti_new);
1747 pushTreeInfo(&node_parent->node_group, ti_new);
1750 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1751 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1753 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1754 TreeInfo *node_parent,
1755 char *level_directory,
1756 char *directory_name)
1758 char *directory_path = getPath2(level_directory, directory_name);
1759 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1760 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1761 LevelDirTree *leveldir_new = NULL;
1764 if (setup_file_hash == NULL)
1766 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1768 free(directory_path);
1774 leveldir_new = newTreeInfo();
1777 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1779 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1781 leveldir_new->filename = getStringCopy(directory_name);
1783 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1785 /* set all structure fields according to the token/value pairs */
1786 ldi = *leveldir_new;
1787 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1788 setSetupInfo(levelinfo_tokens, i,
1789 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1790 *leveldir_new = ldi;
1792 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1794 free(leveldir_new->name);
1795 leveldir_new->name = getStringCopy(leveldir_new->filename);
1798 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1800 if (leveldir_new->identifier == NULL)
1801 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1803 if (leveldir_new->name_sorting == NULL)
1804 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1806 if (node_parent == NULL) /* top level group */
1808 leveldir_new->basepath = level_directory;
1809 leveldir_new->fullpath = leveldir_new->filename;
1811 else /* sub level group */
1813 leveldir_new->basepath = node_parent->basepath;
1814 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1817 if (leveldir_new->levels < 1)
1818 leveldir_new->levels = 1;
1820 leveldir_new->last_level =
1821 leveldir_new->first_level + leveldir_new->levels - 1;
1823 leveldir_new->user_defined =
1824 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1826 leveldir_new->color = LEVELCOLOR(leveldir_new);
1827 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1829 leveldir_new->handicap_level = /* set handicap to default value */
1830 (leveldir_new->user_defined ?
1831 leveldir_new->last_level :
1832 leveldir_new->first_level);
1834 pushTreeInfo(node_first, leveldir_new);
1836 freeSetupFileHash(setup_file_hash);
1838 if (leveldir_new->level_group)
1840 /* create node to link back to current level directory */
1841 createParentTreeInfoNode(leveldir_new);
1843 /* step into sub-directory and look for more level series */
1844 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1845 leveldir_new, directory_path);
1848 free(directory_path);
1854 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1855 TreeInfo *node_parent,
1856 char *level_directory)
1859 struct dirent *dir_entry;
1860 boolean valid_entry_found = FALSE;
1862 if ((dir = opendir(level_directory)) == NULL)
1864 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1868 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1870 struct stat file_status;
1871 char *directory_name = dir_entry->d_name;
1872 char *directory_path = getPath2(level_directory, directory_name);
1874 /* skip entries for current and parent directory */
1875 if (strcmp(directory_name, ".") == 0 ||
1876 strcmp(directory_name, "..") == 0)
1878 free(directory_path);
1882 /* find out if directory entry is itself a directory */
1883 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1884 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1886 free(directory_path);
1890 free(directory_path);
1892 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1893 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1894 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1897 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1904 if (!valid_entry_found)
1906 /* check if this directory directly contains a file "levelinfo.conf" */
1907 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1908 level_directory, ".");
1911 if (!valid_entry_found)
1912 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1916 void LoadLevelInfo()
1918 InitUserLevelDirectory(getLoginName());
1920 DrawInitText("Loading level series:", 120, FC_GREEN);
1922 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1923 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1926 TreeInfo *tst1 = getTreeInfoFromIdentifier(leveldir_first, "jue1");
1927 TreeInfo *tst2 = getTreeInfoFromIdentifier(leveldir_first, "demojue");
1928 printf("::: ??? 1 '%s' [%x, %x]\n", tst1->graphics_path,
1929 tst1->graphics_path, tst2->graphics_path);
1932 /* before sorting, the first entries will be from the user directory */
1933 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1935 if (leveldir_first == NULL)
1936 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1938 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1941 dumpTreeInfo(leveldir_first, 0);
1945 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1946 TreeInfo *node_parent,
1947 char *base_directory,
1948 char *directory_name, int type)
1950 char *directory_path = getPath2(base_directory, directory_name);
1951 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1952 SetupFileHash *setup_file_hash = NULL;
1953 TreeInfo *artwork_new = NULL;
1956 if (access(filename, F_OK) == 0) /* file exists */
1957 setup_file_hash = loadSetupFileHash(filename);
1959 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
1962 struct dirent *dir_entry;
1963 boolean valid_file_found = FALSE;
1965 if ((dir = opendir(directory_path)) != NULL)
1967 while ((dir_entry = readdir(dir)) != NULL)
1969 char *entry_name = dir_entry->d_name;
1971 if (FileIsArtworkType(entry_name, type))
1973 valid_file_found = TRUE;
1981 if (!valid_file_found)
1983 if (strcmp(directory_name, ".") != 0)
1984 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1986 free(directory_path);
1993 artwork_new = newTreeInfo();
1996 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1998 setTreeInfoToDefaults(artwork_new, type);
2000 artwork_new->filename = getStringCopy(directory_name);
2002 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2005 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2008 /* set all structure fields according to the token/value pairs */
2010 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2011 setSetupInfo(levelinfo_tokens, i,
2012 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2015 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2017 free(artwork_new->name);
2018 artwork_new->name = getStringCopy(artwork_new->filename);
2022 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2025 if (artwork_new->identifier == NULL)
2026 artwork_new->identifier = getStringCopy(artwork_new->filename);
2028 if (artwork_new->name_sorting == NULL)
2029 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2032 if (node_parent == NULL) /* top level group */
2034 artwork_new->basepath = getStringCopy(base_directory);
2035 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2037 else /* sub level group */
2039 artwork_new->basepath = getStringCopy(node_parent->basepath);
2040 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2043 artwork_new->user_defined =
2044 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2046 /* (may use ".sort_priority" from "setup_file_hash" above) */
2047 artwork_new->color = ARTWORKCOLOR(artwork_new);
2048 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2050 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2052 if (artwork_new->name != NULL)
2053 free(artwork_new->name);
2055 if (strcmp(artwork_new->filename, ".") == 0)
2057 if (artwork_new->user_defined)
2059 artwork_new->identifier = getStringCopy("private");
2060 artwork_new->sort_priority = ARTWORKCLASS_USER;
2064 artwork_new->identifier = getStringCopy("classic");
2065 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2068 /* set to new values after changing ".sort_priority" */
2069 artwork_new->color = ARTWORKCOLOR(artwork_new);
2070 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2074 artwork_new->identifier = getStringCopy(artwork_new->filename);
2077 artwork_new->name = getStringCopy(artwork_new->identifier);
2078 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2081 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2083 pushTreeInfo(node_first, artwork_new);
2085 freeSetupFileHash(setup_file_hash);
2087 free(directory_path);
2093 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2094 TreeInfo *node_parent,
2095 char *base_directory, int type)
2098 struct dirent *dir_entry;
2099 boolean valid_entry_found = FALSE;
2101 if ((dir = opendir(base_directory)) == NULL)
2103 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2104 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2108 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2110 struct stat file_status;
2111 char *directory_name = dir_entry->d_name;
2112 char *directory_path = getPath2(base_directory, directory_name);
2114 /* skip entries for current and parent directory */
2115 if (strcmp(directory_name, ".") == 0 ||
2116 strcmp(directory_name, "..") == 0)
2118 free(directory_path);
2122 /* find out if directory entry is itself a directory */
2123 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2124 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2126 free(directory_path);
2130 free(directory_path);
2132 /* check if this directory contains artwork with or without config file */
2133 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2135 directory_name, type);
2140 /* check if this directory directly contains artwork itself */
2141 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2142 base_directory, ".",
2144 if (!valid_entry_found)
2145 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2149 static TreeInfo *getDummyArtworkInfo(int type)
2151 /* this is only needed when there is completely no artwork available */
2152 TreeInfo *artwork_new = newTreeInfo();
2154 setTreeInfoToDefaults(artwork_new, type);
2156 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2157 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2158 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2160 if (artwork_new->name != NULL)
2161 free(artwork_new->name);
2163 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2164 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2165 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2170 void LoadArtworkInfo()
2172 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2174 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2175 options.graphics_directory,
2176 TREE_TYPE_GRAPHICS_DIR);
2177 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2178 getUserGraphicsDir(),
2179 TREE_TYPE_GRAPHICS_DIR);
2181 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2182 options.sounds_directory,
2183 TREE_TYPE_SOUNDS_DIR);
2184 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2186 TREE_TYPE_SOUNDS_DIR);
2188 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2189 options.music_directory,
2190 TREE_TYPE_MUSIC_DIR);
2191 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2193 TREE_TYPE_MUSIC_DIR);
2195 if (artwork.gfx_first == NULL)
2196 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2197 if (artwork.snd_first == NULL)
2198 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2199 if (artwork.mus_first == NULL)
2200 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2202 /* before sorting, the first entries will be from the user directory */
2203 artwork.gfx_current =
2204 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2205 if (artwork.gfx_current == NULL)
2206 artwork.gfx_current =
2207 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2208 if (artwork.gfx_current == NULL)
2209 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2211 artwork.snd_current =
2212 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2213 if (artwork.snd_current == NULL)
2214 artwork.snd_current =
2215 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2216 if (artwork.snd_current == NULL)
2217 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2219 artwork.mus_current =
2220 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2221 if (artwork.mus_current == NULL)
2222 artwork.mus_current =
2223 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2224 if (artwork.mus_current == NULL)
2225 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2227 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2228 artwork.snd_current_identifier = artwork.snd_current->identifier;
2229 artwork.mus_current_identifier = artwork.mus_current->identifier;
2232 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2233 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2234 printf("music set == %s\n\n", artwork.mus_current_identifier);
2237 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2238 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2239 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2242 dumpTreeInfo(artwork.gfx_first, 0);
2243 dumpTreeInfo(artwork.snd_first, 0);
2244 dumpTreeInfo(artwork.mus_first, 0);
2248 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2249 LevelDirTree *level_node)
2251 /* recursively check all level directories for artwork sub-directories */
2255 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2256 ARTWORK_DIRECTORY((*artwork_node)->type));
2259 if (!level_node->parent_link)
2260 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2261 level_node->filename, level_node->name);
2264 if (!level_node->parent_link)
2266 TreeInfo *topnode_last = *artwork_node;
2268 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2269 (*artwork_node)->type);
2271 if (topnode_last != *artwork_node)
2273 free((*artwork_node)->identifier);
2274 free((*artwork_node)->name);
2275 free((*artwork_node)->name_sorting);
2277 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2278 (*artwork_node)->name = getStringCopy(level_node->name);
2279 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2281 (*artwork_node)->sort_priority = level_node->sort_priority;
2282 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2288 if (level_node->node_group != NULL)
2289 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2291 level_node = level_node->next;
2295 void LoadLevelArtworkInfo()
2297 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2299 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2300 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2301 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2303 /* needed for reloading level artwork not known at ealier stage */
2304 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2306 artwork.gfx_current =
2307 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2308 if (artwork.gfx_current == NULL)
2309 artwork.gfx_current =
2310 getTreeInfoFromIdentifier(artwork.gfx_first, GRAPHICS_SUBDIR);
2311 if (artwork.gfx_current == NULL)
2312 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2315 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2317 artwork.snd_current =
2318 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2319 if (artwork.snd_current == NULL)
2320 artwork.snd_current =
2321 getTreeInfoFromIdentifier(artwork.snd_first, SOUNDS_SUBDIR);
2322 if (artwork.snd_current == NULL)
2323 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2326 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2328 artwork.mus_current =
2329 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2330 if (artwork.mus_current == NULL)
2331 artwork.mus_current =
2332 getTreeInfoFromIdentifier(artwork.mus_first, MUSIC_SUBDIR);
2333 if (artwork.mus_current == NULL)
2334 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2337 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2338 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2339 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2342 dumpTreeInfo(artwork.gfx_first, 0);
2343 dumpTreeInfo(artwork.snd_first, 0);
2344 dumpTreeInfo(artwork.mus_first, 0);
2348 static void SaveUserLevelInfo()
2354 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2356 if (!(file = fopen(filename, MODE_WRITE)))
2358 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2363 /* always start with reliable default values */
2364 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2367 /* !!! FIX ME !!! */
2368 setString(&ldi.name, getLoginName());
2369 setString(&ldi.author, getRealName());
2371 ldi.first_level = 1;
2372 ldi.sort_priority = LEVELCLASS_USER_START;
2373 ldi.readonly = FALSE;
2374 setString(&ldi.graphics_set, GRAPHICS_SUBDIR);
2375 setString(&ldi.sounds_set, SOUNDS_SUBDIR);
2376 setString(&ldi.music_set, MUSIC_SUBDIR);
2378 ldi.name = getStringCopy(getLoginName());
2379 ldi.author = getStringCopy(getRealName());
2381 ldi.first_level = 1;
2382 ldi.sort_priority = LEVELCLASS_USER_START;
2383 ldi.readonly = FALSE;
2384 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2385 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2386 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2389 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2390 getCookie("LEVELINFO")));
2392 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2393 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2394 i != LEVELINFO_TOKEN_NAME_SORTING &&
2395 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2396 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2401 SetFilePermissions(filename, PERMS_PRIVATE);
2404 char *getSetupValue(int type, void *value)
2406 static char value_string[MAX_LINE_LEN];
2414 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2418 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2422 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2426 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2430 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2434 sprintf(value_string, "%d", *(int *)value);
2438 strcpy(value_string, *(char **)value);
2442 value_string[0] = '\0';
2446 return value_string;
2449 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2453 static char token_string[MAX_LINE_LEN];
2454 int token_type = token_info[token_nr].type;
2455 void *setup_value = token_info[token_nr].value;
2456 char *token_text = token_info[token_nr].text;
2457 char *value_string = getSetupValue(token_type, setup_value);
2459 /* build complete token string */
2460 sprintf(token_string, "%s%s", prefix, token_text);
2462 /* build setup entry line */
2463 line = getFormattedSetupEntry(token_string, value_string);
2465 if (token_type == TYPE_KEY_X11)
2467 Key key = *(Key *)setup_value;
2468 char *keyname = getKeyNameFromKey(key);
2470 /* add comment, if useful */
2471 if (strcmp(keyname, "(undefined)") != 0 &&
2472 strcmp(keyname, "(unknown)") != 0)
2474 /* add at least one whitespace */
2476 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2480 strcat(line, keyname);
2487 void LoadLevelSetup_LastSeries()
2490 SetupFileHash *level_setup_hash = NULL;
2492 /* always start with reliable default values */
2493 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2495 /* ----------------------------------------------------------------------- */
2496 /* ~/.<program>/levelsetup.conf */
2497 /* ----------------------------------------------------------------------- */
2499 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2501 if ((level_setup_hash = loadSetupFileHash(filename)))
2503 char *last_level_series =
2504 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2506 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2508 if (leveldir_current == NULL)
2509 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2511 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2513 freeSetupFileHash(level_setup_hash);
2516 Error(ERR_WARN, "using default setup values");
2521 void SaveLevelSetup_LastSeries()
2524 char *level_subdir = leveldir_current->filename;
2527 /* ----------------------------------------------------------------------- */
2528 /* ~/.<program>/levelsetup.conf */
2529 /* ----------------------------------------------------------------------- */
2531 InitUserDataDirectory();
2533 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2535 if (!(file = fopen(filename, MODE_WRITE)))
2537 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2542 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2543 getCookie("LEVELSETUP")));
2544 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2550 SetFilePermissions(filename, PERMS_PRIVATE);
2553 static void checkSeriesInfo()
2555 static char *level_directory = NULL;
2557 struct dirent *dir_entry;
2559 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2561 level_directory = getPath2((leveldir_current->user_defined ?
2562 getUserLevelDir(NULL) :
2563 options.level_directory),
2564 leveldir_current->fullpath);
2566 if ((dir = opendir(level_directory)) == NULL)
2568 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2572 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2574 if (strlen(dir_entry->d_name) > 4 &&
2575 dir_entry->d_name[3] == '.' &&
2576 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2578 char levelnum_str[4];
2581 strncpy(levelnum_str, dir_entry->d_name, 3);
2582 levelnum_str[3] = '\0';
2584 levelnum_value = atoi(levelnum_str);
2587 if (levelnum_value < leveldir_current->first_level)
2589 Error(ERR_WARN, "additional level %d found", levelnum_value);
2590 leveldir_current->first_level = levelnum_value;
2592 else if (levelnum_value > leveldir_current->last_level)
2594 Error(ERR_WARN, "additional level %d found", levelnum_value);
2595 leveldir_current->last_level = levelnum_value;
2604 void LoadLevelSetup_SeriesInfo()
2607 SetupFileHash *level_setup_hash = NULL;
2608 char *level_subdir = leveldir_current->filename;
2610 /* always start with reliable default values */
2611 level_nr = leveldir_current->first_level;
2613 checkSeriesInfo(leveldir_current);
2615 /* ----------------------------------------------------------------------- */
2616 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2617 /* ----------------------------------------------------------------------- */
2619 level_subdir = leveldir_current->filename;
2621 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2623 if ((level_setup_hash = loadSetupFileHash(filename)))
2627 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2631 level_nr = atoi(token_value);
2633 if (level_nr < leveldir_current->first_level)
2634 level_nr = leveldir_current->first_level;
2635 if (level_nr > leveldir_current->last_level)
2636 level_nr = leveldir_current->last_level;
2639 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2643 int level_nr = atoi(token_value);
2645 if (level_nr < leveldir_current->first_level)
2646 level_nr = leveldir_current->first_level;
2647 if (level_nr > leveldir_current->last_level + 1)
2648 level_nr = leveldir_current->last_level;
2650 if (leveldir_current->user_defined)
2651 level_nr = leveldir_current->last_level;
2653 leveldir_current->handicap_level = level_nr;
2656 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2658 freeSetupFileHash(level_setup_hash);
2661 Error(ERR_WARN, "using default setup values");
2666 void SaveLevelSetup_SeriesInfo()
2669 char *level_subdir = leveldir_current->filename;
2670 char *level_nr_str = int2str(level_nr, 0);
2671 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2674 /* ----------------------------------------------------------------------- */
2675 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2676 /* ----------------------------------------------------------------------- */
2678 InitLevelSetupDirectory(level_subdir);
2680 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2682 if (!(file = fopen(filename, MODE_WRITE)))
2684 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2689 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2690 getCookie("LEVELSETUP")));
2691 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2693 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2694 handicap_level_str));
2699 SetFilePermissions(filename, PERMS_PRIVATE);