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>
27 #define NUM_LEVELCLASS_DESC 8
29 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
42 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
43 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
44 IS_LEVELCLASS_BD(n) ? FC_YELLOW : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_YELLOW : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_SB(n) ? FC_YELLOW : \
49 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
50 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
53 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
54 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
55 IS_LEVELCLASS_BD(n) ? 2 : \
56 IS_LEVELCLASS_EM(n) ? 3 : \
57 IS_LEVELCLASS_SP(n) ? 4 : \
58 IS_LEVELCLASS_DX(n) ? 5 : \
59 IS_LEVELCLASS_SB(n) ? 6 : \
60 IS_LEVELCLASS_CONTRIB(n) ? 7 : \
61 IS_LEVELCLASS_PRIVATE(n) ? 8 : \
64 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
65 IS_ARTWORKCLASS_CONTRIB(n) ? FC_GREEN : \
66 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
67 IS_ARTWORKCLASS_LEVEL(n) ? FC_YELLOW : \
70 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
71 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
72 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
73 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
76 #define TOKEN_VALUE_POSITION_SHORT 32
77 #define TOKEN_VALUE_POSITION_DEFAULT 40
78 #define TOKEN_COMMENT_POSITION_DEFAULT 60
80 #define MAX_COOKIE_LEN 256
82 static int token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
83 static int token_comment_position = TOKEN_COMMENT_POSITION_DEFAULT;
86 /* ------------------------------------------------------------------------- */
88 /* ------------------------------------------------------------------------- */
90 static char *getLevelClassDescription(TreeInfo *ldi)
92 int position = ldi->sort_priority / 100;
94 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
95 return levelclass_desc[position];
97 return "Unknown Level Class";
100 static char *getUserLevelDir(char *level_subdir)
102 static char *userlevel_dir = NULL;
103 char *data_dir = getUserDataDir();
104 char *userlevel_subdir = LEVELS_DIRECTORY;
106 checked_free(userlevel_dir);
108 if (level_subdir != NULL)
109 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
111 userlevel_dir = getPath2(data_dir, userlevel_subdir);
113 return userlevel_dir;
116 static char *getScoreDir(char *level_subdir)
118 static char *score_dir = NULL;
119 char *data_dir = getCommonDataDir();
120 char *score_subdir = SCORES_DIRECTORY;
122 checked_free(score_dir);
124 if (level_subdir != NULL)
125 score_dir = getPath3(data_dir, score_subdir, level_subdir);
127 score_dir = getPath2(data_dir, score_subdir);
132 static char *getLevelSetupDir(char *level_subdir)
134 static char *levelsetup_dir = NULL;
135 char *data_dir = getUserDataDir();
136 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
138 checked_free(levelsetup_dir);
140 if (level_subdir != NULL)
141 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
143 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
145 return levelsetup_dir;
148 static char *getLevelDirFromTreeInfo(TreeInfo *node)
150 static char *level_dir = NULL;
153 return options.level_directory;
155 checked_free(level_dir);
157 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
158 options.level_directory), node->fullpath);
163 char *getCurrentLevelDir()
165 return getLevelDirFromTreeInfo(leveldir_current);
168 static char *getTapeDir(char *level_subdir)
170 static char *tape_dir = NULL;
171 char *data_dir = getUserDataDir();
172 char *tape_subdir = TAPES_DIRECTORY;
174 checked_free(tape_dir);
176 if (level_subdir != NULL)
177 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
179 tape_dir = getPath2(data_dir, tape_subdir);
184 static char *getSolutionTapeDir()
186 static char *tape_dir = NULL;
187 char *data_dir = getCurrentLevelDir();
188 char *tape_subdir = TAPES_DIRECTORY;
190 checked_free(tape_dir);
192 tape_dir = getPath2(data_dir, tape_subdir);
197 static char *getDefaultGraphicsDir(char *graphics_subdir)
199 static char *graphics_dir = NULL;
201 if (graphics_subdir == NULL)
202 return options.graphics_directory;
204 checked_free(graphics_dir);
206 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
211 static char *getDefaultSoundsDir(char *sounds_subdir)
213 static char *sounds_dir = NULL;
215 if (sounds_subdir == NULL)
216 return options.sounds_directory;
218 checked_free(sounds_dir);
220 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
225 static char *getDefaultMusicDir(char *music_subdir)
227 static char *music_dir = NULL;
229 if (music_subdir == NULL)
230 return options.music_directory;
232 checked_free(music_dir);
234 music_dir = getPath2(options.music_directory, music_subdir);
239 static char *getDefaultArtworkSet(int type)
241 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
242 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
243 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
246 static char *getDefaultArtworkDir(int type)
248 return (type == TREE_TYPE_GRAPHICS_DIR ?
249 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
250 type == TREE_TYPE_SOUNDS_DIR ?
251 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
252 type == TREE_TYPE_MUSIC_DIR ?
253 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
256 static char *getUserGraphicsDir()
258 static char *usergraphics_dir = NULL;
260 if (usergraphics_dir == NULL)
261 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
263 return usergraphics_dir;
266 static char *getUserSoundsDir()
268 static char *usersounds_dir = NULL;
270 if (usersounds_dir == NULL)
271 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
273 return usersounds_dir;
276 static char *getUserMusicDir()
278 static char *usermusic_dir = NULL;
280 if (usermusic_dir == NULL)
281 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
283 return usermusic_dir;
286 static char *getSetupArtworkDir(TreeInfo *ti)
288 static char *artwork_dir = NULL;
290 checked_free(artwork_dir);
292 artwork_dir = getPath2(ti->basepath, ti->fullpath);
297 char *setLevelArtworkDir(TreeInfo *ti)
299 char **artwork_path_ptr, **artwork_set_ptr;
300 TreeInfo *level_artwork;
302 if (ti == NULL || leveldir_current == NULL)
305 artwork_path_ptr = LEVELDIR_ARTWORK_PATH_PTR(leveldir_current, ti->type);
306 artwork_set_ptr = LEVELDIR_ARTWORK_SET_PTR( leveldir_current, ti->type);
308 checked_free(*artwork_path_ptr);
310 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
311 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
314 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
315 normally result in using the artwork configured in the setup menu. But
316 if an artwork subdirectory exists (which might contain custom artwork
317 or an artwork configuration file), this level artwork must be treated
318 as relative to the default "classic" artwork, not to the artwork that
319 is currently configured in the setup menu. */
321 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
323 checked_free(*artwork_set_ptr);
327 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
328 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
332 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
333 *artwork_set_ptr = NULL;
339 return *artwork_set_ptr;
342 inline static char *getLevelArtworkSet(int type)
344 if (leveldir_current == NULL)
347 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
350 inline static char *getLevelArtworkDir(int type)
352 if (leveldir_current == NULL)
353 return UNDEFINED_FILENAME;
355 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
358 char *getTapeFilename(int nr)
360 static char *filename = NULL;
361 char basename[MAX_FILENAME_LEN];
363 checked_free(filename);
365 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
366 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
371 char *getSolutionTapeFilename(int nr)
373 static char *filename = NULL;
374 char basename[MAX_FILENAME_LEN];
376 checked_free(filename);
378 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
379 filename = getPath2(getSolutionTapeDir(), basename);
384 char *getScoreFilename(int nr)
386 static char *filename = NULL;
387 char basename[MAX_FILENAME_LEN];
389 checked_free(filename);
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 checked_free(filename);
403 filename = getPath2(getSetupDir(), SETUP_FILENAME);
408 char *getEditorSetupFilename()
410 static char *filename = NULL;
412 checked_free(filename);
413 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
415 if (fileExists(filename))
418 checked_free(filename);
419 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
424 char *getHelpAnimFilename()
426 static char *filename = NULL;
428 checked_free(filename);
430 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
435 char *getHelpTextFilename()
437 static char *filename = NULL;
439 checked_free(filename);
441 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
446 char *getLevelSetInfoFilename()
448 static char *filename = NULL;
463 for (i = 0; basenames[i] != NULL; i++)
465 checked_free(filename);
466 filename = getPath2(getCurrentLevelDir(), basenames[i]);
468 if (fileExists(filename))
475 static char *getCorrectedArtworkBasename(char *basename)
477 char *basename_corrected = basename;
479 #if defined(PLATFORM_MSDOS)
480 if (program.filename_prefix != NULL)
482 int prefix_len = strlen(program.filename_prefix);
484 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
485 basename_corrected = &basename[prefix_len];
487 /* if corrected filename is still longer than standard MS-DOS filename
488 size (8 characters + 1 dot + 3 characters file extension), shorten
489 filename by writing file extension after 8th basename character */
490 if (strlen(basename_corrected) > 8 + 1 + 3)
492 static char *msdos_filename = NULL;
494 checked_free(msdos_filename);
496 msdos_filename = getStringCopy(basename_corrected);
497 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
499 basename_corrected = msdos_filename;
504 return basename_corrected;
507 char *getCustomImageFilename(char *basename)
509 static char *filename = NULL;
510 boolean skip_setup_artwork = FALSE;
512 checked_free(filename);
514 basename = getCorrectedArtworkBasename(basename);
516 if (!setup.override_level_graphics)
518 /* 1st try: look for special artwork in current level series directory */
519 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
520 if (fileExists(filename))
525 /* check if there is special artwork configured in level series config */
526 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
528 /* 2nd try: look for special artwork configured in level series config */
529 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
530 if (fileExists(filename))
535 /* take missing artwork configured in level set config from default */
536 skip_setup_artwork = TRUE;
540 if (!skip_setup_artwork)
542 /* 3rd try: look for special artwork in configured artwork directory */
543 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
544 if (fileExists(filename))
550 /* 4th try: look for default artwork in new default artwork directory */
551 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
552 if (fileExists(filename))
557 /* 5th try: look for default artwork in old default artwork directory */
558 filename = getPath2(options.graphics_directory, basename);
559 if (fileExists(filename))
562 return NULL; /* cannot find specified artwork file anywhere */
565 char *getCustomSoundFilename(char *basename)
567 static char *filename = NULL;
568 boolean skip_setup_artwork = FALSE;
570 checked_free(filename);
572 basename = getCorrectedArtworkBasename(basename);
574 if (!setup.override_level_sounds)
576 /* 1st try: look for special artwork in current level series directory */
577 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
578 if (fileExists(filename))
583 /* check if there is special artwork configured in level series config */
584 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
586 /* 2nd try: look for special artwork configured in level series config */
587 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
588 if (fileExists(filename))
593 /* take missing artwork configured in level set config from default */
594 skip_setup_artwork = TRUE;
598 if (!skip_setup_artwork)
600 /* 3rd try: look for special artwork in configured artwork directory */
601 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
602 if (fileExists(filename))
608 /* 4th try: look for default artwork in new default artwork directory */
609 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
610 if (fileExists(filename))
615 /* 5th try: look for default artwork in old default artwork directory */
616 filename = getPath2(options.sounds_directory, basename);
617 if (fileExists(filename))
620 return NULL; /* cannot find specified artwork file anywhere */
623 char *getCustomMusicFilename(char *basename)
625 static char *filename = NULL;
626 boolean skip_setup_artwork = FALSE;
628 checked_free(filename);
630 basename = getCorrectedArtworkBasename(basename);
632 if (!setup.override_level_music)
634 /* 1st try: look for special artwork in current level series directory */
635 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
636 if (fileExists(filename))
641 /* check if there is special artwork configured in level series config */
642 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
644 /* 2nd try: look for special artwork configured in level series config */
645 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
646 if (fileExists(filename))
651 /* take missing artwork configured in level set config from default */
652 skip_setup_artwork = TRUE;
656 if (!skip_setup_artwork)
658 /* 3rd try: look for special artwork in configured artwork directory */
659 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
660 if (fileExists(filename))
666 /* 4th try: look for default artwork in new default artwork directory */
667 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
668 if (fileExists(filename))
673 /* 5th try: look for default artwork in old default artwork directory */
674 filename = getPath2(options.music_directory, basename);
675 if (fileExists(filename))
678 return NULL; /* cannot find specified artwork file anywhere */
681 char *getCustomArtworkFilename(char *basename, int type)
683 if (type == ARTWORK_TYPE_GRAPHICS)
684 return getCustomImageFilename(basename);
685 else if (type == ARTWORK_TYPE_SOUNDS)
686 return getCustomSoundFilename(basename);
687 else if (type == ARTWORK_TYPE_MUSIC)
688 return getCustomMusicFilename(basename);
690 return UNDEFINED_FILENAME;
693 char *getCustomArtworkConfigFilename(int type)
695 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
698 char *getCustomArtworkLevelConfigFilename(int type)
700 static char *filename = NULL;
702 checked_free(filename);
704 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
709 char *getCustomMusicDirectory(void)
711 static char *directory = NULL;
712 boolean skip_setup_artwork = FALSE;
714 checked_free(directory);
716 if (!setup.override_level_music)
718 /* 1st try: look for special artwork in current level series directory */
719 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
720 if (fileExists(directory))
725 /* check if there is special artwork configured in level series config */
726 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
728 /* 2nd try: look for special artwork configured in level series config */
729 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
730 if (fileExists(directory))
735 /* take missing artwork configured in level set config from default */
736 skip_setup_artwork = TRUE;
740 if (!skip_setup_artwork)
742 /* 3rd try: look for special artwork in configured artwork directory */
743 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
744 if (fileExists(directory))
750 /* 4th try: look for default artwork in new default artwork directory */
751 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
752 if (fileExists(directory))
757 /* 5th try: look for default artwork in old default artwork directory */
758 directory = getStringCopy(options.music_directory);
759 if (fileExists(directory))
762 return NULL; /* cannot find specified artwork file anywhere */
765 void InitTapeDirectory(char *level_subdir)
767 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
768 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
769 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
772 void InitScoreDirectory(char *level_subdir)
774 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
775 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
776 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
779 static void SaveUserLevelInfo();
781 void InitUserLevelDirectory(char *level_subdir)
783 if (!fileExists(getUserLevelDir(level_subdir)))
785 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
786 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
787 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
793 void InitLevelSetupDirectory(char *level_subdir)
795 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
796 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
797 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
801 /* ------------------------------------------------------------------------- */
802 /* some functions to handle lists of level directories */
803 /* ------------------------------------------------------------------------- */
805 TreeInfo *newTreeInfo()
807 return checked_calloc(sizeof(TreeInfo));
810 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
812 node_new->next = *node_first;
813 *node_first = node_new;
816 int numTreeInfo(TreeInfo *node)
829 boolean validLevelSeries(TreeInfo *node)
831 return (node != NULL && !node->node_group && !node->parent_link);
834 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
839 if (node->node_group) /* enter level group (step down into tree) */
840 return getFirstValidTreeInfoEntry(node->node_group);
841 else if (node->parent_link) /* skip start entry of level group */
843 if (node->next) /* get first real level series entry */
844 return getFirstValidTreeInfoEntry(node->next);
845 else /* leave empty level group and go on */
846 return getFirstValidTreeInfoEntry(node->node_parent->next);
848 else /* this seems to be a regular level series */
852 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
857 if (node->node_parent == NULL) /* top level group */
858 return *node->node_top;
859 else /* sub level group */
860 return node->node_parent->node_group;
863 int numTreeInfoInGroup(TreeInfo *node)
865 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
868 int posTreeInfo(TreeInfo *node)
870 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
875 if (node_cmp == node)
879 node_cmp = node_cmp->next;
885 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
887 TreeInfo *node_default = node;
902 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
904 if (identifier == NULL)
909 if (node->node_group)
911 TreeInfo *node_group;
913 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
918 else if (!node->parent_link)
920 if (strcmp(identifier, node->identifier) == 0)
930 void dumpTreeInfo(TreeInfo *node, int depth)
934 printf("Dumping TreeInfo:\n");
938 for (i = 0; i < (depth + 1) * 3; i++)
941 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
942 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
944 if (node->node_group != NULL)
945 dumpTreeInfo(node->node_group, depth + 1);
951 void sortTreeInfo(TreeInfo **node_first,
952 int (*compare_function)(const void *, const void *))
954 int num_nodes = numTreeInfo(*node_first);
955 TreeInfo **sort_array;
956 TreeInfo *node = *node_first;
962 /* allocate array for sorting structure pointers */
963 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
965 /* writing structure pointers to sorting array */
966 while (i < num_nodes && node) /* double boundary check... */
968 sort_array[i] = node;
974 /* sorting the structure pointers in the sorting array */
975 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
978 /* update the linkage of list elements with the sorted node array */
979 for (i = 0; i < num_nodes - 1; i++)
980 sort_array[i]->next = sort_array[i + 1];
981 sort_array[num_nodes - 1]->next = NULL;
983 /* update the linkage of the main list anchor pointer */
984 *node_first = sort_array[0];
988 /* now recursively sort the level group structures */
992 if (node->node_group != NULL)
993 sortTreeInfo(&node->node_group, compare_function);
1000 /* ========================================================================= */
1001 /* some stuff from "files.c" */
1002 /* ========================================================================= */
1004 #if defined(PLATFORM_WIN32)
1006 #define S_IRGRP S_IRUSR
1009 #define S_IROTH S_IRUSR
1012 #define S_IWGRP S_IWUSR
1015 #define S_IWOTH S_IWUSR
1018 #define S_IXGRP S_IXUSR
1021 #define S_IXOTH S_IXUSR
1024 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1029 #endif /* PLATFORM_WIN32 */
1031 /* file permissions for newly written files */
1032 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1033 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1034 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1036 #define MODE_W_PRIVATE (S_IWUSR)
1037 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1038 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1040 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1041 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1043 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1044 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1046 char *getUserDataDir(void)
1048 static char *userdata_dir = NULL;
1050 if (userdata_dir == NULL)
1051 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1053 return userdata_dir;
1056 char *getCommonDataDir(void)
1058 static char *common_data_dir = NULL;
1060 #if defined(PLATFORM_WIN32)
1061 if (common_data_dir == NULL)
1063 char *dir = checked_malloc(MAX_PATH + 1);
1065 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1066 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1067 common_data_dir = getPath2(dir, program.userdata_directory);
1069 common_data_dir = options.rw_base_directory;
1072 if (common_data_dir == NULL)
1073 common_data_dir = options.rw_base_directory;
1076 return common_data_dir;
1081 return getUserDataDir();
1084 static mode_t posix_umask(mode_t mask)
1086 #if defined(PLATFORM_UNIX)
1093 static int posix_mkdir(const char *pathname, mode_t mode)
1095 #if defined(PLATFORM_WIN32)
1096 return mkdir(pathname);
1098 return mkdir(pathname, mode);
1102 void createDirectory(char *dir, char *text, int permission_class)
1104 /* leave "other" permissions in umask untouched, but ensure group parts
1105 of USERDATA_DIR_MODE are not masked */
1106 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1107 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1108 mode_t normal_umask = posix_umask(0);
1109 mode_t group_umask = ~(dir_mode & S_IRWXG);
1110 posix_umask(normal_umask & group_umask);
1112 if (!fileExists(dir))
1113 if (posix_mkdir(dir, dir_mode) != 0)
1114 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1116 posix_umask(normal_umask); /* reset normal umask */
1119 void InitUserDataDirectory()
1121 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1124 void SetFilePermissions(char *filename, int permission_class)
1126 chmod(filename, (permission_class == PERMS_PRIVATE ?
1127 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1130 char *getCookie(char *file_type)
1132 static char cookie[MAX_COOKIE_LEN + 1];
1134 if (strlen(program.cookie_prefix) + 1 +
1135 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1136 return "[COOKIE ERROR]"; /* should never happen */
1138 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1139 program.cookie_prefix, file_type,
1140 program.version_major, program.version_minor);
1145 int getFileVersionFromCookieString(const char *cookie)
1147 const char *ptr_cookie1, *ptr_cookie2;
1148 const char *pattern1 = "_FILE_VERSION_";
1149 const char *pattern2 = "?.?";
1150 const int len_cookie = strlen(cookie);
1151 const int len_pattern1 = strlen(pattern1);
1152 const int len_pattern2 = strlen(pattern2);
1153 const int len_pattern = len_pattern1 + len_pattern2;
1154 int version_major, version_minor;
1156 if (len_cookie <= len_pattern)
1159 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1160 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1162 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1165 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1166 ptr_cookie2[1] != '.' ||
1167 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1170 version_major = ptr_cookie2[0] - '0';
1171 version_minor = ptr_cookie2[2] - '0';
1173 return VERSION_IDENT(version_major, version_minor, 0, 0);
1176 boolean checkCookieString(const char *cookie, const char *template)
1178 const char *pattern = "_FILE_VERSION_?.?";
1179 const int len_cookie = strlen(cookie);
1180 const int len_template = strlen(template);
1181 const int len_pattern = strlen(pattern);
1183 if (len_cookie != len_template)
1186 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1192 /* ------------------------------------------------------------------------- */
1193 /* setup file list and hash handling functions */
1194 /* ------------------------------------------------------------------------- */
1196 char *getFormattedSetupEntry(char *token, char *value)
1199 static char entry[MAX_LINE_LEN];
1201 /* if value is an empty string, just return token without value */
1205 /* start with the token and some spaces to format output line */
1206 sprintf(entry, "%s:", token);
1207 for (i = strlen(entry); i < token_value_position; i++)
1210 /* continue with the token's value */
1211 strcat(entry, value);
1216 SetupFileList *newSetupFileList(char *token, char *value)
1218 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1220 new->token = getStringCopy(token);
1221 new->value = getStringCopy(value);
1228 void freeSetupFileList(SetupFileList *list)
1233 checked_free(list->token);
1234 checked_free(list->value);
1237 freeSetupFileList(list->next);
1242 char *getListEntry(SetupFileList *list, char *token)
1247 if (strcmp(list->token, token) == 0)
1250 return getListEntry(list->next, token);
1253 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1258 if (strcmp(list->token, token) == 0)
1260 checked_free(list->value);
1262 list->value = getStringCopy(value);
1266 else if (list->next == NULL)
1267 return (list->next = newSetupFileList(token, value));
1269 return setListEntry(list->next, token, value);
1272 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1277 if (list->next == NULL)
1278 return (list->next = newSetupFileList(token, value));
1280 return addListEntry(list->next, token, value);
1284 static void printSetupFileList(SetupFileList *list)
1289 printf("token: '%s'\n", list->token);
1290 printf("value: '%s'\n", list->value);
1292 printSetupFileList(list->next);
1297 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1298 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1299 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1300 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1302 #define insert_hash_entry hashtable_insert
1303 #define search_hash_entry hashtable_search
1304 #define change_hash_entry hashtable_change
1305 #define remove_hash_entry hashtable_remove
1308 static unsigned int get_hash_from_key(void *key)
1313 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1314 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1315 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1316 it works better than many other constants, prime or not) has never been
1317 adequately explained.
1319 If you just want to have a good hash function, and cannot wait, djb2
1320 is one of the best string hash functions i know. It has excellent
1321 distribution and speed on many different sets of keys and table sizes.
1322 You are not likely to do better with one of the "well known" functions
1323 such as PJW, K&R, etc.
1325 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1328 char *str = (char *)key;
1329 unsigned int hash = 5381;
1332 while ((c = *str++))
1333 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1338 static int keys_are_equal(void *key1, void *key2)
1340 return (strcmp((char *)key1, (char *)key2) == 0);
1343 SetupFileHash *newSetupFileHash()
1345 SetupFileHash *new_hash =
1346 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1348 if (new_hash == NULL)
1349 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1354 void freeSetupFileHash(SetupFileHash *hash)
1359 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1362 char *getHashEntry(SetupFileHash *hash, char *token)
1367 return search_hash_entry(hash, token);
1370 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1377 value_copy = getStringCopy(value);
1379 /* change value; if it does not exist, insert it as new */
1380 if (!change_hash_entry(hash, token, value_copy))
1381 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1382 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1385 char *removeHashEntry(SetupFileHash *hash, char *token)
1390 return remove_hash_entry(hash, token);
1394 static void printSetupFileHash(SetupFileHash *hash)
1396 BEGIN_HASH_ITERATION(hash, itr)
1398 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1399 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1401 END_HASH_ITERATION(hash, itr)
1405 static void *loadSetupFileData(char *filename, boolean use_hash)
1407 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1408 char *token, *value, *line_ptr;
1409 void *setup_file_data, *insert_ptr = NULL;
1410 boolean read_continued_line = FALSE;
1413 if (!(file = fopen(filename, MODE_READ)))
1415 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1421 setup_file_data = newSetupFileHash();
1423 insert_ptr = setup_file_data = newSetupFileList("", "");
1427 /* read next line of input file */
1428 if (!fgets(line, MAX_LINE_LEN, file))
1431 /* cut trailing newline or carriage return */
1432 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1433 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1436 if (read_continued_line)
1438 /* cut leading whitespaces from input line */
1439 for (line_ptr = line; *line_ptr; line_ptr++)
1440 if (*line_ptr != ' ' && *line_ptr != '\t')
1443 /* append new line to existing line, if there is enough space */
1444 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1445 strcat(previous_line, line_ptr);
1447 strcpy(line, previous_line); /* copy storage buffer to line */
1449 read_continued_line = FALSE;
1452 /* if the last character is '\', continue at next line */
1453 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1455 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1456 strcpy(previous_line, line); /* copy line to storage buffer */
1458 read_continued_line = TRUE;
1463 /* cut trailing comment from input line */
1464 for (line_ptr = line; *line_ptr; line_ptr++)
1466 if (*line_ptr == '#')
1473 /* cut trailing whitespaces from input line */
1474 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1475 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1478 /* ignore empty lines */
1482 /* cut leading whitespaces from token */
1483 for (token = line; *token; token++)
1484 if (*token != ' ' && *token != '\t')
1487 /* start with empty value as reliable default */
1490 /* find end of token to determine start of value */
1491 for (line_ptr = token; *line_ptr; line_ptr++)
1493 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1495 *line_ptr = '\0'; /* terminate token string */
1496 value = line_ptr + 1; /* set beginning of value */
1502 /* cut leading whitespaces from value */
1503 for (; *value; value++)
1504 if (*value != ' ' && *value != '\t')
1509 value = "true"; /* treat tokens without value as "true" */
1515 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1517 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1525 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1526 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1530 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1531 SetupFileList *first_valid_list_entry = setup_file_list->next;
1533 /* free empty list header */
1534 setup_file_list->next = NULL;
1535 freeSetupFileList(setup_file_list);
1536 setup_file_data = first_valid_list_entry;
1538 if (first_valid_list_entry == NULL)
1539 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1542 return setup_file_data;
1545 SetupFileList *loadSetupFileList(char *filename)
1547 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1550 SetupFileHash *loadSetupFileHash(char *filename)
1552 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1555 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1558 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1561 Error(ERR_WARN, "configuration file has no file identifier");
1562 else if (!checkCookieString(value, identifier))
1563 Error(ERR_WARN, "configuration file has wrong file identifier");
1567 /* ========================================================================= */
1568 /* setup file stuff */
1569 /* ========================================================================= */
1571 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1572 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1573 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1575 /* level directory info */
1576 #define LEVELINFO_TOKEN_IDENTIFIER 0
1577 #define LEVELINFO_TOKEN_NAME 1
1578 #define LEVELINFO_TOKEN_NAME_SORTING 2
1579 #define LEVELINFO_TOKEN_AUTHOR 3
1580 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1581 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1582 #define LEVELINFO_TOKEN_LEVELS 6
1583 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1584 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1585 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1586 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1587 #define LEVELINFO_TOKEN_READONLY 11
1588 #define LEVELINFO_TOKEN_GRAPHICS_SET 12
1589 #define LEVELINFO_TOKEN_SOUNDS_SET 13
1590 #define LEVELINFO_TOKEN_MUSIC_SET 14
1591 #define LEVELINFO_TOKEN_FILENAME 15
1592 #define LEVELINFO_TOKEN_FILETYPE 16
1593 #define LEVELINFO_TOKEN_HANDICAP 17
1594 #define LEVELINFO_TOKEN_SKIP_LEVELS 18
1596 #define NUM_LEVELINFO_TOKENS 19
1598 static LevelDirTree ldi;
1600 static struct TokenInfo levelinfo_tokens[] =
1602 /* level directory info */
1603 { TYPE_STRING, &ldi.identifier, "identifier" },
1604 { TYPE_STRING, &ldi.name, "name" },
1605 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1606 { TYPE_STRING, &ldi.author, "author" },
1607 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1608 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1609 { TYPE_INTEGER, &ldi.levels, "levels" },
1610 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1611 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1612 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1613 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1614 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1615 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1616 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1617 { TYPE_STRING, &ldi.music_set, "music_set" },
1618 { TYPE_STRING, &ldi.level_filename, "filename" },
1619 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1620 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1621 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1624 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1628 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1629 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1630 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1631 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1634 ldi->node_parent = NULL;
1635 ldi->node_group = NULL;
1639 ldi->cl_cursor = -1;
1642 ldi->fullpath = NULL;
1643 ldi->basepath = NULL;
1644 ldi->identifier = NULL;
1645 ldi->name = getStringCopy(ANONYMOUS_NAME);
1646 ldi->name_sorting = NULL;
1647 ldi->author = getStringCopy(ANONYMOUS_NAME);
1649 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1650 ldi->latest_engine = FALSE; /* default: get from level */
1651 ldi->parent_link = FALSE;
1652 ldi->in_user_dir = FALSE;
1653 ldi->user_defined = FALSE;
1655 ldi->class_desc = NULL;
1657 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1659 ldi->imported_from = NULL;
1660 ldi->imported_by = NULL;
1662 ldi->graphics_set = NULL;
1663 ldi->sounds_set = NULL;
1664 ldi->music_set = NULL;
1665 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1666 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1667 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1669 ldi->level_filename = NULL;
1670 ldi->level_filetype = NULL;
1673 ldi->first_level = 0;
1674 ldi->last_level = 0;
1675 ldi->level_group = FALSE;
1676 ldi->handicap_level = 0;
1677 ldi->readonly = TRUE;
1678 ldi->handicap = TRUE;
1679 ldi->skip_levels = FALSE;
1683 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1687 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1689 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1694 /* copy all values from the parent structure */
1696 ldi->type = parent->type;
1698 ldi->node_top = parent->node_top;
1699 ldi->node_parent = parent;
1700 ldi->node_group = NULL;
1704 ldi->cl_cursor = -1;
1707 ldi->fullpath = NULL;
1708 ldi->basepath = NULL;
1709 ldi->identifier = NULL;
1710 ldi->name = getStringCopy(ANONYMOUS_NAME);
1711 ldi->name_sorting = NULL;
1712 ldi->author = getStringCopy(parent->author);
1714 ldi->sort_priority = parent->sort_priority;
1715 ldi->latest_engine = parent->latest_engine;
1716 ldi->parent_link = FALSE;
1717 ldi->in_user_dir = parent->in_user_dir;
1718 ldi->user_defined = parent->user_defined;
1719 ldi->color = parent->color;
1720 ldi->class_desc = getStringCopy(parent->class_desc);
1722 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1724 ldi->imported_from = getStringCopy(parent->imported_from);
1725 ldi->imported_by = getStringCopy(parent->imported_by);
1727 ldi->graphics_set = NULL;
1728 ldi->sounds_set = NULL;
1729 ldi->music_set = NULL;
1730 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1731 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1732 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1734 ldi->level_filename = NULL;
1735 ldi->level_filetype = NULL;
1738 ldi->first_level = 0;
1739 ldi->last_level = 0;
1740 ldi->level_group = FALSE;
1741 ldi->handicap_level = 0;
1742 ldi->readonly = TRUE;
1743 ldi->handicap = TRUE;
1744 ldi->skip_levels = FALSE;
1748 static void freeTreeInfo(TreeInfo *ldi)
1750 checked_free(ldi->subdir);
1751 checked_free(ldi->fullpath);
1752 checked_free(ldi->basepath);
1753 checked_free(ldi->identifier);
1755 checked_free(ldi->name);
1756 checked_free(ldi->name_sorting);
1757 checked_free(ldi->author);
1759 checked_free(ldi->class_desc);
1761 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1763 checked_free(ldi->imported_from);
1764 checked_free(ldi->imported_by);
1766 checked_free(ldi->graphics_set);
1767 checked_free(ldi->sounds_set);
1768 checked_free(ldi->music_set);
1770 checked_free(ldi->graphics_path);
1771 checked_free(ldi->sounds_path);
1772 checked_free(ldi->music_path);
1774 checked_free(ldi->level_filename);
1775 checked_free(ldi->level_filetype);
1779 void setSetupInfo(struct TokenInfo *token_info,
1780 int token_nr, char *token_value)
1782 int token_type = token_info[token_nr].type;
1783 void *setup_value = token_info[token_nr].value;
1785 if (token_value == NULL)
1788 /* set setup field to corresponding token value */
1793 *(boolean *)setup_value = get_boolean_from_string(token_value);
1797 *(Key *)setup_value = getKeyFromKeyName(token_value);
1801 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1805 *(int *)setup_value = get_integer_from_string(token_value);
1809 checked_free(*(char **)setup_value);
1810 *(char **)setup_value = getStringCopy(token_value);
1818 static int compareTreeInfoEntries(const void *object1, const void *object2)
1820 const TreeInfo *entry1 = *((TreeInfo **)object1);
1821 const TreeInfo *entry2 = *((TreeInfo **)object2);
1822 int class_sorting1, class_sorting2;
1825 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1827 class_sorting1 = LEVELSORTING(entry1);
1828 class_sorting2 = LEVELSORTING(entry2);
1832 class_sorting1 = ARTWORKSORTING(entry1);
1833 class_sorting2 = ARTWORKSORTING(entry2);
1836 if (entry1->parent_link || entry2->parent_link)
1837 compare_result = (entry1->parent_link ? -1 : +1);
1838 else if (entry1->sort_priority == entry2->sort_priority)
1840 char *name1 = getStringToLower(entry1->name_sorting);
1841 char *name2 = getStringToLower(entry2->name_sorting);
1843 compare_result = strcmp(name1, name2);
1848 else if (class_sorting1 == class_sorting2)
1849 compare_result = entry1->sort_priority - entry2->sort_priority;
1851 compare_result = class_sorting1 - class_sorting2;
1853 return compare_result;
1856 static void createParentTreeInfoNode(TreeInfo *node_parent)
1860 if (node_parent == NULL)
1863 ti_new = newTreeInfo();
1864 setTreeInfoToDefaults(ti_new, node_parent->type);
1866 ti_new->node_parent = node_parent;
1867 ti_new->parent_link = TRUE;
1869 setString(&ti_new->identifier, node_parent->identifier);
1870 setString(&ti_new->name, ".. (parent directory)");
1871 setString(&ti_new->name_sorting, ti_new->name);
1873 setString(&ti_new->subdir, "..");
1874 setString(&ti_new->fullpath, node_parent->fullpath);
1876 ti_new->sort_priority = node_parent->sort_priority;
1877 ti_new->latest_engine = node_parent->latest_engine;
1879 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1881 pushTreeInfo(&node_parent->node_group, ti_new);
1884 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1885 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1887 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1888 TreeInfo *node_parent,
1889 char *level_directory,
1890 char *directory_name)
1892 char *directory_path = getPath2(level_directory, directory_name);
1893 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1894 SetupFileHash *setup_file_hash;
1895 LevelDirTree *leveldir_new = NULL;
1898 /* unless debugging, silently ignore directories without "levelinfo.conf" */
1899 if (!options.debug && !fileExists(filename))
1901 free(directory_path);
1907 setup_file_hash = loadSetupFileHash(filename);
1909 if (setup_file_hash == NULL)
1911 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1913 free(directory_path);
1919 leveldir_new = newTreeInfo();
1922 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1924 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1926 leveldir_new->subdir = getStringCopy(directory_name);
1928 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1930 /* set all structure fields according to the token/value pairs */
1931 ldi = *leveldir_new;
1932 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1933 setSetupInfo(levelinfo_tokens, i,
1934 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1935 *leveldir_new = ldi;
1937 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1938 setString(&leveldir_new->name, leveldir_new->subdir);
1940 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1942 if (leveldir_new->identifier == NULL)
1943 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
1945 if (leveldir_new->name_sorting == NULL)
1946 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1948 if (node_parent == NULL) /* top level group */
1950 leveldir_new->basepath = getStringCopy(level_directory);
1951 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
1953 else /* sub level group */
1955 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1956 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1959 if (leveldir_new->levels < 1)
1960 leveldir_new->levels = 1;
1962 leveldir_new->last_level =
1963 leveldir_new->first_level + leveldir_new->levels - 1;
1965 leveldir_new->in_user_dir =
1966 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1968 /* adjust sort priority if user's private level directory was detected */
1969 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
1970 leveldir_new->in_user_dir &&
1971 strcmp(leveldir_new->subdir, getLoginName()) == 0)
1972 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
1974 leveldir_new->user_defined =
1975 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
1977 leveldir_new->color = LEVELCOLOR(leveldir_new);
1979 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1981 leveldir_new->handicap_level = /* set handicap to default value */
1982 (leveldir_new->user_defined || !leveldir_new->handicap ?
1983 leveldir_new->last_level : leveldir_new->first_level);
1985 pushTreeInfo(node_first, leveldir_new);
1987 freeSetupFileHash(setup_file_hash);
1989 if (leveldir_new->level_group)
1991 /* create node to link back to current level directory */
1992 createParentTreeInfoNode(leveldir_new);
1994 /* step into sub-directory and look for more level series */
1995 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1996 leveldir_new, directory_path);
1999 free(directory_path);
2005 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2006 TreeInfo *node_parent,
2007 char *level_directory)
2010 struct dirent *dir_entry;
2011 boolean valid_entry_found = FALSE;
2013 if ((dir = opendir(level_directory)) == NULL)
2015 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2019 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2021 struct stat file_status;
2022 char *directory_name = dir_entry->d_name;
2023 char *directory_path = getPath2(level_directory, directory_name);
2025 /* skip entries for current and parent directory */
2026 if (strcmp(directory_name, ".") == 0 ||
2027 strcmp(directory_name, "..") == 0)
2029 free(directory_path);
2033 /* find out if directory entry is itself a directory */
2034 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2035 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2037 free(directory_path);
2041 free(directory_path);
2043 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2044 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2045 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2048 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2055 if (!valid_entry_found)
2057 /* check if this directory directly contains a file "levelinfo.conf" */
2058 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2059 level_directory, ".");
2062 if (!valid_entry_found)
2063 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2067 void LoadLevelInfo()
2069 InitUserLevelDirectory(getLoginName());
2071 DrawInitText("Loading level series:", 120, FC_GREEN);
2073 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2074 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2076 /* before sorting, the first entries will be from the user directory */
2077 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2079 if (leveldir_first == NULL)
2080 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2082 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2085 dumpTreeInfo(leveldir_first, 0);
2089 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2090 TreeInfo *node_parent,
2091 char *base_directory,
2092 char *directory_name, int type)
2094 char *directory_path = getPath2(base_directory, directory_name);
2095 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2096 SetupFileHash *setup_file_hash = NULL;
2097 TreeInfo *artwork_new = NULL;
2100 if (fileExists(filename))
2101 setup_file_hash = loadSetupFileHash(filename);
2103 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2106 struct dirent *dir_entry;
2107 boolean valid_file_found = FALSE;
2109 if ((dir = opendir(directory_path)) != NULL)
2111 while ((dir_entry = readdir(dir)) != NULL)
2113 char *entry_name = dir_entry->d_name;
2115 if (FileIsArtworkType(entry_name, type))
2117 valid_file_found = TRUE;
2125 if (!valid_file_found)
2127 if (strcmp(directory_name, ".") != 0)
2128 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2130 free(directory_path);
2137 artwork_new = newTreeInfo();
2140 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2142 setTreeInfoToDefaults(artwork_new, type);
2144 artwork_new->subdir = getStringCopy(directory_name);
2146 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2149 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2152 /* set all structure fields according to the token/value pairs */
2154 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2155 setSetupInfo(levelinfo_tokens, i,
2156 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2159 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2160 setString(&artwork_new->name, artwork_new->subdir);
2163 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2166 if (artwork_new->identifier == NULL)
2167 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2169 if (artwork_new->name_sorting == NULL)
2170 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2173 if (node_parent == NULL) /* top level group */
2175 artwork_new->basepath = getStringCopy(base_directory);
2176 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2178 else /* sub level group */
2180 artwork_new->basepath = getStringCopy(node_parent->basepath);
2181 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2184 artwork_new->in_user_dir =
2185 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2187 /* (may use ".sort_priority" from "setup_file_hash" above) */
2188 artwork_new->color = ARTWORKCOLOR(artwork_new);
2190 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2192 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2194 if (strcmp(artwork_new->subdir, ".") == 0)
2196 if (artwork_new->user_defined)
2198 setString(&artwork_new->identifier, "private");
2199 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2203 setString(&artwork_new->identifier, "classic");
2204 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2207 /* set to new values after changing ".sort_priority" */
2208 artwork_new->color = ARTWORKCOLOR(artwork_new);
2210 setString(&artwork_new->class_desc,
2211 getLevelClassDescription(artwork_new));
2215 setString(&artwork_new->identifier, artwork_new->subdir);
2218 setString(&artwork_new->name, artwork_new->identifier);
2219 setString(&artwork_new->name_sorting, artwork_new->name);
2222 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2224 pushTreeInfo(node_first, artwork_new);
2226 freeSetupFileHash(setup_file_hash);
2228 free(directory_path);
2234 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2235 TreeInfo *node_parent,
2236 char *base_directory, int type)
2239 struct dirent *dir_entry;
2240 boolean valid_entry_found = FALSE;
2242 if ((dir = opendir(base_directory)) == NULL)
2244 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2245 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2249 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2251 struct stat file_status;
2252 char *directory_name = dir_entry->d_name;
2253 char *directory_path = getPath2(base_directory, directory_name);
2255 /* skip entries for current and parent directory */
2256 if (strcmp(directory_name, ".") == 0 ||
2257 strcmp(directory_name, "..") == 0)
2259 free(directory_path);
2263 /* find out if directory entry is itself a directory */
2264 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2265 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2267 free(directory_path);
2271 free(directory_path);
2273 /* check if this directory contains artwork with or without config file */
2274 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2276 directory_name, type);
2281 /* check if this directory directly contains artwork itself */
2282 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2283 base_directory, ".",
2285 if (!valid_entry_found)
2286 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2290 static TreeInfo *getDummyArtworkInfo(int type)
2292 /* this is only needed when there is completely no artwork available */
2293 TreeInfo *artwork_new = newTreeInfo();
2295 setTreeInfoToDefaults(artwork_new, type);
2297 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2298 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2299 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2301 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2302 setString(&artwork_new->name, UNDEFINED_FILENAME);
2303 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2308 void LoadArtworkInfo()
2310 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2312 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2313 options.graphics_directory,
2314 TREE_TYPE_GRAPHICS_DIR);
2315 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2316 getUserGraphicsDir(),
2317 TREE_TYPE_GRAPHICS_DIR);
2319 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2320 options.sounds_directory,
2321 TREE_TYPE_SOUNDS_DIR);
2322 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2324 TREE_TYPE_SOUNDS_DIR);
2326 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2327 options.music_directory,
2328 TREE_TYPE_MUSIC_DIR);
2329 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2331 TREE_TYPE_MUSIC_DIR);
2333 if (artwork.gfx_first == NULL)
2334 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2335 if (artwork.snd_first == NULL)
2336 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2337 if (artwork.mus_first == NULL)
2338 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2340 /* before sorting, the first entries will be from the user directory */
2341 artwork.gfx_current =
2342 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2343 if (artwork.gfx_current == NULL)
2344 artwork.gfx_current =
2345 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2346 if (artwork.gfx_current == NULL)
2347 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2349 artwork.snd_current =
2350 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2351 if (artwork.snd_current == NULL)
2352 artwork.snd_current =
2353 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2354 if (artwork.snd_current == NULL)
2355 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2357 artwork.mus_current =
2358 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2359 if (artwork.mus_current == NULL)
2360 artwork.mus_current =
2361 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2362 if (artwork.mus_current == NULL)
2363 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2365 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2366 artwork.snd_current_identifier = artwork.snd_current->identifier;
2367 artwork.mus_current_identifier = artwork.mus_current->identifier;
2370 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2371 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2372 printf("music set == %s\n\n", artwork.mus_current_identifier);
2375 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2376 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2377 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2380 dumpTreeInfo(artwork.gfx_first, 0);
2381 dumpTreeInfo(artwork.snd_first, 0);
2382 dumpTreeInfo(artwork.mus_first, 0);
2386 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2387 LevelDirTree *level_node)
2389 /* recursively check all level directories for artwork sub-directories */
2393 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2394 ARTWORK_DIRECTORY((*artwork_node)->type));
2396 if (!level_node->parent_link)
2398 TreeInfo *topnode_last = *artwork_node;
2400 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2401 (*artwork_node)->type);
2403 if (topnode_last != *artwork_node)
2405 free((*artwork_node)->identifier);
2406 free((*artwork_node)->name);
2407 free((*artwork_node)->name_sorting);
2409 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2410 (*artwork_node)->name = getStringCopy(level_node->name);
2411 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2413 (*artwork_node)->sort_priority = level_node->sort_priority;
2414 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2420 if (level_node->node_group != NULL)
2421 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2423 level_node = level_node->next;
2427 void LoadLevelArtworkInfo()
2429 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2431 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2432 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2433 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2435 /* needed for reloading level artwork not known at ealier stage */
2437 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2439 artwork.gfx_current =
2440 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2441 if (artwork.gfx_current == NULL)
2442 artwork.gfx_current =
2443 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2444 if (artwork.gfx_current == NULL)
2445 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2448 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2450 artwork.snd_current =
2451 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2452 if (artwork.snd_current == NULL)
2453 artwork.snd_current =
2454 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2455 if (artwork.snd_current == NULL)
2456 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2459 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2461 artwork.mus_current =
2462 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2463 if (artwork.mus_current == NULL)
2464 artwork.mus_current =
2465 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2466 if (artwork.mus_current == NULL)
2467 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2470 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2471 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2472 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2475 dumpTreeInfo(artwork.gfx_first, 0);
2476 dumpTreeInfo(artwork.snd_first, 0);
2477 dumpTreeInfo(artwork.mus_first, 0);
2481 static void SaveUserLevelInfo()
2483 LevelDirTree *level_info;
2488 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2490 if (!(file = fopen(filename, MODE_WRITE)))
2492 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2497 level_info = newTreeInfo();
2499 /* always start with reliable default values */
2500 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2502 setString(&level_info->name, getLoginName());
2503 setString(&level_info->author, getRealName());
2504 level_info->levels = 100;
2505 level_info->first_level = 1;
2507 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2509 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2510 getCookie("LEVELINFO")));
2513 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2515 if (i == LEVELINFO_TOKEN_NAME ||
2516 i == LEVELINFO_TOKEN_AUTHOR ||
2517 i == LEVELINFO_TOKEN_LEVELS ||
2518 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2519 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2521 /* just to make things nicer :) */
2522 if (i == LEVELINFO_TOKEN_AUTHOR)
2523 fprintf(file, "\n");
2526 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2530 SetFilePermissions(filename, PERMS_PRIVATE);
2532 freeTreeInfo(level_info);
2536 char *getSetupValue(int type, void *value)
2538 static char value_string[MAX_LINE_LEN];
2546 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2550 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2554 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2558 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2562 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2566 sprintf(value_string, "%d", *(int *)value);
2570 strcpy(value_string, *(char **)value);
2574 value_string[0] = '\0';
2578 return value_string;
2581 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2585 static char token_string[MAX_LINE_LEN];
2586 int token_type = token_info[token_nr].type;
2587 void *setup_value = token_info[token_nr].value;
2588 char *token_text = token_info[token_nr].text;
2589 char *value_string = getSetupValue(token_type, setup_value);
2591 /* build complete token string */
2592 sprintf(token_string, "%s%s", prefix, token_text);
2594 /* build setup entry line */
2595 line = getFormattedSetupEntry(token_string, value_string);
2597 if (token_type == TYPE_KEY_X11)
2599 Key key = *(Key *)setup_value;
2600 char *keyname = getKeyNameFromKey(key);
2602 /* add comment, if useful */
2603 if (strcmp(keyname, "(undefined)") != 0 &&
2604 strcmp(keyname, "(unknown)") != 0)
2606 /* add at least one whitespace */
2608 for (i = strlen(line); i < token_comment_position; i++)
2612 strcat(line, keyname);
2619 void LoadLevelSetup_LastSeries()
2621 /* ----------------------------------------------------------------------- */
2622 /* ~/.<program>/levelsetup.conf */
2623 /* ----------------------------------------------------------------------- */
2625 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2626 SetupFileHash *level_setup_hash = NULL;
2628 /* always start with reliable default values */
2629 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2631 if ((level_setup_hash = loadSetupFileHash(filename)))
2633 char *last_level_series =
2634 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2636 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2638 if (leveldir_current == NULL)
2639 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2641 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2643 freeSetupFileHash(level_setup_hash);
2646 Error(ERR_WARN, "using default setup values");
2651 void SaveLevelSetup_LastSeries()
2653 /* ----------------------------------------------------------------------- */
2654 /* ~/.<program>/levelsetup.conf */
2655 /* ----------------------------------------------------------------------- */
2657 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2658 char *level_subdir = leveldir_current->subdir;
2661 InitUserDataDirectory();
2663 if (!(file = fopen(filename, MODE_WRITE)))
2665 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2670 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2671 getCookie("LEVELSETUP")));
2672 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2677 SetFilePermissions(filename, PERMS_PRIVATE);
2682 static void checkSeriesInfo()
2684 static char *level_directory = NULL;
2686 struct dirent *dir_entry;
2688 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2690 level_directory = getPath2((leveldir_current->in_user_dir ?
2691 getUserLevelDir(NULL) :
2692 options.level_directory),
2693 leveldir_current->fullpath);
2695 if ((dir = opendir(level_directory)) == NULL)
2697 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2701 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2703 if (strlen(dir_entry->d_name) > 4 &&
2704 dir_entry->d_name[3] == '.' &&
2705 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2707 char levelnum_str[4];
2710 strncpy(levelnum_str, dir_entry->d_name, 3);
2711 levelnum_str[3] = '\0';
2713 levelnum_value = atoi(levelnum_str);
2716 if (levelnum_value < leveldir_current->first_level)
2718 Error(ERR_WARN, "additional level %d found", levelnum_value);
2719 leveldir_current->first_level = levelnum_value;
2721 else if (levelnum_value > leveldir_current->last_level)
2723 Error(ERR_WARN, "additional level %d found", levelnum_value);
2724 leveldir_current->last_level = levelnum_value;
2733 void LoadLevelSetup_SeriesInfo()
2736 SetupFileHash *level_setup_hash = NULL;
2737 char *level_subdir = leveldir_current->subdir;
2739 /* always start with reliable default values */
2740 level_nr = leveldir_current->first_level;
2742 checkSeriesInfo(leveldir_current);
2744 /* ----------------------------------------------------------------------- */
2745 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2746 /* ----------------------------------------------------------------------- */
2748 level_subdir = leveldir_current->subdir;
2750 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2752 if ((level_setup_hash = loadSetupFileHash(filename)))
2756 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2760 level_nr = atoi(token_value);
2762 if (level_nr < leveldir_current->first_level)
2763 level_nr = leveldir_current->first_level;
2764 if (level_nr > leveldir_current->last_level)
2765 level_nr = leveldir_current->last_level;
2768 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2772 int level_nr = atoi(token_value);
2774 if (level_nr < leveldir_current->first_level)
2775 level_nr = leveldir_current->first_level;
2776 if (level_nr > leveldir_current->last_level + 1)
2777 level_nr = leveldir_current->last_level;
2779 if (leveldir_current->user_defined || !leveldir_current->handicap)
2780 level_nr = leveldir_current->last_level;
2782 leveldir_current->handicap_level = level_nr;
2785 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2787 freeSetupFileHash(level_setup_hash);
2790 Error(ERR_WARN, "using default setup values");
2795 void SaveLevelSetup_SeriesInfo()
2798 char *level_subdir = leveldir_current->subdir;
2799 char *level_nr_str = int2str(level_nr, 0);
2800 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2803 /* ----------------------------------------------------------------------- */
2804 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2805 /* ----------------------------------------------------------------------- */
2807 InitLevelSetupDirectory(level_subdir);
2809 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2811 if (!(file = fopen(filename, MODE_WRITE)))
2813 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2818 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2819 getCookie("LEVELSETUP")));
2820 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2822 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2823 handicap_level_str));
2827 SetFilePermissions(filename, PERMS_PRIVATE);