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 ? GFX_CLASSIC_SUBDIR :
274 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
275 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
278 static char *getDefaultArtworkDir(int type)
280 return (type == TREE_TYPE_GRAPHICS_DIR ?
281 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
282 type == TREE_TYPE_SOUNDS_DIR ?
283 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
284 type == TREE_TYPE_MUSIC_DIR ?
285 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
288 static char *getUserGraphicsDir()
290 static char *usergraphics_dir = NULL;
292 if (usergraphics_dir == NULL)
293 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
295 return usergraphics_dir;
298 static char *getUserSoundsDir()
300 static char *usersounds_dir = NULL;
302 if (usersounds_dir == NULL)
303 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
305 return usersounds_dir;
308 static char *getUserMusicDir()
310 static char *usermusic_dir = NULL;
312 if (usermusic_dir == NULL)
313 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
315 return usermusic_dir;
318 static char *getSetupArtworkDir(TreeInfo *ti)
320 static char *artwork_dir = NULL;
322 if (artwork_dir != NULL)
325 artwork_dir = getPath2(ti->basepath, ti->fullpath);
330 char *setLevelArtworkDir(TreeInfo *ti)
332 char **artwork_path_ptr, **artwork_set_ptr;
333 TreeInfo *level_artwork;
335 if (ti == NULL || leveldir_current == NULL)
338 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
339 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
341 if (*artwork_path_ptr != NULL)
342 free(*artwork_path_ptr);
344 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
345 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
348 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
349 normally result in using the artwork configured in the setup menu. But
350 if an artwork subdirectory exists (which might contain custom artwork
351 or an artwork configuration file), this level artwork must be treated
352 as relative to the default "classic" artwork, not to the artwork that
353 is currently configured in the setup menu. */
355 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
357 if (*artwork_set_ptr != NULL)
358 free(*artwork_set_ptr);
362 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
363 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
367 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
368 *artwork_set_ptr = NULL;
374 return *artwork_set_ptr;
377 inline static char *getLevelArtworkSet(int type)
379 if (leveldir_current == NULL)
382 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
385 inline static char *getLevelArtworkDir(int type)
387 if (leveldir_current == NULL)
388 return UNDEFINED_FILENAME;
390 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
393 char *getLevelFilename(int nr)
395 static char *filename = NULL;
396 char basename[MAX_FILENAME_LEN];
398 if (filename != NULL)
402 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
404 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
406 filename = getPath2(getCurrentLevelDir(), basename);
411 char *getTapeFilename(int nr)
413 static char *filename = NULL;
414 char basename[MAX_FILENAME_LEN];
416 if (filename != NULL)
419 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
420 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
425 char *getScoreFilename(int nr)
427 static char *filename = NULL;
428 char basename[MAX_FILENAME_LEN];
430 if (filename != NULL)
433 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
434 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
439 char *getSetupFilename()
441 static char *filename = NULL;
443 if (filename != NULL)
446 filename = getPath2(getSetupDir(), SETUP_FILENAME);
451 static char *getCorrectedArtworkBasename(char *basename)
453 char *basename_corrected = basename;
455 #if defined(PLATFORM_MSDOS)
456 if (program.filename_prefix != NULL)
458 int prefix_len = strlen(program.filename_prefix);
460 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
461 basename_corrected = &basename[prefix_len];
463 /* if corrected filename is still longer than standard MS-DOS filename
464 size (8 characters + 1 dot + 3 characters file extension), shorten
465 filename by writing file extension after 8th basename character */
466 if (strlen(basename_corrected) > 8 + 1 + 3)
468 static char *msdos_filename = NULL;
470 if (msdos_filename != NULL)
471 free(msdos_filename);
473 msdos_filename = getStringCopy(basename_corrected);
474 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
476 basename_corrected = msdos_filename;
481 return basename_corrected;
484 char *getCustomImageFilename(char *basename)
486 static char *filename = NULL;
487 boolean skip_setup_artwork = FALSE;
489 if (filename != NULL)
492 basename = getCorrectedArtworkBasename(basename);
494 if (!setup.override_level_graphics)
496 /* 1st try: look for special artwork in current level series directory */
497 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
498 if (fileExists(filename))
503 /* check if there is special artwork configured in level series config */
504 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
506 /* 2nd try: look for special artwork configured in level series config */
507 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
508 if (fileExists(filename))
513 /* take missing artwork configured in level set config from default */
514 skip_setup_artwork = TRUE;
518 if (!skip_setup_artwork)
520 /* 3rd try: look for special artwork in configured artwork directory */
521 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
522 if (fileExists(filename))
528 /* 4th try: look for default artwork in new default artwork directory */
529 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
530 if (fileExists(filename))
535 /* 5th try: look for default artwork in old default artwork directory */
536 filename = getPath2(options.graphics_directory, basename);
537 if (fileExists(filename))
540 return NULL; /* cannot find specified artwork file anywhere */
543 char *getCustomSoundFilename(char *basename)
545 static char *filename = NULL;
546 boolean skip_setup_artwork = FALSE;
548 if (filename != NULL)
551 basename = getCorrectedArtworkBasename(basename);
553 if (!setup.override_level_sounds)
555 /* 1st try: look for special artwork in current level series directory */
556 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
557 if (fileExists(filename))
562 /* check if there is special artwork configured in level series config */
563 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
565 /* 2nd try: look for special artwork configured in level series config */
566 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
567 if (fileExists(filename))
572 /* take missing artwork configured in level set config from default */
573 skip_setup_artwork = TRUE;
577 if (!skip_setup_artwork)
579 /* 3rd try: look for special artwork in configured artwork directory */
580 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
581 if (fileExists(filename))
587 /* 4th try: look for default artwork in new default artwork directory */
588 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
589 if (fileExists(filename))
594 /* 5th try: look for default artwork in old default artwork directory */
595 filename = getPath2(options.sounds_directory, basename);
596 if (fileExists(filename))
599 return NULL; /* cannot find specified artwork file anywhere */
602 char *getCustomArtworkFilename(char *basename, int type)
604 if (type == ARTWORK_TYPE_GRAPHICS)
605 return getCustomImageFilename(basename);
606 else if (type == ARTWORK_TYPE_SOUNDS)
607 return getCustomSoundFilename(basename);
609 return UNDEFINED_FILENAME;
612 char *getCustomArtworkConfigFilename(int type)
614 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
617 char *getCustomArtworkLevelConfigFilename(int type)
619 static char *filename = NULL;
621 if (filename != NULL)
624 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
629 char *getCustomMusicDirectory(void)
631 static char *directory = NULL;
632 boolean skip_setup_artwork = FALSE;
634 if (directory != NULL)
637 if (!setup.override_level_music)
639 /* 1st try: look for special artwork in current level series directory */
640 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
641 if (fileExists(directory))
646 /* check if there is special artwork configured in level series config */
647 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
649 /* 2nd try: look for special artwork configured in level series config */
650 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
651 if (fileExists(directory))
656 /* take missing artwork configured in level set config from default */
657 skip_setup_artwork = TRUE;
661 if (!skip_setup_artwork)
663 /* 3rd try: look for special artwork in configured artwork directory */
664 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
665 if (fileExists(directory))
671 /* 4th try: look for default artwork in new default artwork directory */
672 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
673 if (fileExists(directory))
678 /* 5th try: look for default artwork in old default artwork directory */
679 directory = getStringCopy(options.music_directory);
680 if (fileExists(directory))
683 return NULL; /* cannot find specified artwork file anywhere */
686 void InitTapeDirectory(char *level_subdir)
688 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
689 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
690 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
693 void InitScoreDirectory(char *level_subdir)
695 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
696 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
697 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
700 static void SaveUserLevelInfo();
702 void InitUserLevelDirectory(char *level_subdir)
704 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
706 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
707 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
708 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
714 void InitLevelSetupDirectory(char *level_subdir)
716 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
717 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
718 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
722 /* ------------------------------------------------------------------------- */
723 /* some functions to handle lists of level directories */
724 /* ------------------------------------------------------------------------- */
726 TreeInfo *newTreeInfo()
728 return checked_calloc(sizeof(TreeInfo));
731 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
733 node_new->next = *node_first;
734 *node_first = node_new;
737 int numTreeInfo(TreeInfo *node)
750 boolean validLevelSeries(TreeInfo *node)
752 return (node != NULL && !node->node_group && !node->parent_link);
755 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
760 if (node->node_group) /* enter level group (step down into tree) */
761 return getFirstValidTreeInfoEntry(node->node_group);
762 else if (node->parent_link) /* skip start entry of level group */
764 if (node->next) /* get first real level series entry */
765 return getFirstValidTreeInfoEntry(node->next);
766 else /* leave empty level group and go on */
767 return getFirstValidTreeInfoEntry(node->node_parent->next);
769 else /* this seems to be a regular level series */
773 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
778 if (node->node_parent == NULL) /* top level group */
779 return *node->node_top;
780 else /* sub level group */
781 return node->node_parent->node_group;
784 int numTreeInfoInGroup(TreeInfo *node)
786 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
789 int posTreeInfo(TreeInfo *node)
791 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
796 if (node_cmp == node)
800 node_cmp = node_cmp->next;
806 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
808 TreeInfo *node_default = node;
823 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
825 if (identifier == NULL)
830 if (node->node_group)
832 TreeInfo *node_group;
834 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
839 else if (!node->parent_link)
841 if (strcmp(identifier, node->identifier) == 0)
851 void dumpTreeInfo(TreeInfo *node, int depth)
855 printf("Dumping TreeInfo:\n");
859 for (i=0; i<(depth + 1) * 3; i++)
863 printf("filename == '%s' ['%s', '%s'] [%d])\n",
864 node->filename, node->fullpath, node->basepath, node->user_defined);
866 printf("filename == '%s' (%s) [%s] (%d)\n",
867 node->filename, node->name, node->identifier, node->sort_priority);
870 if (node->node_group != NULL)
871 dumpTreeInfo(node->node_group, depth + 1);
877 void sortTreeInfo(TreeInfo **node_first,
878 int (*compare_function)(const void *, const void *))
880 int num_nodes = numTreeInfo(*node_first);
881 TreeInfo **sort_array;
882 TreeInfo *node = *node_first;
888 /* allocate array for sorting structure pointers */
889 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
891 /* writing structure pointers to sorting array */
892 while (i < num_nodes && node) /* double boundary check... */
894 sort_array[i] = node;
900 /* sorting the structure pointers in the sorting array */
901 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
904 /* update the linkage of list elements with the sorted node array */
905 for (i=0; i<num_nodes - 1; i++)
906 sort_array[i]->next = sort_array[i + 1];
907 sort_array[num_nodes - 1]->next = NULL;
909 /* update the linkage of the main list anchor pointer */
910 *node_first = sort_array[0];
914 /* now recursively sort the level group structures */
918 if (node->node_group != NULL)
919 sortTreeInfo(&node->node_group, compare_function);
926 /* ========================================================================= */
927 /* some stuff from "files.c" */
928 /* ========================================================================= */
930 #if defined(PLATFORM_WIN32)
932 #define S_IRGRP S_IRUSR
935 #define S_IROTH S_IRUSR
938 #define S_IWGRP S_IWUSR
941 #define S_IWOTH S_IWUSR
944 #define S_IXGRP S_IXUSR
947 #define S_IXOTH S_IXUSR
950 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
955 #endif /* PLATFORM_WIN32 */
957 /* file permissions for newly written files */
958 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
959 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
960 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
962 #define MODE_W_PRIVATE (S_IWUSR)
963 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
964 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
966 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
967 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
969 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
970 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
972 char *getUserDataDir(void)
974 static char *userdata_dir = NULL;
976 if (userdata_dir == NULL)
977 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
982 char *getCommonDataDir(void)
984 static char *common_data_dir = NULL;
986 #if defined(PLATFORM_WIN32)
987 if (common_data_dir == NULL)
989 char *dir = checked_malloc(MAX_PATH + 1);
991 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
992 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
993 common_data_dir = getPath2(dir, program.userdata_directory);
995 common_data_dir = options.rw_base_directory;
998 if (common_data_dir == NULL)
999 common_data_dir = options.rw_base_directory;
1002 return common_data_dir;
1007 return getUserDataDir();
1010 static mode_t posix_umask(mode_t mask)
1012 #if defined(PLATFORM_UNIX)
1019 static int posix_mkdir(const char *pathname, mode_t mode)
1021 #if defined(PLATFORM_WIN32)
1022 return mkdir(pathname);
1024 return mkdir(pathname, mode);
1028 void createDirectory(char *dir, char *text, int permission_class)
1030 /* leave "other" permissions in umask untouched, but ensure group parts
1031 of USERDATA_DIR_MODE are not masked */
1032 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1033 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1034 mode_t normal_umask = posix_umask(0);
1035 mode_t group_umask = ~(dir_mode & S_IRWXG);
1036 posix_umask(normal_umask & group_umask);
1038 if (access(dir, F_OK) != 0)
1039 if (posix_mkdir(dir, dir_mode) != 0)
1040 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1042 posix_umask(normal_umask); /* reset normal umask */
1045 void InitUserDataDirectory()
1047 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1050 void SetFilePermissions(char *filename, int permission_class)
1052 chmod(filename, (permission_class == PERMS_PRIVATE ?
1053 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1056 char *getCookie(char *file_type)
1058 static char cookie[MAX_COOKIE_LEN + 1];
1060 if (strlen(program.cookie_prefix) + 1 +
1061 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1062 return "[COOKIE ERROR]"; /* should never happen */
1064 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1065 program.cookie_prefix, file_type,
1066 program.version_major, program.version_minor);
1071 int getFileVersionFromCookieString(const char *cookie)
1073 const char *ptr_cookie1, *ptr_cookie2;
1074 const char *pattern1 = "_FILE_VERSION_";
1075 const char *pattern2 = "?.?";
1076 const int len_cookie = strlen(cookie);
1077 const int len_pattern1 = strlen(pattern1);
1078 const int len_pattern2 = strlen(pattern2);
1079 const int len_pattern = len_pattern1 + len_pattern2;
1080 int version_major, version_minor;
1082 if (len_cookie <= len_pattern)
1085 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1086 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1088 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1091 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1092 ptr_cookie2[1] != '.' ||
1093 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1096 version_major = ptr_cookie2[0] - '0';
1097 version_minor = ptr_cookie2[2] - '0';
1099 return VERSION_IDENT(version_major, version_minor, 0);
1102 boolean checkCookieString(const char *cookie, const char *template)
1104 const char *pattern = "_FILE_VERSION_?.?";
1105 const int len_cookie = strlen(cookie);
1106 const int len_template = strlen(template);
1107 const int len_pattern = strlen(pattern);
1109 if (len_cookie != len_template)
1112 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1118 /* ------------------------------------------------------------------------- */
1119 /* setup file list and hash handling functions */
1120 /* ------------------------------------------------------------------------- */
1122 char *getFormattedSetupEntry(char *token, char *value)
1125 static char entry[MAX_LINE_LEN];
1127 /* start with the token and some spaces to format output line */
1128 sprintf(entry, "%s:", token);
1129 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1132 /* continue with the token's value */
1133 strcat(entry, value);
1138 SetupFileList *newSetupFileList(char *token, char *value)
1140 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1142 new->token = getStringCopy(token);
1143 new->value = getStringCopy(value);
1150 void freeSetupFileList(SetupFileList *list)
1160 freeSetupFileList(list->next);
1164 char *getListEntry(SetupFileList *list, char *token)
1169 if (strcmp(list->token, token) == 0)
1172 return getListEntry(list->next, token);
1175 void setListEntry(SetupFileList *list, char *token, char *value)
1180 if (strcmp(list->token, token) == 0)
1185 list->value = getStringCopy(value);
1187 else if (list->next == NULL)
1188 list->next = newSetupFileList(token, value);
1190 setListEntry(list->next, token, value);
1194 static void printSetupFileList(SetupFileList *list)
1199 printf("token: '%s'\n", list->token);
1200 printf("value: '%s'\n", list->value);
1202 printSetupFileList(list->next);
1207 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1208 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1209 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1210 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1212 #define insert_hash_entry hashtable_insert
1213 #define search_hash_entry hashtable_search
1214 #define change_hash_entry hashtable_change
1215 #define remove_hash_entry hashtable_remove
1218 static unsigned int get_hash_from_key(void *key)
1223 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1224 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1225 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1226 it works better than many other constants, prime or not) has never been
1227 adequately explained.
1229 If you just want to have a good hash function, and cannot wait, djb2
1230 is one of the best string hash functions i know. It has excellent
1231 distribution and speed on many different sets of keys and table sizes.
1232 You are not likely to do better with one of the "well known" functions
1233 such as PJW, K&R, etc.
1235 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1238 char *str = (char *)key;
1239 unsigned int hash = 5381;
1242 while ((c = *str++))
1243 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1248 static int keys_are_equal(void *key1, void *key2)
1250 return (strcmp((char *)key1, (char *)key2) == 0);
1253 SetupFileHash *newSetupFileHash()
1255 SetupFileHash *new_hash =
1256 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1261 void freeSetupFileHash(SetupFileHash *hash)
1266 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1269 char *getHashEntry(SetupFileHash *hash, char *token)
1274 return search_hash_entry(hash, token);
1277 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1284 value_copy = getStringCopy(value);
1286 /* change value; if it does not exist, insert it as new */
1287 if (!change_hash_entry(hash, token, value_copy))
1288 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1289 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1294 static void printSetupFileHash(SetupFileHash *hash)
1296 BEGIN_HASH_ITERATION(hash, itr)
1298 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1299 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1301 END_HASH_ITERATION(hash, itr)
1306 static void *loadSetupFileData(char *filename, boolean use_hash)
1309 char line[MAX_LINE_LEN];
1310 char *token, *value, *line_ptr;
1311 void *setup_file_data;
1315 setup_file_data = newSetupFileHash();
1317 setup_file_data = newSetupFileList("", "");
1319 if (!(file = fopen(filename, MODE_READ)))
1321 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1327 /* read next line of input file */
1328 if (!fgets(line, MAX_LINE_LEN, file))
1331 /* cut trailing comment or whitespace from input line */
1332 for (line_ptr = line; *line_ptr; line_ptr++)
1334 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1341 /* cut trailing whitespaces from input line */
1342 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1343 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1346 /* ignore empty lines */
1350 line_len = strlen(line);
1352 /* cut leading whitespaces from token */
1353 for (token = line; *token; token++)
1354 if (*token != ' ' && *token != '\t')
1357 /* find end of token */
1358 for (line_ptr = token; *line_ptr; line_ptr++)
1360 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1367 if (line_ptr < line + line_len)
1368 value = line_ptr + 1;
1372 /* cut leading whitespaces from value */
1373 for (; *value; value++)
1374 if (*value != ' ' && *value != '\t')
1377 if (*token && *value)
1380 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1382 setListEntry((SetupFileList *)setup_file_data, token, value);
1390 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1391 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1395 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1396 SetupFileList *first_valid_list_entry = setup_file_list->next;
1398 /* free empty list header */
1399 setup_file_list->next = NULL;
1400 freeSetupFileList(setup_file_list);
1401 setup_file_data = first_valid_list_entry;
1403 if (first_valid_list_entry == NULL)
1404 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1407 return setup_file_data;
1410 SetupFileList *loadSetupFileList(char *filename)
1412 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1415 SetupFileHash *loadSetupFileHash(char *filename)
1417 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1420 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1423 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1426 Error(ERR_WARN, "configuration file has no file identifier");
1427 else if (!checkCookieString(value, identifier))
1428 Error(ERR_WARN, "configuration file has wrong file identifier");
1432 /* ========================================================================= */
1433 /* setup file stuff */
1434 /* ========================================================================= */
1436 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1437 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1438 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1440 /* level directory info */
1441 #define LEVELINFO_TOKEN_IDENTIFIER 0
1442 #define LEVELINFO_TOKEN_NAME 1
1443 #define LEVELINFO_TOKEN_NAME_SORTING 2
1444 #define LEVELINFO_TOKEN_AUTHOR 3
1445 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1446 #define LEVELINFO_TOKEN_LEVELS 5
1447 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1448 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1449 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1450 #define LEVELINFO_TOKEN_READONLY 9
1451 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1452 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1453 #define LEVELINFO_TOKEN_MUSIC_SET 12
1455 #define NUM_LEVELINFO_TOKENS 13
1457 static LevelDirTree ldi;
1459 static struct TokenInfo levelinfo_tokens[] =
1461 /* level directory info */
1462 { TYPE_STRING, &ldi.identifier, "identifier" },
1463 { TYPE_STRING, &ldi.name, "name" },
1464 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1465 { TYPE_STRING, &ldi.author, "author" },
1466 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1467 { TYPE_INTEGER, &ldi.levels, "levels" },
1468 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1469 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1470 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1471 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1472 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1473 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1474 { TYPE_STRING, &ldi.music_set, "music_set" }
1477 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1481 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1482 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1483 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1484 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1487 ldi->node_parent = NULL;
1488 ldi->node_group = NULL;
1492 ldi->cl_cursor = -1;
1494 ldi->filename = NULL;
1495 ldi->fullpath = NULL;
1496 ldi->basepath = NULL;
1497 ldi->identifier = NULL;
1498 ldi->name = getStringCopy(ANONYMOUS_NAME);
1499 ldi->name_sorting = NULL;
1500 ldi->author = getStringCopy(ANONYMOUS_NAME);
1502 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1503 ldi->parent_link = FALSE;
1504 ldi->user_defined = FALSE;
1506 ldi->class_desc = NULL;
1508 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1510 ldi->imported_from = NULL;
1512 ldi->graphics_set = NULL;
1513 ldi->sounds_set = NULL;
1514 ldi->music_set = NULL;
1515 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1516 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1517 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1520 ldi->first_level = 0;
1521 ldi->last_level = 0;
1522 ldi->level_group = FALSE;
1523 ldi->handicap_level = 0;
1524 ldi->readonly = TRUE;
1528 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1532 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1534 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1540 /* copy all values from the parent structure */
1542 ldi->type = parent->type;
1544 ldi->node_top = parent->node_top;
1545 ldi->node_parent = parent;
1546 ldi->node_group = NULL;
1550 ldi->cl_cursor = -1;
1552 ldi->filename = NULL;
1553 ldi->fullpath = NULL;
1554 ldi->basepath = NULL;
1555 ldi->identifier = NULL;
1556 ldi->name = getStringCopy(ANONYMOUS_NAME);
1557 ldi->name_sorting = NULL;
1558 ldi->author = getStringCopy(parent->author);
1560 ldi->sort_priority = parent->sort_priority;
1561 ldi->parent_link = FALSE;
1562 ldi->user_defined = parent->user_defined;
1563 ldi->color = parent->color;
1564 ldi->class_desc = getStringCopy(parent->class_desc);
1566 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1568 ldi->imported_from = getStringCopy(parent->imported_from);
1570 ldi->graphics_set = NULL;
1571 ldi->sounds_set = NULL;
1572 ldi->music_set = NULL;
1573 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1574 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1575 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1578 ldi->first_level = 0;
1579 ldi->last_level = 0;
1580 ldi->level_group = FALSE;
1581 ldi->handicap_level = 0;
1582 ldi->readonly = TRUE;
1588 /* first copy all values from the parent structure ... */
1591 /* ... then set all fields to default that cannot be inherited from parent.
1592 This is especially important for all those fields that can be set from
1593 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1594 calls 'free()' for all already set token values which requires that no
1595 other structure's pointer may point to them!
1598 ldi->filename = NULL;
1599 ldi->fullpath = NULL;
1600 ldi->basepath = NULL;
1601 ldi->identifier = NULL;
1602 ldi->name = getStringCopy(ANONYMOUS_NAME);
1603 ldi->name_sorting = NULL;
1604 ldi->author = getStringCopy(parent->author);
1606 ldi->imported_from = getStringCopy(parent->imported_from);
1607 ldi->class_desc = getStringCopy(parent->class_desc);
1609 ldi->graphics_set = NULL;
1610 ldi->sounds_set = NULL;
1611 ldi->music_set = NULL;
1612 ldi->graphics_path = NULL;
1613 ldi->sounds_path = NULL;
1614 ldi->music_path = NULL;
1616 ldi->level_group = FALSE;
1617 ldi->parent_link = FALSE;
1619 ldi->node_top = parent->node_top;
1620 ldi->node_parent = parent;
1621 ldi->node_group = NULL;
1627 static void freeTreeInfo(TreeInfo *ldi)
1630 free(ldi->filename);
1632 free(ldi->fullpath);
1634 free(ldi->basepath);
1635 if (ldi->identifier)
1636 free(ldi->identifier);
1640 if (ldi->name_sorting)
1641 free(ldi->name_sorting);
1645 if (ldi->class_desc)
1646 free(ldi->class_desc);
1648 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1650 if (ldi->graphics_set)
1651 free(ldi->graphics_set);
1652 if (ldi->sounds_set)
1653 free(ldi->sounds_set);
1655 free(ldi->music_set);
1657 if (ldi->graphics_path)
1658 free(ldi->graphics_path);
1659 if (ldi->sounds_path)
1660 free(ldi->sounds_path);
1661 if (ldi->music_path)
1662 free(ldi->music_path);
1666 void setSetupInfo(struct TokenInfo *token_info,
1667 int token_nr, char *token_value)
1669 int token_type = token_info[token_nr].type;
1670 void *setup_value = token_info[token_nr].value;
1672 if (token_value == NULL)
1675 /* set setup field to corresponding token value */
1680 *(boolean *)setup_value = get_boolean_from_string(token_value);
1684 *(Key *)setup_value = getKeyFromKeyName(token_value);
1688 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1692 *(int *)setup_value = get_integer_from_string(token_value);
1696 if (*(char **)setup_value != NULL)
1697 free(*(char **)setup_value);
1698 *(char **)setup_value = getStringCopy(token_value);
1706 static int compareTreeInfoEntries(const void *object1, const void *object2)
1708 const TreeInfo *entry1 = *((TreeInfo **)object1);
1709 const TreeInfo *entry2 = *((TreeInfo **)object2);
1710 int class_sorting1, class_sorting2;
1713 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1715 class_sorting1 = LEVELSORTING(entry1);
1716 class_sorting2 = LEVELSORTING(entry2);
1720 class_sorting1 = ARTWORKSORTING(entry1);
1721 class_sorting2 = ARTWORKSORTING(entry2);
1724 if (entry1->parent_link || entry2->parent_link)
1725 compare_result = (entry1->parent_link ? -1 : +1);
1726 else if (entry1->sort_priority == entry2->sort_priority)
1728 char *name1 = getStringToLower(entry1->name_sorting);
1729 char *name2 = getStringToLower(entry2->name_sorting);
1731 compare_result = strcmp(name1, name2);
1736 else if (class_sorting1 == class_sorting2)
1737 compare_result = entry1->sort_priority - entry2->sort_priority;
1739 compare_result = class_sorting1 - class_sorting2;
1741 return compare_result;
1744 static void createParentTreeInfoNode(TreeInfo *node_parent)
1748 if (node_parent == NULL)
1751 ti_new = newTreeInfo();
1752 setTreeInfoToDefaults(ti_new, node_parent->type);
1754 ti_new->node_parent = node_parent;
1755 ti_new->parent_link = TRUE;
1758 setString(&ti_new->identifier, node_parent->identifier);
1759 setString(&ti_new->name, ".. (parent directory)");
1760 setString(&ti_new->name_sorting, ti_new->name);
1762 setString(&ti_new->filename, "..");
1763 setString(&ti_new->fullpath, node_parent->fullpath);
1765 ti_new->sort_priority = node_parent->sort_priority;
1767 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1769 ti_new->identifier = getStringCopy(node_parent->identifier);
1770 ti_new->name = ".. (parent directory)";
1771 ti_new->name_sorting = getStringCopy(ti_new->name);
1773 ti_new->filename = "..";
1774 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1776 ti_new->sort_priority = node_parent->sort_priority;
1778 ti_new->class_desc = getLevelClassDescription(ti_new);
1781 pushTreeInfo(&node_parent->node_group, ti_new);
1784 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1785 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1787 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1788 TreeInfo *node_parent,
1789 char *level_directory,
1790 char *directory_name)
1792 char *directory_path = getPath2(level_directory, directory_name);
1793 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1794 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1795 LevelDirTree *leveldir_new = NULL;
1798 if (setup_file_hash == NULL)
1800 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1802 free(directory_path);
1808 leveldir_new = newTreeInfo();
1811 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1813 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1815 leveldir_new->filename = getStringCopy(directory_name);
1817 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1819 /* set all structure fields according to the token/value pairs */
1820 ldi = *leveldir_new;
1821 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1822 setSetupInfo(levelinfo_tokens, i,
1823 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1824 *leveldir_new = ldi;
1827 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1828 setString(&leveldir_new->name, leveldir_new->filename);
1830 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1832 free(leveldir_new->name);
1833 leveldir_new->name = getStringCopy(leveldir_new->filename);
1837 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1839 if (leveldir_new->identifier == NULL)
1840 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1842 if (leveldir_new->name_sorting == NULL)
1843 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1845 if (node_parent == NULL) /* top level group */
1847 leveldir_new->basepath = getStringCopy(level_directory);
1848 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1850 else /* sub level group */
1852 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1853 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1856 if (leveldir_new->levels < 1)
1857 leveldir_new->levels = 1;
1859 leveldir_new->last_level =
1860 leveldir_new->first_level + leveldir_new->levels - 1;
1863 leveldir_new->user_defined =
1864 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1866 leveldir_new->user_defined =
1867 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1870 leveldir_new->color = LEVELCOLOR(leveldir_new);
1872 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1874 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1877 leveldir_new->handicap_level = /* set handicap to default value */
1878 (leveldir_new->user_defined ?
1879 leveldir_new->last_level :
1880 leveldir_new->first_level);
1882 pushTreeInfo(node_first, leveldir_new);
1884 freeSetupFileHash(setup_file_hash);
1886 if (leveldir_new->level_group)
1888 /* create node to link back to current level directory */
1889 createParentTreeInfoNode(leveldir_new);
1891 /* step into sub-directory and look for more level series */
1892 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1893 leveldir_new, directory_path);
1896 free(directory_path);
1902 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1903 TreeInfo *node_parent,
1904 char *level_directory)
1907 struct dirent *dir_entry;
1908 boolean valid_entry_found = FALSE;
1910 if ((dir = opendir(level_directory)) == NULL)
1912 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1916 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1918 struct stat file_status;
1919 char *directory_name = dir_entry->d_name;
1920 char *directory_path = getPath2(level_directory, directory_name);
1922 /* skip entries for current and parent directory */
1923 if (strcmp(directory_name, ".") == 0 ||
1924 strcmp(directory_name, "..") == 0)
1926 free(directory_path);
1930 /* find out if directory entry is itself a directory */
1931 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1932 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1934 free(directory_path);
1938 free(directory_path);
1940 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1941 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1942 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1945 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1952 if (!valid_entry_found)
1954 /* check if this directory directly contains a file "levelinfo.conf" */
1955 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1956 level_directory, ".");
1959 if (!valid_entry_found)
1960 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1964 void LoadLevelInfo()
1966 InitUserLevelDirectory(getLoginName());
1968 DrawInitText("Loading level series:", 120, FC_GREEN);
1970 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1971 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1973 /* before sorting, the first entries will be from the user directory */
1974 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1976 if (leveldir_first == NULL)
1977 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1979 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1982 dumpTreeInfo(leveldir_first, 0);
1986 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1987 TreeInfo *node_parent,
1988 char *base_directory,
1989 char *directory_name, int type)
1991 char *directory_path = getPath2(base_directory, directory_name);
1992 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1993 SetupFileHash *setup_file_hash = NULL;
1994 TreeInfo *artwork_new = NULL;
1997 if (access(filename, F_OK) == 0) /* file exists */
1998 setup_file_hash = loadSetupFileHash(filename);
2000 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2003 struct dirent *dir_entry;
2004 boolean valid_file_found = FALSE;
2006 if ((dir = opendir(directory_path)) != NULL)
2008 while ((dir_entry = readdir(dir)) != NULL)
2010 char *entry_name = dir_entry->d_name;
2012 if (FileIsArtworkType(entry_name, type))
2014 valid_file_found = TRUE;
2022 if (!valid_file_found)
2024 if (strcmp(directory_name, ".") != 0)
2025 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2027 free(directory_path);
2034 artwork_new = newTreeInfo();
2037 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2039 setTreeInfoToDefaults(artwork_new, type);
2041 artwork_new->filename = getStringCopy(directory_name);
2043 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2046 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2049 /* set all structure fields according to the token/value pairs */
2051 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2052 setSetupInfo(levelinfo_tokens, i,
2053 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2057 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2058 setString(&artwork_new->name, artwork_new->filename);
2060 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2062 free(artwork_new->name);
2063 artwork_new->name = getStringCopy(artwork_new->filename);
2068 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2071 if (artwork_new->identifier == NULL)
2072 artwork_new->identifier = getStringCopy(artwork_new->filename);
2074 if (artwork_new->name_sorting == NULL)
2075 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2078 if (node_parent == NULL) /* top level group */
2080 artwork_new->basepath = getStringCopy(base_directory);
2081 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2083 else /* sub level group */
2085 artwork_new->basepath = getStringCopy(node_parent->basepath);
2086 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2090 artwork_new->user_defined =
2091 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2093 artwork_new->user_defined =
2094 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2097 /* (may use ".sort_priority" from "setup_file_hash" above) */
2098 artwork_new->color = ARTWORKCOLOR(artwork_new);
2100 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2102 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2105 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2108 if (artwork_new->name != NULL)
2110 free(artwork_new->name);
2111 artwork_new->name = NULL;
2116 if (artwork_new->identifier != NULL)
2118 free(artwork_new->identifier);
2119 artwork_new->identifier = NULL;
2123 if (strcmp(artwork_new->filename, ".") == 0)
2125 if (artwork_new->user_defined)
2128 setString(&artwork_new->identifier, "private");
2130 artwork_new->identifier = getStringCopy("private");
2132 artwork_new->sort_priority = ARTWORKCLASS_USER;
2137 setString(&artwork_new->identifier, "classic");
2139 artwork_new->identifier = getStringCopy("classic");
2141 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2144 /* set to new values after changing ".sort_priority" */
2145 artwork_new->color = ARTWORKCOLOR(artwork_new);
2147 setString(&artwork_new->class_desc,
2148 getLevelClassDescription(artwork_new));
2150 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2156 setString(&artwork_new->identifier, artwork_new->filename);
2158 artwork_new->identifier = getStringCopy(artwork_new->filename);
2163 setString(&artwork_new->name, artwork_new->identifier);
2164 setString(&artwork_new->name_sorting, artwork_new->name);
2166 artwork_new->name = getStringCopy(artwork_new->identifier);
2167 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2171 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2173 pushTreeInfo(node_first, artwork_new);
2175 freeSetupFileHash(setup_file_hash);
2177 free(directory_path);
2183 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2184 TreeInfo *node_parent,
2185 char *base_directory, int type)
2188 struct dirent *dir_entry;
2189 boolean valid_entry_found = FALSE;
2191 if ((dir = opendir(base_directory)) == NULL)
2193 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2194 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2198 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2200 struct stat file_status;
2201 char *directory_name = dir_entry->d_name;
2202 char *directory_path = getPath2(base_directory, directory_name);
2204 /* skip entries for current and parent directory */
2205 if (strcmp(directory_name, ".") == 0 ||
2206 strcmp(directory_name, "..") == 0)
2208 free(directory_path);
2212 /* find out if directory entry is itself a directory */
2213 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2214 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2216 free(directory_path);
2220 free(directory_path);
2222 /* check if this directory contains artwork with or without config file */
2223 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2225 directory_name, type);
2230 /* check if this directory directly contains artwork itself */
2231 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2232 base_directory, ".",
2234 if (!valid_entry_found)
2235 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2239 static TreeInfo *getDummyArtworkInfo(int type)
2241 /* this is only needed when there is completely no artwork available */
2242 TreeInfo *artwork_new = newTreeInfo();
2244 setTreeInfoToDefaults(artwork_new, type);
2247 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2248 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2249 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2251 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2252 setString(&artwork_new->name, UNDEFINED_FILENAME);
2253 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2255 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2256 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2257 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2259 if (artwork_new->name != NULL)
2260 free(artwork_new->name);
2262 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2263 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2264 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2270 void LoadArtworkInfo()
2272 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2274 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2275 options.graphics_directory,
2276 TREE_TYPE_GRAPHICS_DIR);
2277 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2278 getUserGraphicsDir(),
2279 TREE_TYPE_GRAPHICS_DIR);
2281 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2282 options.sounds_directory,
2283 TREE_TYPE_SOUNDS_DIR);
2284 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2286 TREE_TYPE_SOUNDS_DIR);
2288 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2289 options.music_directory,
2290 TREE_TYPE_MUSIC_DIR);
2291 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2293 TREE_TYPE_MUSIC_DIR);
2295 if (artwork.gfx_first == NULL)
2296 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2297 if (artwork.snd_first == NULL)
2298 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2299 if (artwork.mus_first == NULL)
2300 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2302 /* before sorting, the first entries will be from the user directory */
2303 artwork.gfx_current =
2304 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2305 if (artwork.gfx_current == NULL)
2306 artwork.gfx_current =
2307 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2308 if (artwork.gfx_current == NULL)
2309 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2311 artwork.snd_current =
2312 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2313 if (artwork.snd_current == NULL)
2314 artwork.snd_current =
2315 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2316 if (artwork.snd_current == NULL)
2317 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2319 artwork.mus_current =
2320 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2321 if (artwork.mus_current == NULL)
2322 artwork.mus_current =
2323 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2324 if (artwork.mus_current == NULL)
2325 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2327 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2328 artwork.snd_current_identifier = artwork.snd_current->identifier;
2329 artwork.mus_current_identifier = artwork.mus_current->identifier;
2332 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2333 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2334 printf("music set == %s\n\n", artwork.mus_current_identifier);
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 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2349 LevelDirTree *level_node)
2351 /* recursively check all level directories for artwork sub-directories */
2355 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2356 ARTWORK_DIRECTORY((*artwork_node)->type));
2359 if (!level_node->parent_link)
2360 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2361 level_node->filename, level_node->name);
2364 if (!level_node->parent_link)
2366 TreeInfo *topnode_last = *artwork_node;
2368 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2369 (*artwork_node)->type);
2371 if (topnode_last != *artwork_node)
2373 free((*artwork_node)->identifier);
2374 free((*artwork_node)->name);
2375 free((*artwork_node)->name_sorting);
2377 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2378 (*artwork_node)->name = getStringCopy(level_node->name);
2379 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2381 (*artwork_node)->sort_priority = level_node->sort_priority;
2382 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2388 if (level_node->node_group != NULL)
2389 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2391 level_node = level_node->next;
2395 void LoadLevelArtworkInfo()
2397 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2399 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2400 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2401 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2403 /* needed for reloading level artwork not known at ealier stage */
2404 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2406 artwork.gfx_current =
2407 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2408 if (artwork.gfx_current == NULL)
2409 artwork.gfx_current =
2410 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2411 if (artwork.gfx_current == NULL)
2412 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2415 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2417 artwork.snd_current =
2418 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2419 if (artwork.snd_current == NULL)
2420 artwork.snd_current =
2421 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2422 if (artwork.snd_current == NULL)
2423 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2426 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2428 artwork.mus_current =
2429 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2430 if (artwork.mus_current == NULL)
2431 artwork.mus_current =
2432 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2433 if (artwork.mus_current == NULL)
2434 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2437 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2438 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2439 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2442 dumpTreeInfo(artwork.gfx_first, 0);
2443 dumpTreeInfo(artwork.snd_first, 0);
2444 dumpTreeInfo(artwork.mus_first, 0);
2448 static void SaveUserLevelInfo()
2450 LevelDirTree *level_info;
2455 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2457 if (!(file = fopen(filename, MODE_WRITE)))
2459 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2464 level_info = newTreeInfo();
2466 /* always start with reliable default values */
2467 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2470 setString(&level_info->name, getLoginName());
2471 setString(&level_info->author, getRealName());
2472 level_info->levels = 100;
2473 level_info->first_level = 1;
2474 level_info->sort_priority = LEVELCLASS_USER_START;
2475 level_info->readonly = FALSE;
2476 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2477 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2478 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2480 ldi.name = getStringCopy(getLoginName());
2481 ldi.author = getStringCopy(getRealName());
2483 ldi.first_level = 1;
2484 ldi.sort_priority = LEVELCLASS_USER_START;
2485 ldi.readonly = FALSE;
2486 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2487 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2488 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2491 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2492 getCookie("LEVELINFO")));
2495 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2496 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2497 i != LEVELINFO_TOKEN_NAME_SORTING &&
2498 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2499 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2503 SetFilePermissions(filename, PERMS_PRIVATE);
2505 freeTreeInfo(level_info);
2509 char *getSetupValue(int type, void *value)
2511 static char value_string[MAX_LINE_LEN];
2519 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2523 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2527 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2531 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2535 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2539 sprintf(value_string, "%d", *(int *)value);
2543 strcpy(value_string, *(char **)value);
2547 value_string[0] = '\0';
2551 return value_string;
2554 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2558 static char token_string[MAX_LINE_LEN];
2559 int token_type = token_info[token_nr].type;
2560 void *setup_value = token_info[token_nr].value;
2561 char *token_text = token_info[token_nr].text;
2562 char *value_string = getSetupValue(token_type, setup_value);
2564 /* build complete token string */
2565 sprintf(token_string, "%s%s", prefix, token_text);
2567 /* build setup entry line */
2568 line = getFormattedSetupEntry(token_string, value_string);
2570 if (token_type == TYPE_KEY_X11)
2572 Key key = *(Key *)setup_value;
2573 char *keyname = getKeyNameFromKey(key);
2575 /* add comment, if useful */
2576 if (strcmp(keyname, "(undefined)") != 0 &&
2577 strcmp(keyname, "(unknown)") != 0)
2579 /* add at least one whitespace */
2581 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2585 strcat(line, keyname);
2592 void LoadLevelSetup_LastSeries()
2595 SetupFileHash *level_setup_hash = NULL;
2597 /* always start with reliable default values */
2598 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2600 /* ----------------------------------------------------------------------- */
2601 /* ~/.<program>/levelsetup.conf */
2602 /* ----------------------------------------------------------------------- */
2604 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2606 if ((level_setup_hash = loadSetupFileHash(filename)))
2608 char *last_level_series =
2609 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2611 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2613 if (leveldir_current == NULL)
2614 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2616 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2618 freeSetupFileHash(level_setup_hash);
2621 Error(ERR_WARN, "using default setup values");
2626 void SaveLevelSetup_LastSeries()
2629 char *level_subdir = leveldir_current->filename;
2632 /* ----------------------------------------------------------------------- */
2633 /* ~/.<program>/levelsetup.conf */
2634 /* ----------------------------------------------------------------------- */
2636 InitUserDataDirectory();
2638 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2640 if (!(file = fopen(filename, MODE_WRITE)))
2642 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2647 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2648 getCookie("LEVELSETUP")));
2649 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2654 SetFilePermissions(filename, PERMS_PRIVATE);
2659 static void checkSeriesInfo()
2661 static char *level_directory = NULL;
2663 struct dirent *dir_entry;
2665 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2667 level_directory = getPath2((leveldir_current->user_defined ?
2668 getUserLevelDir(NULL) :
2669 options.level_directory),
2670 leveldir_current->fullpath);
2672 if ((dir = opendir(level_directory)) == NULL)
2674 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2678 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2680 if (strlen(dir_entry->d_name) > 4 &&
2681 dir_entry->d_name[3] == '.' &&
2682 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2684 char levelnum_str[4];
2687 strncpy(levelnum_str, dir_entry->d_name, 3);
2688 levelnum_str[3] = '\0';
2690 levelnum_value = atoi(levelnum_str);
2693 if (levelnum_value < leveldir_current->first_level)
2695 Error(ERR_WARN, "additional level %d found", levelnum_value);
2696 leveldir_current->first_level = levelnum_value;
2698 else if (levelnum_value > leveldir_current->last_level)
2700 Error(ERR_WARN, "additional level %d found", levelnum_value);
2701 leveldir_current->last_level = levelnum_value;
2710 void LoadLevelSetup_SeriesInfo()
2713 SetupFileHash *level_setup_hash = NULL;
2714 char *level_subdir = leveldir_current->filename;
2716 /* always start with reliable default values */
2717 level_nr = leveldir_current->first_level;
2719 checkSeriesInfo(leveldir_current);
2721 /* ----------------------------------------------------------------------- */
2722 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2723 /* ----------------------------------------------------------------------- */
2725 level_subdir = leveldir_current->filename;
2727 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2729 if ((level_setup_hash = loadSetupFileHash(filename)))
2733 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2737 level_nr = atoi(token_value);
2739 if (level_nr < leveldir_current->first_level)
2740 level_nr = leveldir_current->first_level;
2741 if (level_nr > leveldir_current->last_level)
2742 level_nr = leveldir_current->last_level;
2745 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2749 int level_nr = atoi(token_value);
2751 if (level_nr < leveldir_current->first_level)
2752 level_nr = leveldir_current->first_level;
2753 if (level_nr > leveldir_current->last_level + 1)
2754 level_nr = leveldir_current->last_level;
2756 if (leveldir_current->user_defined)
2757 level_nr = leveldir_current->last_level;
2759 leveldir_current->handicap_level = level_nr;
2762 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2764 freeSetupFileHash(level_setup_hash);
2767 Error(ERR_WARN, "using default setup values");
2772 void SaveLevelSetup_SeriesInfo()
2775 char *level_subdir = leveldir_current->filename;
2776 char *level_nr_str = int2str(level_nr, 0);
2777 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2780 /* ----------------------------------------------------------------------- */
2781 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2782 /* ----------------------------------------------------------------------- */
2784 InitLevelSetupDirectory(level_subdir);
2786 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2788 if (!(file = fopen(filename, MODE_WRITE)))
2790 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2795 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2796 getCookie("LEVELSETUP")));
2797 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2799 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2800 handicap_level_str));
2804 SetFilePermissions(filename, PERMS_PRIVATE);